Как указать резервные шрифты в Java2D/Graphics2D

Я использую g.drawString(str, x, y) для рисования строки с объектом Graphics2D g. Текущий шрифт g не покрывает все символы str (у меня там есть, например, китайские иероглифы). В Mac OS X автоматически используется резервный шрифт, но не в Windows, где вместо нужных символов появляются черные квадратные контуры.

  • Почему поведение отличается в зависимости от платформы?
  • Как указать резервный шрифт (или несколько резервных шрифтов) на случай отсутствия символов?

(Например, один из хороших шрифтов здесь.)

Обновление/Дополнительная информация

Таким образом, исходный шрифт, который не поддерживает все символы, не является одним из логические шрифты, но это шрифт в комплекте, который поставляется с моим приложением и был получен с помощью Font.createFont(). Таким образом, добавление шрифтов в папку JRE lib/fonts/fallback здесь не работает.


person Jean-Philippe Pellet    schedule 28.02.2012    source источник


Ответы (1)


Мы могли бы атрибутировать строку для переключения шрифта на «плохие» символы и использовать Graphics2D.drawString(AttributedCharacterIterator iterator, int x, int y) для отображения результата. Преимущество: это будет работать с любым шрифтом. Недостаток: без кэширования промежуточных объектов это будет работать медленнее и грязнее.

Итак, я предлагаю использовать 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» в качестве запасного):

введите здесь описание изображения

Полный код предполагаемого 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;
    }
}
person Mersenne    schedule 28.02.2012
comment
К сожалению, это также не работает с модификациями fontconfig.properties. Обычный способ — добавить файл пользовательского шрифта в резервную последовательность. Необходимые строки: filename.Yourfont=YOURFONT.TTF и allfonts.yourfont=Yourfont; затем добавьте yourfont в список sequence.fallback. Я планировал расширить ответ подробностями, но, к сожалению, это работает только для стандартных логических шрифтов. Возможно, потребуется еще немного fontconfig-magic, но это не соответствует моим навыкам и опыту. Прошу прощения за бесполезный ответ. - person Mersenne; 28.02.2012
comment
Я удалил неправильное решение. По крайней мере, обходной путь делает свое дело. - person Mersenne; 29.02.2012