给定字体pt大小和dpi屏幕分辨率的Java2d / AWT文本呈现奇怪的大小/边界?
Posted
技术标签:
【中文标题】给定字体pt大小和dpi屏幕分辨率的Java2d / AWT文本呈现奇怪的大小/边界?【英文标题】:Weird sizes / bounds on Java2d / AWT text rendering with given font pt size and dpi screen resolution? 【发布时间】:2016-03-24 04:48:29 【问题描述】:我刚刚开始使用 Java2D / AWT 在屏幕上直接渲染文本。基本上它可以工作,但我无法理解 AWT 报告并呈现在屏幕上的结果的像素大小/尺寸。
参数:
文本是“这是一个字形测试” 字体为 java.awt。Font[family=Dialog,name=sans,style=plain,size=72] (pt) 屏幕分辨率为 96 dpi 平台是 x86-64 Linux X11 和 Java 8已使用 Toolkit.getDefaultToolkit().getScreenResolution() 以编程方式确定屏幕分辨率,并对应于 xdpyinfo。
fontMetrics.get...()、graphics.drawString()和drawGlyphVector()的结果都与这些一致恕我直言,结果值有些奇怪:
边界为 java.awt.geom.Rectangle2D$Float[x=0.0,y=-71.55469,w=448.0,h=55.875] 字体上升:45 px 字体下降:12 px 字体高度:57 像素如果我将字体大小从 72 更改为 192 pt,结果是一致的:java.awt.geom.Rectangle2D$Float[x=0.0,y=-190.8125,w=1208.0,h=149.0],上升:119,下降:31,高度:150。如果我改变字体,只有宽度改变,而高度保持不变。 (这是我的预期。)
我假设 AWT 使用 DTP 指标,其中 1 pt总是 1/72 英寸。在 96 dpi 的屏幕上,96 像素将构成一英寸(物理上)。 (提供的屏幕 dpi 值不一定准确反映实际物理设备的值,但这并不重要。)因此,72 pt == 1 英寸应转换为 96。我希望找到接近 96 的值 在结果大小值(或它们的组合)中的某个位置,可能是高度值,但它们似乎都没有接近它...
问题:
任何人都可以像我一样复制类似的价值观还是非常不典型? 如何计算边界高度/字体高度? (我预计字体高度为 96 px 和 72 pt) 或者:如果不是类似DTP的点,Java中字体大小参数的解释(单位)是什么? 是否只是 AWT 中的舍入或其他不准确错误导致 bounds.h 与 metrics.height 和 metrics.ascent + metrics.descent 还是这种差异有价值的信息? 我是否遗漏了其他相关参数? (此处不涉及仿射变换。) 是否有一种安全的(跨平台)方法可以在 96 dpi 设备和 72 pt 字体大小上可靠地使字体高度为 96 px? (将字体大小乘以一个常数值是否可以接受?)【问题讨论】:
发布MCVE。 另外,不要在你的问题中使用太多的粗体,它会让人难以阅读。在它们之间放置技术术语:` 这是很多问题,所以我假设您已经看过 this *** help article,它首先告诉您首先自己搜索答案,使用网络搜索等。您有没有找到任何解释 Java2D 如何处理字体度量的页面?似乎不太可能没有,特别是因为它已经存在了很长时间,而且 javadocs 可以非常详尽。 【参考方案1】:问题已解决:这是我的错!
更准确地说:我实际上不是通过简单的new Font (Font.DIALOG, Font.PLAIN, 72))
或类似方法初始化 Font 对象,而是使用new Font(Map<? extends AttributedCharacterIterator.Attribute,?> attributes)
构造函数。那个有一张带有各种字体属性的地图,从我的配置中预设。因为一个变量预设了一个错误的值,它实际上包含了一个map.put (SUPERSCRIPT, SUPERSCRIPT_SUPER);
(在我的实际代码中并不像这里显示的那样清晰)。
因此,这些奇怪的边界实际上是字体一直被创建为较小的 superscript 变体的结果。没有它,边界/创建的文本大小会更接近我的预期:
Dialog,style=plain,size=72],高度:84,上升:67,下降:17,边界:java.awt.geom.Rectangle2D$Float[x=0.0,y=-66.83203,w=681.0 ,h=83.8125]
虽然它们的尺寸并不完全准确,但它们更接近。感谢 user1803551 的 MCVE 建议让我走上了正轨。为了完整起见,这里是(省略导入):
public final class TextRenderTest extends WindowAdapter
int fontSource;
static private final String TESTTEXT = "This is a glyph test";
static public void main (String [] args) new TextRenderTest ().main ();
void main ()
Frame frame = new Frame ("Text render test")
@Override public void paint (Graphics g)
FontMetrics fm;
Font font;
g.setFont (fontSource++ % 2 == 0 ? createFont () : new Font (Font.DIALOG, Font.PLAIN, 72));
g.drawString (TESTTEXT, 16, 96);
fm = g.getFontMetrics ();
System.out.println (
"DPI: " + Toolkit.getDefaultToolkit ().getScreenResolution () +
", font: " + g.getFont () + ", font height: " + fm.getHeight () +
", ascent: " + fm.getAscent () + ", descent: " + fm.getDescent () +
", bounds: " + fm.getStringBounds (TESTTEXT, g)); ;
frame.setBounds (64, 32, 800, 280);
frame.setVisible (true);
frame.addWindowListener (this);
static private Font createFont ()
HashMap <TextAttribute,Object> map = new HashMap<TextAttribute,Object> (9, 1.0f);
map.put (FAMILY, "sans");
map.put (WEIGHT, 500);
map.put (WIDTH, 1);
map.put (SIZE, 72);
map.put (SUPERSCRIPT, SUPERSCRIPT_SUPER); // <-- Something like this was the fault
return new Font (map);
@Override public void windowClosing (WindowEvent event) event.getWindow ().dispose ();
此示例以触发器方式演示了这两种变体:预期边界和意外边界(使用上标)。重绘过程很脏,只看控制台输出。
所以我可以回答我所有的问题:
-
不,其他人不太可能重现这些奇怪的结果 ;-)
我的预期基本上是正确的,尽管在细节上仍有细微的差异。
字体大小的解释(单位)似乎基本上是Point,确实。
这些微小的变化是我唯一无法解释的,尽管它们对我来说可以忽略不计。
错过了 SUPERSCRIPT 参数
可能不再需要进一步的补偿
【讨论】:
以上是关于给定字体pt大小和dpi屏幕分辨率的Java2d / AWT文本呈现奇怪的大小/边界?的主要内容,如果未能解决你的问题,请参考以下文章