如何在Java2D / Graphics2D中指定回退字体

时间:2012-02-28 12:52:24

标签: java fonts java-2d graphics2d fallback

我正在使用g.drawString(str, x, y)绘制一个带有Graphics2D对象g的字符串。当前字体g并未涵盖str的所有字符(我有例如中文字符)。在Mac OS X上,似乎会自动使用后备字体,但不会在Windows上显示黑色方形轮廓,而不是所需的字符。

  • 为什么行为会因平台而异?
  • 如果字符丢失,如何指定后备字体(或多种后备字体)?

(例如,一种漂亮的字体there。)

更新/更多信息

因此,不支持所有字符的原始字体不是JVM的logical fonts之一,而是我的应用程序附带的捆绑字体,是使用Font.createFont()获得的。因此,将字体添加到JRE的lib/fonts/fallback文件夹不起作用。

1 个答案:

答案 0 :(得分:6)

我们可以将字符串属性设置为在“坏”符号上切换字体,并使用Graphics2D.drawString(AttributedCharacterIterator iterator, int x, int y)来呈现结果。 Advantage :这适用于任何字体。 缺点:如果没有某种中间对象的缓存,这将会更慢,更脏。

所以,我建议在整个字符串上使用AttributedString主字体属性:

AttributedString astr = new AttributedString(text);
astr.addAttribute(TextAttribute.FONT, mainFont, 0, textLength);

并在特定部分使用后备字体:

astr.addAttribute(TextAttribute.FONT, fallbackFont, fallbackBegin, fallbackEnd);

渲染本身:

g2d.drawString(astr.getIterator(), 20, 30);

结果(物理“Segoe Print”作为主要字体,逻辑“Serif”作为后备):

enter image description here

完成所谓的SSCCE代码:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.font.TextAttribute;
import java.text.AttributedString;

import javax.swing.JComponent;
import javax.swing.JFrame;

public class FontTest extends JFrame {

    public FontTest() {
        setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        getContentPane().setLayout(new BorderLayout());
        getContentPane().add(new TestStringComponent());
        pack();
    }

    public static void main(String[] args) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new FontTest().setVisible(true);
            }
        });
    }
}

class TestStringComponent extends JComponent {

    protected void paintComponent(Graphics g) {
        Graphics2D g2d = (Graphics2D) g;
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());

        g.setColor(getForeground());

        Font mainFont = new Font("Segoe Print", Font.PLAIN, 25);
        Font fallbackFont = new Font("Serif", Font.PLAIN, 25);

        String s = "Test 漢鼎繁古印 Test 漢鼎繁古印 Test";

        g2d.drawString(createFallbackString(s, mainFont, fallbackFont).getIterator(), 20, 30);
    }

    public Dimension getPreferredSize() {
        return new Dimension(500, 40);
    }

    private AttributedString createFallbackString(String text, Font mainFont, Font fallbackFont) {
        AttributedString result = new AttributedString(text);

        int textLength = text.length(); 
        result.addAttribute(TextAttribute.FONT, mainFont, 0, textLength);

        boolean fallback = false;
        int fallbackBegin = 0;
        for (int i = 0; i < text.length(); i++) {
            boolean curFallback = !mainFont.canDisplay(text.charAt(i));
            if (curFallback != fallback) {
                fallback = curFallback;
                if (fallback) {
                    fallbackBegin = i;
                } else {
                    result.addAttribute(TextAttribute.FONT, fallbackFont, fallbackBegin, i);
                }
            }
        }
        return result;
    }
}