[POI] ofdrw 2.1.0 转换pdf,部分ofd内部字体无法加载的问题

字体问题

导致字体无法加载主要有两个因素: 1. 系统内没有安装对应的字体 2. 如果是ofd文件ofdrw首先去ofd解压文件抓取内部字体文件,如果这里出现异常会导致该部分字体无法正常显示。相关问题也可以看我这篇开发手账(一),下面是解决方案

一、对于没有安装字体的,需要安装字体,或者使用org.ofdrw.converter.FontLoader#scanFontDir(java.nio.file.Path)进行启动时扫描,可使用反射对以加载字体进行查看。

Field namePathMapping = ReflectUtil.getField(FontLoader.class, "fontNamePathMapping");

Map<String, String> fontNamePathMapping = (Map<String, String>) ReflectUtil.getFieldValue(preload,namePathMapping);

System.out.println("已加载字体:" + JSONUtil.toJsonStr(fontNamePathMapping.keySet()));

二、ofdrw会走下面的方法org.ofdrw.converter.FontLoader#loadFontSimilarStream进行字体加载,可用看到它在首次会判断从ResourceLocator 加载ctFont,并没有对字体文件有效性进行判断,如果外部异常,则无法加载默认字体从而导致部分文字直接显示不出来。

解决方法:可以直接注释掉内嵌字体相关代码(我选用的),或者在异常报错处进行捕获并加载有效字体。需要这里都是需要对ofdrw源码进行修改

    public InputStream loadFontSimilarStream(ResourceLocator rl, CT_Font ctFont) {
        byte[] buf = null;
        try {
            if (ctFont != null) {
                // 内嵌字体绝对路径
                //ST_Loc fontFileLoc = ctFont.getFontFile();
                //if (fontFileLoc != null) {
                //    String fontAbsPath = rl.getFile(ctFont.getFontFile()).toAbsolutePath().toString();
                //    buf = Files.readAllBytes(Paths.get(fontAbsPath));
                //} else {

                    // 无法从内部加载时,通过相似字体查找
                    String similarFontPath = getReplaceSimilarFontPath(ctFont.getFamilyName(), ctFont.getFontName());
                    if (similarFontPath != null) {
                        buf = Files.readAllBytes(Paths.get(similarFontPath));
                    }
                //}
            }
        } catch (Exception e) {
            if (DEBUG) {
                log.warn("无法加载字体: " + ctFont.getFamilyName() + " " + ctFont.getFontName() + " " + ctFont.getFontFile(), e);
            }
        }

        if (buf == null || buf.length == 0) {
            try {
                buf = Files.readAllBytes(DefaultFontPath);
            } catch (IOException e) {
                throw new RuntimeException("默认字体文件读取异常", e);
            }
        }
        return new ByteArrayInputStream(buf);
    }

匹配规则问题

2024年2月4日-今天又被折磨了一天,OFD文件会存在调用字体别名的情况,而ofdrw加载匹配规则并不全,导致像FZXBSKFZXiaoBiaoSong无法匹配对应的字体方正小标宋_GBK

解决方法:①添加匹配规则 ②进行中文转拼音和拼音首字母。

		FontLoader preload = FontLoader.Preload();
        preload.addSimilarFontReplaceRegexMapping(".*FZXBSK.*","方正小标宋_GBK");
        preload.addSimilarFontReplaceRegexMapping(".*FZHTK.*","方正黑体_GBK");
        preload.addSimilarFontReplaceRegexMapping(".*FZFSK.*","方正仿宋_GBK");
        preload.addSimilarFontReplaceRegexMapping(".*FZKTK.*","方正楷体_GBK");
        preload.addSimilarFontReplaceRegexMapping(".*FZXiaoBiaoSong.*","方正小标宋_GBK");

2024年2月4日-晚,org.ofdrw.converter.FontLoader#similarFontReplaceRegexMapping 的ketset 并没有进行优先最长匹配,会导致FZXiaoBiaoSong匹配为了.*Song.*。需要修改org.ofdrw.converter.FontLoader#getReplaceSimilarFontPath 代码

    /**
     * 获取配置的 相似字体 对应的字体路径
     *
     * @param familyName 字族名(用于在字体不存在是替代字体,可为空)
     * @param fontName   字体名
     * @return 字体操作系统内绝对路径,如果不存在返还 null
     */
    public String getReplaceSimilarFontPath(@Nullable String familyName, String fontName) {
        if (fontName == null && familyName == null) {
            return null;
        }
        // 首先尝试使用名称直接查找
        String fontPath = getSystemFontPath(familyName, fontName);
        if (fontPath != null) {
            return fontPath;
        }

        String name = null;
        //todo 排序 最长匹配
        List<Map.Entry<Pattern, String>> sort = CollUtil.sort(similarFontReplaceRegexMapping.entrySet(), (m1, m2) -> -m1.getKey().pattern().length() + m2.getKey().pattern().length());
        for (Map.Entry<Pattern, String> entry : sort) {
            Pattern pattern = entry.getKey();
            if (fontName != null && pattern.matcher(fontName).matches()) {
                name = entry.getValue();
                break;
            }
            if (familyName != null && pattern.matcher(familyName).matches()) {
                // 通过字体名称无法找到时,使用字族名替换
                name = entry.getValue();
                break;
            }
        }
        if (name != null) {
            fontPath = getSystemFontPath(null, name);
        }

        return fontPath;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值