给定首选字体列表,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();
我如何根据preferredFontList顺序分段字符串及其支持显示(Font.canDisplay(...))codePoints? TS_FileTmcrFileHandler.addText(...)
//simplified code
var text = "Tuğalsan Karabacak ♠☀☁☃☎☛ ŞşİiIıÜüÖöÇ窺Ğğ";
fragment(text, preferredFontList).forEach((subText, decidedFontIdx)->{
addText(subText, preferredFontList.get(decidedFontIdx));
});
你对“碎片”这个词的使用让我感到困惑。这并没有错,但它含糊不清。我假设您希望将字符串分解(分段)为子字符串,每个子字符串都可以通过您喜欢的字体之一完全呈现。
“文本串”是具有共同属性的字符子序列。在这种情况下,该公共属性是将用于呈现字符的字体。 Java 有一个类可以存储具有基于属性的文本:AttributedString。
因此,我们可以创建一个循环,尝试在每种字体上调用 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;
}
我们现在可以通过几种不同的方式使用 AttributedString 的 iterator:
这是第一种方法的示例:
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);
}
});
}
}