为什么 java.awt.Font.getStringBounds 在不同的机器上给出不同的结果?
Why does java.awt.Font.getStringBounds give different result on different machines?
我有一个生成 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",
0,
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,name=Europa-Bold,style=plain,size=10]
java.awt.geom.Rectangle2D$Float[x=0.0,y=-7.6757812,w=20.06897,h=10.094452]
如您所见,我实际上随应用程序一起提供了字体文件,所以我相信两台机器实际上都不可能使用不同的字体。
我猜想在这些条件下 getStringBounds
的输出是系统无关的。显然不是。什么可能导致差异?
免责声明! : 我不是字体开发专家,只是分享我的经验。
是的,有点像本地人。例如,即使是新的 JavaFX web 视图也取决于 webkit
.
如果你深入调试 getStringBounds
,你会意识到它达到了一个点,字体管理器应该决定加载一个具体的字体管理器,其中 class 名称应该是系统属性 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
值可以验证您的 显然不是。什么可能导致差异?标记。
对于某些 nix 系统,sun.font.fontmanager
的 val 可能是 sun.awt.X11FontManager
,但对于 windows 可能是 null
,因此管理器将是 sun.awt.Win32FontManager
.
现在每个经理,可能取决于不同的基础 shaping/rendering engine/impl 肯定(this may help)。
主要原因可能是字体的性质。因为它们大多是矢量的东西。所以基于 platform/env,呈现的文本可能更大,也可能更小。例如,也许 windows 在请求的文本呈现上应用桌面 cleartype 和屏幕文本大小 (DPI)。
看来,即使你正好有两个sun.awt.X11FontManager
经理,结果也会有所不同。 this may help too
如果您只是尝试示例代码,在在线编译器上,您肯定会面临不同的结果。
ideaone (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.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.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.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)
注意 libfreetype
的 failed/missed 负载,它是原生/C
字体渲染应用程序
编码结果(https://www.tutorialspoint.com/compile_java_online.php)
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]
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.839991,w=24.0,h=12.569988]
我的机器
fnt manager: null
java.awt.Font[family=Tahoma,name=tahoma,style=plain,size=10]
java.awt.geom.Rectangle2D$Float[x=0.0,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
注册字体,然后使用 Font
实例化它解决了我们的问题。所以你也可以试试看。
解决方案
最后,我只是 step-down jdk 东西(脖子真的很痛),想出了 harfbuzz
(塑形)+ freetype
(rendering) native impl 这确实是一种内心的平静。
所以...
• 您可以考虑将您的生产服务器(简单方法)作为渲染字体推进和渲染的参考,并基于它(而不是开发机器)验证结果
• 或者,使用交叉和独立(可能是本机)shaping/rendering 字体engine/impl 以确保开发和生产结果相同。
我有一个生成 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",
0,
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,name=Europa-Bold,style=plain,size=10]
java.awt.geom.Rectangle2D$Float[x=0.0,y=-7.6757812,w=20.06897,h=10.094452]
如您所见,我实际上随应用程序一起提供了字体文件,所以我相信两台机器实际上都不可能使用不同的字体。
我猜想在这些条件下 getStringBounds
的输出是系统无关的。显然不是。什么可能导致差异?
免责声明! : 我不是字体开发专家,只是分享我的经验。
是的,有点像本地人。例如,即使是新的 JavaFX web 视图也取决于 webkit
.
如果你深入调试 getStringBounds
,你会意识到它达到了一个点,字体管理器应该决定加载一个具体的字体管理器,其中 class 名称应该是系统属性 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
值可以验证您的 显然不是。什么可能导致差异?标记。
对于某些 nix 系统,sun.font.fontmanager
的 val 可能是 sun.awt.X11FontManager
,但对于 windows 可能是 null
,因此管理器将是 sun.awt.Win32FontManager
.
现在每个经理,可能取决于不同的基础 shaping/rendering engine/impl 肯定(this may help)。
主要原因可能是字体的性质。因为它们大多是矢量的东西。所以基于 platform/env,呈现的文本可能更大,也可能更小。例如,也许 windows 在请求的文本呈现上应用桌面 cleartype 和屏幕文本大小 (DPI)。
看来,即使你正好有两个sun.awt.X11FontManager
经理,结果也会有所不同。 this may help too
如果您只是尝试示例代码,在在线编译器上,您肯定会面临不同的结果。
ideaone (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.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.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.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)
注意 libfreetype
的 failed/missed 负载,它是原生/C
字体渲染应用程序
编码结果(https://www.tutorialspoint.com/compile_java_online.php)
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]
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.839991,w=24.0,h=12.569988]
我的机器
fnt manager: null
java.awt.Font[family=Tahoma,name=tahoma,style=plain,size=10]
java.awt.geom.Rectangle2D$Float[x=0.0,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
注册字体,然后使用 Font
实例化它解决了我们的问题。所以你也可以试试看。
解决方案
最后,我只是 step-down jdk 东西(脖子真的很痛),想出了 harfbuzz
(塑形)+ freetype
(rendering) native impl 这确实是一种内心的平静。
所以...
• 您可以考虑将您的生产服务器(简单方法)作为渲染字体推进和渲染的参考,并基于它(而不是开发机器)验证结果
• 或者,使用交叉和独立(可能是本机)shaping/rendering 字体engine/impl 以确保开发和生产结果相同。