Java:FontMetrics上升不正确?

当我查看FontMetric.getAscent()的javadoc时,我看到:

字体上升是从字体的基线到大多数字母数字字符顶部的距离。 Font中的某些字符可能会延伸到字体上升线之上。

但我写了一个快速的演示程序,我看到了这个: 在此处输入图像描述

其中每行文本的4条水平线是:

  • getDescent()降低基线位置
  • 基线位置
  • getAscent()引发的基线位置
  • getHeight()引发的基线位置

注意getAscent()行和字符顶部之间的空格。 我看过大多数字体和大小,总是存在这种差距。 (而字体下降看起来恰到好处。) 是什么给出的?

 package com.example.fonts; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.GraphicsEnvironment; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Arrays; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.JTextPane; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; public class FontMetricsExample extends JFrame { static final int marg = 10; public FontMetricsExample() { super(FontMetricsExample.class.getSimpleName()); JPanel panel = new JPanel(new BorderLayout()); JPanel fontPanel = new JPanel(new BorderLayout()); final JTextPane textSource = new JTextPane(); textSource.setText("ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" +"abcdefghijklmnopqrstuvwxyz\n" +"0123456789!@#$%^&*()[]{}"); final SpinnerNumberModel fontSizeModel = new SpinnerNumberModel(18, 4, 32, 1); final String fonts[] = GraphicsEnvironment.getLocalGraphicsEnvironment() .getAvailableFontFamilyNames(); final JComboBox fontFamilyBox = new JComboBox(fonts); fontFamilyBox.setSelectedItem("Arial"); final JPanel text = new JPanel() { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); String fontFamilyName = fonts[fontFamilyBox.getSelectedIndex()]; int fontSize = fontSizeModel.getNumber().intValue(); Font f = new Font(fontFamilyName, 0, fontSize); g.setFont(f); FontMetrics fm = g.getFontMetrics(); int lineHeight = fm.getHeight(); String[] s0 = textSource.getText().split("\n"); int x0 = marg; int y0 = getHeight()-marg-(marg+lineHeight)*s0.length; for (int i = 0; i < s0.length; ++i) { y0 += marg+lineHeight; String s = s0[i]; g.drawString(s, x0, y0); int w = fm.stringWidth(s); for (int yofs : Arrays.asList( 0, // baseline -fm.getHeight(), -fm.getAscent(), fm.getDescent())) { g.drawLine(x0,y0+yofs,x0+w,y0+yofs); } } } }; final JSpinner fontSizeSpinner = new JSpinner(fontSizeModel); fontSizeSpinner.getModel().addChangeListener( new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { text.repaint(); } }); text.setMinimumSize(new Dimension(200,100)); text.setPreferredSize(new Dimension(400,150)); ActionListener repainter = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { text.repaint(); } }; textSource.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { text.repaint(); } @Override public void insertUpdate(DocumentEvent e) {} @Override public void removeUpdate(DocumentEvent e) {} }); fontFamilyBox.addActionListener(repainter); fontPanel.add(fontFamilyBox, BorderLayout.CENTER); fontPanel.add(fontSizeSpinner, BorderLayout.EAST); fontPanel.add(textSource, BorderLayout.SOUTH); panel.add(fontPanel, BorderLayout.NORTH); panel.add(text, BorderLayout.CENTER); setContentPane(panel); pack(); setDefaultCloseOperation(EXIT_ON_CLOSE); } public static void main(String[] args) { new FontMetricsExample().setVisible(true); } } 

一个可能的原因是该值考虑了带有变音符号的字母。

例如,添加变形金刚ÄÖÜ表明他们的trema更接近上升(尽管他们仍然没有达到它)。

寻找上升的更一般定义我在维基百科中找到了定义 :

[..] 上升跨越基线和距离基线最远的字形顶部之间的距离。 上升和下降可能包括也可能不包括由重音或变音符号添加的距离。

因此,即使在排版中,似乎也没有精确的绝对定义。

我遇到了同样的问题,似乎可以通过使用GlyphVector类获得字符的真正上限。

 package graphics; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.font.GlyphVector; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; public class FontMetricsTest2 { public static void main(String[] args) throws IOException { //Draw the text to measure it with a drawing program BufferedImage img = new BufferedImage( 500, 300, BufferedImage.TYPE_INT_RGB); Graphics2D graphics = img.createGraphics(); Font font = new Font(Font.SERIF, Font.PLAIN, 150); graphics.setFont(font); String text = "ABCxyz"; graphics.drawString(text, 20, 180); ImageIO.write(img, "PNG", new File("G:\\someDir\\fontMetrics2.png")); //Failed attempts to determine ascent with FontMetrics FontMetrics fm = graphics.getFontMetrics(); System.out.println("FM Ascent=" + fm.getAscent() + ", FM descent=" + fm.getDescent()); //returned ascent is too high System.out.println("FM string bounds: " + fm.getStringBounds(text, graphics)); //too high as well //The succesful way with glyph vector GlyphVector gv = font.layoutGlyphVector( graphics.getFontRenderContext(), text.toCharArray(), 0, text.length(), Font.LAYOUT_LEFT_TO_RIGHT); Rectangle pixBounds = gv.getPixelBounds( graphics.getFontRenderContext(), 0, 0); System.out.println("GlyphVector - pixelBounds: " + pixBounds); Rectangle2D visBounds = gv.getVisualBounds(); System.out.println("GlyphVector - visualBounds: " + visBounds); } } 

通过字符串中出现的字符的上升返回的矩形中的y值表示“text”变量。

像素边界和视觉边界之间的主要区别在于pixelBounds是整数,而visualBounds是浮点数。 否则他们似乎差不多。

TrueType参考手册说,字体的上升存储在’hhea’表中。 hhea的文档指出,“上升,下降和lineGap的值代表字体创建者的设计意图,而不是任何计算值。” OpenType规范是TrueType规范的扩展。 它还将ascender存储在hhea表中,并引用了asType的TrueType定义。 最重要的是,上升属性是一个指南,而不是绝对的。 GlyphLayoutVector是获取文本边界的最准确方法。