如何确定2种字体是否具有相同的字形?

And*_*son 7 java fonts swing awt

这个Q&A的灵感来自于使用SwingUnicode char而不是渲染,真正使用什么字体?但由于它没有回答所提出的具体问题,我决定将其作为自己的问答进入.这里的问题是......

如何确定给定文本的两种字体在返回相同的字符字形时是否有效等效?

And*_*son 7

这里使用的技巧是比较GlyphVector返回String的感兴趣.这种方法的关键在于该方法fontsAreEquivalentForText(Font,Font,String).

这是一个示例输出Arial.

在此输入图像描述

GUI有三个基本组件.

  1. 要在GUI顶部的文本字段中测试的文本.在这里,我们检查字符串中的字母The quick brown fox jumps over the lazy dog.
  2. 所有字体的列表显示在左侧.
  3. 当从所有字体的列表中选择一个字体时,它将弹出一个可取消的(需要一段时间检查超过400种字体!)对话框,逐步构建右侧的列表.此列表是等效字体(应包括所选字体,禁止编程错误).

import java.awt.*;
import java.awt.event.*;
import java.awt.font.FontRenderContext;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.*;

public class FontEquivalence {

    public boolean fontsAreEquivalentForText(Font f1, Font f2, String text) {
        Area area1 = new Area(
                f1.deriveFont(25f).
                createGlyphVector(fontRenderContext, text).
                getOutline());
        Area area2 = new Area(
                f2.deriveFont(25f).
                createGlyphVector(fontRenderContext, text).
                getOutline());
        return area2.equals(area1);
    }

    public void findEquivalentFonts(final Font font) {
        if (dialog == null) {
            dialog = getDialog(ui);
        }
        fontChecker = new SwingWorker() {

            @Override
            protected Object doInBackground() throws Exception {
                dialog.setLocationRelativeTo(ui);
                sameFontListModel.clear();
                String s = inputString.getText();
                int fontNumber = fonts.length;
                progress.setMaximum(fontNumber);
                int ii = 1;
                for (Font f : fonts) {
                    if (fontsAreEquivalentForText(f, font, s)) {
                        sameFontListModel.addElement(f);
                    }
                    progress.setValue(ii++);
                    if (fontChecker.isCancelled()) {
                        break;
                    }
                }
                dialog.setVisible(false);
                return null;
            }
        };
        fontChecker.execute();
        dialog.setVisible(true);
    }

    public JDialog getDialog(JComponent comp) {
        Container cont = comp.getTopLevelAncestor();
        Frame f = null;
        if (cont instanceof Frame) {
            f = (Frame) cont;
        }
        final JDialog d = new JDialog(f, 
                "Searching " + fonts.length + " fonts for equivalents..", 
                true);
        JPanel p = new JPanel(new BorderLayout(15, 15));
        p.setBorder(new EmptyBorder(40, 100, 40, 100));
        p.add(progress, BorderLayout.CENTER);

        JButton cancel = new JButton("Cancel");
        ActionListener al = new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                fontChecker.cancel(true);

                d.setVisible(false);
            }
        };
        cancel.addActionListener(al);
        JPanel control = new JPanel(new FlowLayout(FlowLayout.CENTER));
        control.add(cancel);
        p.add(control, BorderLayout.PAGE_END);

        d.add(p);
        d.pack();

        return d;
    }

    public JComponent getUI() {
        if (ui == null) {
            ui = new JPanel(new BorderLayout(2, 2));
            inputString = new JTextField(text, 15);
            inputString.setFont(inputString.getFont().deriveFont(20f));
            ui.add(inputString, BorderLayout.PAGE_START);
            GraphicsEnvironment ge = GraphicsEnvironment.
                    getLocalGraphicsEnvironment();
            fonts = ge.getAllFonts();
            final JList fontList = new JList(fonts);
            ListSelectionListener lsl = new ListSelectionListener() {

                @Override
                public void valueChanged(ListSelectionEvent e) {
                    if (!e.getValueIsAdjusting()) {
                        Font font = (Font) fontList.getSelectedValue();
                        findEquivalentFonts(font);
                    }
                }
            };
            fontList.addListSelectionListener(lsl);
            fontList.setCellRenderer(new FontCellRenderer());
            fontList.setVisibleRowCount(15);
            ui.add(new JScrollPane(fontList), BorderLayout.LINE_START);

            JList list = new JList(sameFontListModel);
            list.setCellRenderer(new FontCellRenderer());
            ui.add(new JScrollPane(list));

            BufferedImage bi = new BufferedImage(
                    1, 1, BufferedImage.TYPE_INT_RGB);
            Graphics2D g = bi.createGraphics();
            fontRenderContext = g.getFontRenderContext();

            progress = new JProgressBar(0, fonts.length);
            progress.setStringPainted(true);
        }
        return ui;
    }

    JPanel ui = null;
    JTextField inputString;
    String text = "The quick brown fox jumps over the lazy dog.";
    Font[] fonts;
    DefaultListModel sameFontListModel = new DefaultListModel();
    FontRenderContext fontRenderContext;
    JDialog dialog;
    SwingWorker fontChecker;
    JProgressBar progress;

    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                JFrame f = new JFrame("Font Equivalence");
                f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                f.setContentPane(new FontEquivalence().getUI());
                f.pack();
                f.setLocationByPlatform(true);
                f.setVisible(true);
            }
        };
        SwingUtilities.invokeLater(r);
    }
}

class FontCellRenderer extends DefaultListCellRenderer {

    public Component getListCellRendererComponent(
            JList list,
            Object value,
            int index,
            boolean isSelected,
            boolean cellHasFocus) {
        JLabel label = (JLabel) super.getListCellRendererComponent(
                list, value, index, isSelected, cellHasFocus);
        Font font = (Font) value;
        label.setFont(font.deriveFont(20f));
        label.setText(font.getName());
        return label;
    }
}
Run Code Online (Sandbox Code Playgroud)

更新

StanislavL在评论中提醒我这种方法有些脆弱:

注意:使用Font.layoutGlyphVector()而不是createGlyphVector().当前的解决方案可能会为某些重新排序字形的字体生成错误的结果.来自Javadoc about createGlyphVector()

除了将字形映射到字符之外,此方法不执行任何其他处理.这意味着此方法对于需要重新排序,整形或连字替换的某些脚本(如阿拉伯语,希伯来语,泰语和印度语)无用.

我用阿拉伯语和希伯来语渲染了这样的东西.

我将按原样保留此代码,但有关详细信息,请参阅Font.layoutGlyphVector(FontRenderContext,char[],start,limit,flags):

返回一个新GlyphVector对象,如果可能,执行文本的完整布局.复杂文本需要完整布局,例如阿拉伯语或印地语.对不同脚本的支持取决于字体和实现...