是否有与 FontUtilities.getCompositeFontUIResource(Font) 等效的非专有等效项?
Posted
技术标签:
【中文标题】是否有与 FontUtilities.getCompositeFontUIResource(Font) 等效的非专有等效项?【英文标题】:Is there a non-proprietary equivalent to FontUtilities.getCompositeFontUIResource(Font)? 【发布时间】:2016-05-11 23:24:15 【问题描述】:如果您只是使用new Font("Arial", Font.PLAIN, 10)
创建字体,那么稍后,当您尝试在该字体中显示缺失的字形时,您会看到熟悉的方块,指示缺失的字形。
很久以前,我们找到了解决此问题的方法 - 将字体传递给 FontUtilities.getCompositeFontUIResource(Font)
,然后您会得到一个 Font
,它处理不在字体中的字符的后备。
问题是,该实用程序位于sun.font
,我想消除编译器警告。
鉴于这段时间已经过去了很多年,现在有合适的方法来做到这一点吗?
演示:
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import sun.font.FontUtilities;
public class TestFonts implements Runnable
@Override
public void run()
Font font = new Font("Arial", Font.PLAIN, 20);
JLabel label1 = new JLabel("Before \u30C6\u30B9\u30C8");
label1.setFont(font);
JLabel label2 = new JLabel("After \u30C6\u30B9\u30C8");
label2.setFont(FontUtilities.getCompositeFontUIResource(font));
JFrame frame = new JFrame("Font Test");
frame.setLayout(new GridLayout(2, 1));
frame.add(label1);
frame.add(label2);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
public static void main(String[] args)
SwingUtilities.invokeLater(new TestFonts());
结果:
【问题讨论】:
How do I specify fallback fonts in Java2D/Graphics2D的可能重复 也许吧,虽然那里的答案对我来说并不能解决问题,因为我并不热衷于重写 Swing 中碰巧呈现文本的每个类。 【参考方案1】:在 JDK 源码中挖掘了一下,我发现了一个直接调用内部方法的公共方法。我不知道这被认为是多么狡猾,但它至少是公共 API。 :)
label2.setFont(StyleContext.getDefaultStyleContext()
.getFont(familyName, style, size));
【讨论】:
【参考方案2】:您可以利用 JComponents 能够显示 html 的事实,并覆盖 JComponent 字体无法显示的字符的字体,方法是将这些字符放在 <span>
: 中:
import java.util.Formatter;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
import sun.font.FontUtilities;
public class TestFonts implements Runnable
/**
* Replaces plain text meant to be displayed in a JComponent with
* HTML that forces the font to Dialog for any characters which the
* specified font cannot natively display.
*
* @param originalText text to transform to HTML, with forced fonts
* where needed
* @param font default font which will be used to display text
*
* @return HTML version of original text, which forces fonts where
* necessary to ensure all characters will be displayed
*/
static String toCompositeFontText(String originalText,
Font font)
Formatter html = new Formatter();
html.format("%s", "<html><body style='white-space: nowrap'>");
boolean fontOverride = false;
int len = originalText.length();
for (int i = 0; i < len; i = originalText.offsetByCodePoints(i, 1))
int c = originalText.codePointAt(i);
if (font.canDisplay(c))
if (fontOverride)
html.format("%s", "</span>");
fontOverride = false;
else
if (!fontOverride)
html.format("<span style='font-family: \"%s\"'>",
Font.DIALOG);
fontOverride = true;
if (c == '<' || c == '>' || c == '&' || c < 32 || c >= 127)
html.format("&#%d;", c);
else
html.format("%c", c);
if (fontOverride)
html.format("%s", "</span>");
html.format("%s", "</body></html>");
return html.toString();
/**
* Replaces text of the specified JLabel with HTML that contains the
* same text, but forcing the font to Dialog for any characters which
* the JLabel's current font cannot display.
*
* @param label JLabel whose text will be adjusted and replaced
*/
static void adjustText(JLabel label)
label.setText(toCompositeFontText(label.getText(), label.getFont()));
@Override
public void run()
Font font = new Font("Arial", Font.PLAIN, 20);
JLabel label1 = new JLabel("Before \u30C6\u30B9\u30C8");
label1.setFont(font);
JLabel label2 = new JLabel("After \u30C6\u30B9\u30C8");
label2.setFont(FontUtilities.getCompositeFontUIResource(font));
JLabel label3 = new JLabel("Corrected \u30C6\u30B9\u30C8");
label3.setFont(font);
adjustText(label3);
JFrame frame = new JFrame("Font Test");
frame.setLayout(new GridLayout(3, 1));
frame.add(label1);
frame.add(label2);
frame.add(label3);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
public static void main(String[] args)
SwingUtilities.invokeLater(new TestFonts());
更新:
此外,您可以监控标签的text
属性,以自动实现:
label.addPropertyChangeListener("text", new PropertyChangeListener()
@Override
public void propertyChange(PropertyChangeEvent event)
String text = (String) event.getNewValue();
if (text != null && !text.startsWith("<html>"))
adjustText((JLabel) event.getSource());
);
明显的缺点是没有(简单的)方法来调整已经以<html>
开头的文本。 (实际上我很确定,即使这可以通过将标签的文本加载为HTMLDocument 来完成。)
【讨论】:
我真的不想在任何可能包含一些可变文本的组件上执行此操作...:/ 我同意这很尴尬。您可以添加一个 PropertyChangeListener 以使其成为一个自动过程。答案已相应更新。以上是关于是否有与 FontUtilities.getCompositeFontUIResource(Font) 等效的非专有等效项?的主要内容,如果未能解决你的问题,请参考以下文章
是否有与 RecyclerView 等效的 addHeaderView?