Java中如何基于Font.canDisplay方法按偏好字体列表拆分字符串
按偏好字体顺序结合码点支持拆分字符串
需求说明
给定一个按优先级排序的字体列表,需要将输入字符串拆分为连续字符片段,每个片段使用列表中第一个能显示该片段所有字符码点的字体,最终返回每个片段及其对应的字体索引。
核心实现
首先定义一个简单记录类存储片段和字体索引(Java 16+ 可用,低版本可替换为AbstractMap.SimpleEntry或自定义类):
record TextFontFragment(String text, int fontIndex) {}
然后实现拆分逻辑:
import java.awt.Font; import java.util.ArrayList; import java.util.List; public class TextFontSplitter { public static List<TextFontFragment> fragment(String text, List<Font> preferredFonts) { List<TextFontFragment> fragments = new ArrayList<>(); if (text.isEmpty() || preferredFonts.isEmpty()) { return fragments; } // 初始化第一个字符的字体索引 int currentFontIdx = findFirstSupportingFontIdx(text.codePointAt(0), preferredFonts); StringBuilder currentFragment = new StringBuilder(); int offset = 0; // 遍历字符串的每个码点 while (offset < text.length()) { int codePoint = text.codePointAt(offset); int targetFontIdx = findFirstSupportingFontIdx(codePoint, preferredFonts); if (targetFontIdx == currentFontIdx) { // 同一字体,追加字符 currentFragment.appendCodePoint(codePoint); } else { // 字体切换,保存当前片段并重置 fragments.add(new TextFontFragment(currentFragment.toString(), currentFontIdx)); currentFragment = new StringBuilder().appendCodePoint(codePoint); currentFontIdx = targetFontIdx; } // 移动偏移量,处理代理对(如emoji等4字节字符) offset += Character.charCount(codePoint); } // 添加最后一个未保存的片段 if (!currentFragment.isEmpty()) { fragments.add(new TextFontFragment(currentFragment.toString(), currentFontIdx)); } return fragments; } // 按偏好顺序找到第一个支持该码点的字体索引 private static int findFirstSupportingFontIdx(int codePoint, List<Font> preferredFonts) { for (int i = 0; i < preferredFonts.size(); i++) { if (preferredFonts.get(i).canDisplay(codePoint)) { return i; } } // 无匹配字体时返回最后一个(可根据需求调整默认逻辑) return preferredFonts.size() - 1; } }
使用示例
public class Main { public static void main(String[] args) { // 初始化偏好字体列表(需确保字体文件路径正确) List<Font> preferredFontList = List.of( loadFont("Roboto-Regular.ttf"), loadFont("FreeSerif.ttf"), loadFont("Quivira-A8VL.ttf"), loadFont("Code2000-rdLO.ttf") ); String text = "Tuğalsan Karabacak ♠☀☁☃☎☛ ŞşİiIıÜüÖöÇ窺Ğğ"; TextFontSplitter.fragment(text, preferredFontList).forEach(fragment -> { // 调用渲染方法,传入片段和对应字体 addText(fragment.text(), preferredFontList.get(fragment.fontIndex())); }); } // 字体加载辅助方法 private static Font loadFont(String fontFilePath) { try { return Font.createFont(Font.TRUETYPE_FONT, new java.io.File(fontFilePath)); } catch (Exception e) { throw new RuntimeException("加载字体失败: " + fontFilePath, e); } } // 模拟文本渲染方法 private static void addText(String text, Font font) { System.out.printf("渲染文本: %s | 使用字体: %s%n", text, font.getName()); } }
关键细节说明
- Unicode码点处理:使用
codePointAt和Character.charCount遍历字符,避免拆分Unicode代理对(如表情符号等4字节字符)。 - 优先级匹配:严格按照字体列表顺序检查
Font.canDisplay(codePoint),确保优先使用高优先级字体。 - 边界处理:覆盖空文本、空字体列表的异常场景,循环结束后主动保存最后一段文本。
内容的提问来源于stack exchange,提问作者Tugalsan Karabacak
相关产品推荐
相关产品推荐

