给出了首选字体列表,Main.main(...)

//simplified code
List<java.awt.Font> preferredFontList = List.of(
  "Roboto-Regular.ttf", "FreeSerif.ttf", "Quivira-A8VL.ttf", "Code2000-rdLO.ttf"
).stream().map(fontName->createFont(fontName)).toList();

我如何才能根据首选字体列表的顺序和它们对显示(Font.canDisplay(...))代码点的支持来分割字符串?TS_FileTmcrFileHandler.addText(...)

//simplified code
var text = "Tuğalsan Karabacak ♠☀☁☃☎☛ ŞşİiIıÜüÖöÇ窺Ğğ";
fragment(text, preferredFontList).forEach((subText, decidedFontIdx)->{
  addText(subText, preferredFontList.get(decidedFontIdx));
});

推荐答案

你用的"碎片"这个词把我搞糊涂了.这不是错误的,但它是模糊的.我将假设您想要将字符串分解(片段)成子字符串,每个子字符串都可以由您喜欢的一种字体完全呈现.

"文本串"是具有共同属性的字符的子序列.在本例中,公共属性是将用于呈现字符字体.Java有一个可以存储文本的类,其中包含基于属性的运行:AttributedString.

因此,我们可以创建一个循环,try 在每个字体上调用Font.canDisplayUpTo,直到该方法返回一个有效的索引,表明该字体至少可以显示一个字符. 我们可以使用返回的索引不仅将字体应用于AttributedString的部分,而且还可以通过字符串前进并使用相同的字体循环判断文本的下一部分,重复该过程,直到我们到达字符串的结尾:

private AttributedString applyFontsTo(String text) {
    AttributedString attrText = new AttributedString(text);

    int len = text.length();
    int textRunStart = 0;
    CharacterIterator i = new StringCharacterIterator(text);
    while (textRunStart >= 0) {
        Font matchingFont = null;
        String runText = null;

        for (Font font : preferredFontList) {
            int textRunEnd = font.canDisplayUpTo(i, textRunStart, len);
            if (textRunEnd != textRunStart) {
                matchingFont = font.deriveFont(24f);
                attrText.addAttribute(TextAttribute.FONT, matchingFont,
                    textRunStart, textRunEnd >= 0 ? textRunEnd : len);

                textRunStart = textRunEnd;
                break;
            }
        }

        if (matchingFont == null) {
            int index = i.getIndex();
            throw new IllegalArgumentException(String.format(
                "Character at index %d (U+%04X) "
                + "cannot be displayed by any of %s",
                index, text.codePointAt(index), preferredFontList));
        }
    }

    return attrText;
}

现在,我们可以通过两种不同的方式使用AttributedStringiterator:

  • 我们可以通过将其传递给Graphics.drawString来在绘制方法中呈现它.
  • 我们可以使用迭代器的getRunLimit方法和继承的setIndex方法遍历迭代器的运行,以其他方式处理文本和字体.

以下是第一种方法的一个示例:

public void show(String text) {
    AttributedString a = applyFontsTo(text);

    JPanel panel = new JPanel() {
        private static final long serialVersionUID = 1;

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(text.length() * 20, 50);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            ((Graphics2D) g).setRenderingHint(
                RenderingHints.KEY_TEXT_ANTIALIASING, 
                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            g.drawString(a.getIterator(), 6, getHeight() - 12);
        }
    };

    JFrame frame = new JFrame("Multi-Font Renderer");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(panel);
    frame.pack();
    frame.setLocationByPlatform(true);
    frame.setVisible(true);
}

下面是第二种方法的一个例子:

public void showAsList(String text) {
    Box box = Box.createVerticalBox();

    AttributedString a = applyFontsTo(text);
    AttributedCharacterIterator i = a.getIterator();
    while (i.getIndex() < i.getEndIndex()) {
        int runLimit = i.getRunLimit();
        String runText = text.substring(i.getIndex(), runLimit);
        Font font = (Font) i.getAttribute(TextAttribute.FONT);

        JLabel label = new JLabel(runText);
        label.setFont(font);
        box.add(label);

        i.setIndex(runLimit);
    }

    JFrame frame = new JFrame("Multi-Font Renderer");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(new JScrollPane(box));
    frame.pack();
    frame.setLocationByPlatform(true);
    frame.setVisible(true);
}

下面是一个完整的程序,它演示了这两种方法:

import java.io.InputStream;
import java.io.IOException;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;

import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontFormatException;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.TextAttribute;

import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JLabel;
import javax.swing.JScrollPane;

public class MultiFontRenderer {
    private final List<Font> preferredFontList;

    public MultiFontRenderer() {
        preferredFontList = List.of(
            "Roboto-Regular.ttf",
            "FreeSerif.ttf",
            "Quivira.otf",
            "Code2000-rdLO.ttf"
        ).stream().map(fontName -> createFont(fontName)).collect(
            Collectors.toUnmodifiableList());
    }

    private static Font createFont(String name) {
        try (InputStream fontResource =
            MultiFontRenderer.class.getResourceAsStream(name)) {

            return Font.createFont(Font.TRUETYPE_FONT, fontResource);
        } catch (IOException | FontFormatException e) {
            throw new RuntimeException("Cannot load font \"" + name + "\"", e);
        }
    }

    private AttributedString applyFontsTo(String text) {
        AttributedString attrText = new AttributedString(text);

        int len = text.length();
        int textRunStart = 0;
        CharacterIterator i = new StringCharacterIterator(text);
        while (textRunStart >= 0) {
            Font matchingFont = null;
            String runText = null;

            for (Font font : preferredFontList) {
                int textRunEnd = font.canDisplayUpTo(i, textRunStart, len);
                if (textRunEnd != textRunStart) {
                    matchingFont = font.deriveFont(24f);
                    attrText.addAttribute(TextAttribute.FONT, matchingFont,
                        textRunStart, textRunEnd >= 0 ? textRunEnd : len);

                    textRunStart = textRunEnd;
                    break;
                }
            }

            if (matchingFont == null) {
                int index = i.getIndex();
                throw new IllegalArgumentException(String.format(
                    "Character at index %d (U+%04X) "
                    + "cannot be displayed by any of %s",
                    index, text.codePointAt(index), preferredFontList));
            }
        }

        return attrText;
    }

    public void show(String text) {
        AttributedString a = applyFontsTo(text);

        JPanel panel = new JPanel() {
            private static final long serialVersionUID = 1;

            @Override
            public Dimension getPreferredSize() {
                return new Dimension(text.length() * 20, 50);
            }

            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
                ((Graphics2D) g).setRenderingHint(
                    RenderingHints.KEY_TEXT_ANTIALIASING, 
                    RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
                g.drawString(a.getIterator(), 6, getHeight() - 12);
            }
        };

        JFrame frame = new JFrame("Multi-Font Renderer");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(panel);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public void showAsList(String text) {
        Box box = Box.createVerticalBox();

        AttributedString a = applyFontsTo(text);
        AttributedCharacterIterator i = a.getIterator();
        while (i.getIndex() < i.getEndIndex()) {
            int runLimit = i.getRunLimit();
            String runText = text.substring(i.getIndex(), runLimit);
            Font font = (Font) i.getAttribute(TextAttribute.FONT);

            JLabel label = new JLabel(runText);
            label.setFont(font);
            box.add(label);

            i.setIndex(runLimit);
        }

        JFrame frame = new JFrame("Multi-Font Renderer");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(new JScrollPane(box));
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        boolean showList = args.length > 0;

        EventQueue.invokeLater(() -> {
            var text = "Tuğalsan Karabacak ♠☀☁☃☎☛ ŞşİiIıÜüÖöÇ窺Ğğ";

            MultiFontRenderer renderer = new MultiFontRenderer();
            if (showList) {
                renderer.showAsList(text);
            } else {
                renderer.show(text);
            }
        });
    }
}

Java相关问答推荐

Gmail Javi API批量处理太多请求

将偶数元素移动到数组的前面,同时保持相对顺序

Java模式匹配记录

如何打印本系列的第n项y=-(1)-(1+2)+(1+2+3)+(1+2+3+4)-(1+2+3+4+5)...Java中的(1+2+3+4...+n)

连接Quarkus中的两个异步操作

如何使用Maven和Spring Boot将构建时初始化、跟踪类初始化正确传递到本机编译

如何找到MongoDB文档并进行本地化?

获取字符串中带空格的数字和Java中的字符

Spring data JPA/Hibernate根据id获取一个列值

Com.example.service.QuestionService中的构造函数的参数0需要找不到的类型为';com.example.Dao.QuestionDao;的Bean

如何从JNI方法正确调用NSOpenPanel以在正确的线程上运行?

Spring @Value default无法计算表达式

有没有更快的方法在N个容器中删除重复项?

Tinylog中的滚动文件会在每次应用启动时覆盖日志(log)文件

Java堆中的许多java.time.ZoneRegion实例.ZoneId实例不应该被缓存吗?

如何在EL处理器中定义带有命名空间的变量?

向Java进程发送`kill-11`会引发NullPointerException吗?

如何从命令行编译包中的所有类?

如何制作回文程序?

SonarQube在合并升级到java17后对旧代码提出错误