这个问答文章的灵感来自于Unicode字符在Swing中未渲染,实际使用的字体是什么?但由于它没有回答特定问题,我决定将其作为单独的问答。这里的问题是...
如何确定对于给定的文本,两种字体是否有效地等效于返回相同的字符字形?
这个问答文章的灵感来自于Unicode字符在Swing中未渲染,实际使用的字体是什么?但由于它没有回答特定问题,我决定将其作为单独的问答。这里的问题是...
如何确定对于给定的文本,两种字体是否有效地等效于返回相同的字符字形?
GlyphVector
。这种方法的关键在于fontsAreEquivalentForText(Font, Font, String)
方法中可以看到。Arial
的示例输出。
GUI有三个基本组件。
The quick brown fox jumps over the lazy dog.
中的字母。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 = (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 = (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 = () -> {
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 {
@Override
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;
}
}
更新
StanislavL在评论中指出了这种方法的一些不稳定性:
注意:使用
Font.layoutGlyphVector()
而不是createGlyphVector()
。当前解决方案可能会为某些重新排序字形的字体生成错误的结果。从关于createGlyphVector()
的 Javadoc 中可以看到:
除了将字形映射到字符之外,此方法不执行任何其他处理。这意味着该方法对某些需要重新排序、成形或连字替换的脚本(如阿拉伯语、希伯来语、泰语和印度语)无用。
我曾经看到过阿拉伯语和希伯来语的渲染问题。
我将保留此代码,但有关更多详细信息,请参见 Font.layoutGlyphVector(FontRenderContext,char[],start,limit,flags)
,它:
返回一个新的
GlyphVector
对象,如果可能的话,执行完整的文本布局。对于复杂的文本,如阿拉伯语或印地语,需要完整的布局。支持不同脚本取决于字体和实现。