19

javadoc を見ると、次のように表示されFontMetric.getAscent()ます。

フォント アセントは、フォントのベースラインからほとんどの英数字の上端までの距離です。フォントの一部の文字は、フォント アセント ラインより上にある場合があります。

しかし、簡単なデモ プログラムを作成したところ、次のように表示されました。 ここに画像の説明を入力

テキストの各行の 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);
    }
}
4

3 に答える 3

14

考えられる理由の 1 つは、この値が分音記号付きの文字を考慮に入れていることです。

たとえば、ウムラウト ÄÖÜ を追加すると、トレマが上昇にかなり近づいていることを示します (ただし、まだ到達していません)。

アセントのより一般的な定義を探していると、ウィキペディアで定義が見つかります。

[..]アセントは、ベースラインと、ベースラインから最も遠くに到達するグリフの上部との間の距離に及びます。上昇と下降には、アクセントまたは分音記号によって追加される距離が含まれる場合と含まれない場合があります。

そのため、タイポグラフィーの中でも正確で絶対的な定義は存在しないようです。

于 2011-06-01T13:53:35.980 に答える
8

私は同じ問題に遭遇し、文字の真の上限は 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 値。

ピクセル境界とビジュアル境界の主な違いは、pixelBounds が整数で、visualBounds が float であることです。そうでなければ、それらはほぼ等しいように見えます。

于 2012-12-27T13:58:49.157 に答える
2

TrueType リファレンス マニュアルによると、フォントのアセントは「hhea」テーブルに格納されます。hhea のドキュメントには、「ascent、descent、および lineGap の値は、計算された値ではなく、フォントの作成者の設計意図を表しています」と記載されています。OpenType 仕様は、TrueType 仕様の拡張です。また、アセンダーを hhea テーブルに格納し、アセントの TrueType 定義を参照します。要するに、アセント プロパティはガイドであり、絶対的なものではありません。GlyphLayoutVector は、テキストの境界を取得する最も正確な方法です。

于 2013-01-17T20:15:45.100 に答える