2011-06-01 24 views
14

當我看的javadoc爲FontMetric.getAscent()我看到:Java:FontMetrics ascent不正確?

字體上升是從字體的基線到大多數字母數字字符頂部的距離。字體中的某些字符可能會延伸到字體上升線之上。

但我寫了一個快速的演示程序,我看到: enter image description here

,其中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" 
           +"[email protected]#$%^&*()[]{}"); 
         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); 
        } 
    } 
    
  • +0

    使用'TextLayout'可能會有更好的運氣。 – 2011-08-10 11:34:04

    +1

    @安德魯 - 謹慎闡述? – 2011-08-10 13:02:02

    回答

    10

    一個可能的原因是,這個值需要與diacritics賬戶字母。例如,添加變音符號ÄÖÜ表明他們的trema更接近上升(雖然他們還沒有達到它)。

    尋找上升的一個更一般的定義,我覺得the definition in Wikipedia

    [..]的上升跨越基線和字形的到達從基線最遠的頂部之間的距離。上升和下降可能包括也可能不包括由口音或變音標記添加的距離。

    因此,即使在印刷術中,似乎也沒有精確的絕對定義。

    +2

    我想是這樣......但爲什麼*它應該考慮到變音符號? 'getAscent()'說它應該是*最* *字母數字字符的頂部。如果你想要最大的上升,有'getMaxAscent()'。 – 2011-06-01 13:56:12

    +0

    @Jason:我大多在這裏猜測。但是由於變音符號很多,可以應用於幾乎所有的字母,人們可以爭辯說*大多數*字母數字字符實際上*確實有*變音符號。這只是一部分內容。 – 2011-06-01 13:57:38

    +0

    這不是最令人滿意的答案,但我會接受,因爲它有一定的意義。 – 2011-06-08 13:14:49

    5

    我遇到了同樣的問題,似乎可以通過使用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是浮點數。否則,他們看起來幾乎相等

    2

    TrueType Reference Manual表示字體的上升存儲在'hhea'表中。 hhea的文檔狀態,「上升,下降和lineGap的值代表字體創建者的設計意圖,而不是任何計算值。」 OpenType specification是TrueType規範的擴展。它還將上行存儲在hhea表中並引用上升的TrueType定義。底線,上升財產是一個指導,而不是絕對的。 GlyphLayoutVector是獲取文本邊界的最準確的方法。