从类路径注册字体会抛出 java.nio.BufferUnderflowException



基本上,我要做的是将所有字体(.ttf(注册到类路径的目录中。与从 IDE 启动应用程序时相比,在作为 jar 文件运行时执行此操作要复杂得多。当我从 IDE 启动应用程序时,不会引发任何异常,我可以立即使用这些字体。但是,当我通过 jar 文件启动应用程序时,它仍然注册字体,但它会引发以下异常:

java.awt.FontFormatException: java.nio.BufferUnderflowException
at sun.font.TrueTypeFont.init(Unknown Source)
at sun.font.TrueTypeFont.<init>(Unknown Source)
at sun.font.TrueTypeFont.<init>(Unknown Source)
at sun.font.SunFontManager.createFont2D(Unknown Source)
at java.awt.Font.<init>(Unknown Source)
at java.awt.Font.createFont0(Unknown Source)
at java.awt.Font.createFont(Unknown Source)
at com.cyr1en.cgdl.util.FontUtil.lambda$registerAllFonts$1(FontUtil.java:65)
at java.util.ArrayList.forEach(Unknown Source)
at com.cyr1en.cgdl.util.FontUtil.registerAllFonts(FontUtil.java:63)
at com.cyr1en.test.Launcher.main(Launcher.java:20)

但正如我所说,字体仍然注册,我仍然可以使用它。

这是我的注册功能:

public static void registerAllFonts(String fontsDir) {
try {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
final File jarFile = new File(FontUtil.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
if (FileUtil.isJarFile(jarFile)) {
JarFile jar = new JarFile(jarFile);
Enumeration<JarEntry> entries = jar.entries();
ArrayList<JarEntry> jarEntries = Collections.list(entries);
List<JarEntry> fonts = jarEntries.stream().filter(f -> f.getName().startsWith(fontsDir)).collect(Collectors.toList());
fonts.forEach(f -> {
try {
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, jar.getInputStream(f)));
} catch (FontFormatException | IOException e) {
ExceptionUtil.generateErrorLog(e);
}
});
jar.close();
} else {
URL url = FontUtil.class.getResource(fontsDir);
if (url != null) {
File file = new File(url.toURI());
if (file.listFiles() != null)
for (File f : file.listFiles()) {
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, f));
}
}
}
} catch (IOException | FontFormatException | URISyntaxException e) {
ExceptionUtil.generateErrorLog(e);
}
}

我在 filter(( 中的谓词包括实际的字体目录本身。所以在列表中,我得到[fonts/fonts/font1.ttffonts/font2.ttf],当然你不能从fonts/创建字体。

因此,解决方案是将谓词编辑为:

.filter(f -> f.getName().startsWith(finalFontsDir) && !f.getName().equals(finalFontsDir))

感谢@Robin格林

编辑:

自从传递"fonts/"以来,还更改了功能,因为该目录实际上不适用于class.getResource(fontsDir),并且仅适用于JarEntry#getName()。因此,只需通过"/fonts/",当用jar启动时,只需删除第一个/即可。

public static void registerAllFonts(String fontsDir) {
try {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
final File jarFile = new File(FontUtil.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
if (FileUtil.isJarFile(jarFile)) {
if(fontsDir.startsWith("/"))
fontsDir = fontsDir.substring(1);
JarFile jar = new JarFile(jarFile);
Enumeration<JarEntry> entries = jar.entries();
ArrayList<JarEntry> jarEntries = Collections.list(entries);
final String finalFontsDir = fontsDir;
List<JarEntry> fonts = jarEntries.stream()
.filter(f -> f.getName().startsWith(finalFontsDir) && 
!f.getName().equals(finalFontsDir))
.collect(Collectors.toList());
fonts.forEach(f -> {
try {
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, jar.getInputStream(f)));
} catch (FontFormatException | IOException e) {
e.printStackTrace();
}
});
jar.close();
} else {
URL url = FontUtil.class.getResource(fontsDir);
if (url != null) {
File file = new File(url.toURI());
if (file.listFiles() != null)
for (File f : file.listFiles()) {
ge.registerFont(Font.createFont(Font.TRUETYPE_FONT, f));
}
}
}
} catch (IOException | FontFormatException | URISyntaxException e) {
ExceptionUtil.generateErrorLog(e);
}
}

最新更新