问题描述
我有一个生成PDF报告的应用程序(使用JasperReports),但是,如果我在开发笔记本电脑上运行我的应用程序,则文本字段的大小会与在服务器上生成完全相同的报告时略有不同。我最终将问题简化为以下代码:
final Font font = Font.createFont(
Font.TRUETYPE_FONT,MyTest.class.getResourceAsstream("/fonts/europa/Europa-Bold.otf")
).deriveFont(10f);
System.out.println(font);
System.out.println(font.getStringBounds(
"Text",4,new FontRenderContext(null,true,true)
));
在我的笔记本电脑上打印:
java.awt.Font[family=Europa-Bold,name=Europa-Bold,style=plain,size=10]
java.awt.geom.Rectangle2D$Float[x=0.0,y=-9.90999,w=20.080002,h=12.669988]
在服务器上打印:
java.awt.Font[family=Europa-Bold,y=-7.6757812,w=20.06897,h=10.094452]
如您所见,我实际上在应用程序中附带了字体文件,所以我相信两台机器实际上都不可能使用其他字体。
我会猜想在这些条件下getStringBounds
的输出与系统无关。显然不是。可能导致差异的原因是什么?
解决方法
免责声明! :我不是字体开发专家,只是分享我的经验。
是的,它是本地语言。例如,甚至新的JavaFX Web视图都取决于webkit
。
如果您深入研究getStringBounds
的调试,您将意识到它已经到了一个点,字体管理器应该决定加载具体的字体管理器,而类名应该是系统属性sun.font.fontmanager
sun.font.FontManagerFactory
...
private static final String DEFAULT_CLASS;
static {
if (FontUtilities.isWindows) {
DEFAULT_CLASS = "sun.awt.Win32FontManager";
} else if (FontUtilities.isMacOSX) {
DEFAULT_CLASS = "sun.font.CFontManager";
} else {
DEFAULT_CLASS = "sun.awt.X11FontManager";
}
}
...
public static synchronized FontManager getInstance() {
...
String fmClassName = System.getProperty("sun.font.fontmanager",DEFAULT_CLASS);
}
这些DEFAULT_CLASS
值可以验证您的显然不是。可能造成差异的原因是什么?标记。
sun.font.fontmanager
的值在某些nix系统中可能是sun.awt.X11FontManager
,但对于Windows可能是null
,因此管理器将是sun.awt.Win32FontManager
。
现在,每位经理肯定都会依赖于不同的基础整形/渲染引擎/ Impl(this may help)。
主要原因可能是字体的性质。由于它们大多是矢量材料。因此,基于平台/环境,渲染的文本可能更大或更小。例如,也许Windows会在要求的文字渲染中应用桌面cleartype和屏幕文字大小(DPI)。
看来,即使您只有两个sun.awt.X11FontManager
经理,结果也会有所不同。 this may help too
如果只是尝试使用示例代码,则在在线编译器上,肯定会遇到各种各样的结果。
ideatone(https://ideone.com/AuQvMV)的结果无法发生,stderr
有一些有趣的信息
java.lang.UnsatisfiedLinkError: /opt/jdk/lib/libfontmanager.so: libfreetype.so.6: cannot open shared object file: No such file or directory
at java.base/java.lang.ClassLoader$NativeLibrary.load0(Native Method)
at java.base/java.lang.ClassLoader$NativeLibrary.load(ClassLoader.java:2430)
at java.base/java.lang.ClassLoader$NativeLibrary.loadLibrary(ClassLoader.java:2487)
at java.base/java.lang.ClassLoader.loadLibrary0(ClassLoader.java:2684)
at java.base/java.lang.ClassLoader.loadLibrary(ClassLoader.java:2638)
at java.base/java.lang.Runtime.loadLibrary0(Runtime.java:827)
at java.base/java.lang.System.loadLibrary(System.java:1902)
at java.desktop/sun.font.FontManagerNativeLibrary$1.run(FontManagerNativeLibrary.java:57)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:310)
at java.desktop/sun.font.FontManagerNativeLibrary.<clinit>(FontManagerNativeLibrary.java:32)
at java.desktop/sun.font.SunFontManager$1.run(SunFontManager.java:270)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:310)
at java.desktop/sun.font.SunFontManager.<clinit>(SunFontManager.java:266)
at java.base/java.lang.Class.forName0(Native Method)
at java.base/java.lang.Class.forName(Class.java:415)
at java.desktop/sun.font.FontManagerFactory$1.run(FontManagerFactory.java:82)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:310)
at java.desktop/sun.font.FontManagerFactory.getInstance(FontManagerFactory.java:74)
at java.desktop/java.awt.Font.getFont2D(Font.java:497)
at java.desktop/java.awt.Font.getFamily(Font.java:1410)
at java.desktop/java.awt.Font.getFamily_NoClientCode(Font.java:1384)
at java.desktop/java.awt.Font.getFamily(Font.java:1376)
at java.desktop/java.awt.Font.toString(Font.java:1869)
at java.base/java.lang.String.valueOf(String.java:3042)
at java.base/java.io.PrintStream.println(PrintStream.java:897)
at Ideone.main(Main.java:19)
请注意{/ {1}}是本地/ libfreetype
字体渲染应用程序的失败/丢失负载
编码基础(https://www.tutorialspoint.com/compile_java_online.php)的结果
C
jdoodle(https://www.jdoodle.com/online-java-compiler/)的结果
fnt manager: sun.awt.X11FontManager
java.awt.Font[family=Dialog,name=tahoma,style=plain,size=10]
java.awt.geom.Rectangle2D$Float[x=0.0,y=-9.282227,w=22.09961,h=11.640625]
我的机器
fnt manager: sun.awt.X11FontManager
java.awt.Font[family=Dialog,y=-9.839991,w=24.0,h=12.569988]
我的故事 (可能有帮助,您可以尝试)
几年前,我遇到过类似的问题,在fnt manager: null
java.awt.Font[family=Tahoma,y=-10.004883,w=19.399414,h=12.0703125]
上使用完全相同的嵌入字体进行文本渲染失败,原因是复杂的文本渲染(许多连字)。不仅是大小,而且还会打断连字,字距调整等等。
我可以使用另一种解决方法来解决我的问题(不记得是否修复了大小调整,但肯定没有断字)
macOs/jdk8
使用InputStream is = Main.class.getResourceAsStream(fontFile);
Font newFont = Font.createFont(Font.TRUETYPE_FONT,is);
GraphicsEnvironment.getLocalGraphicsEnvironment().registerFont(newFont);
//later load the font by constructing a Font ins
Font f = new Font(name/*name of the embedded font*/,style,size);
注册字体,然后使用GraphicsEnvironment
实例化字体解决了我们的问题。因此,您也可以尝试一下。
解决方案
最后,我只是永久性地降低了jdk的内容(脖子上的确很痛),并提出了Font
(塑形)+ harfbuzz
(渲染)本机暗示内心的平静。
所以...
•您可以考虑将生产服务器(简便方法)作为渲染字体前进和渲染的参考,并基于它验证结果(而不是开发机器)
•或者,使用交叉和独立的(可能是本机的)整形/渲染字体引擎/ Impl以确保开发和生产结果相同。