• 在spring boot 中使用itext和itextrender生成pdf文件


    转载请注明出处 https://www.cnblogs.com/majianming/p/9539376.html
    项目中需要对订单生成pdf文件,在第一版本其实已经有了比较满意的pdf文档,但是还是存在问题的,主要是itext的css支持能力实在是太差,测试过程中发现margin都不支持,和我对接pdf的html模板的伙伴也是一直在改,凭着不想一直被打的希望,终于找到了下面好一点的方案。总的来说,就是加了itextrender这个支持常见的css2.1的封装。

    首选介绍一下这次使用的环境设置

    1. spring boot 2.0
    2. itext 5.5.13
    3. xmlworker 5.5.13
    4. itext-asin asian 5.2.0
    5. flying-saucer-pdf-itext5 9.1.12

    在maven pom文件添加

        <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.itextpdf.tool/xmlworker -->
        <dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.xhtmlrenderer/flying-saucer-pdf-itext5 -->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf-itext5</artifactId>
            <version>9.1.12</version>
        </dependency>
    

    在代码中默认使用

        ITextRenderer iTextRenderer = new ITextRenderer();
        iTextRenderer.setPDFVersion(PdfWriter.VERSION_1_7);
        iTextRenderer.setDocumentFromString(html);//pdf的内容,有其他生成模式,方法名类似
        iTextRenderer.layout();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            iTextRenderer.createPDF(outputStream);
        } catch (IOException e) {
            logger.error("输出pdf错误" + e.getMessage(), e);
        } catch (DocumentException e) {
            logger.error("生成pdf错误" + e.getMessage(), e);
        }
    

    好了,启动跑一下。中文文字一个都没有显示。按照之前的想法,那设置一下字体文件路径就好了吧,下面的借鉴于itext的com.itextpdf.text.FontFactoryImp#registerDirectories(), 用于向itextRender注册默认的字体路径,

        ITextRenderer iTextRenderer = new ITextRenderer();
        
        try {
            ITextFontResolver fontResolver = iTextRenderer.getFontResolver();
            String windir = System.getenv("windir");
            String fileSeparator = System.getProperty("file.separator");
            if (windir != null && fileSeparator != null) {
                fontResolver.addFontDirectory(windir + fileSeparator + "fonts", BaseFont.NOT_EMBEDDED);
            }
            fontResolver.addFontDirectory("/usr/share/X11/fonts", BaseFont.EMBEDDED);
            fontResolver.addFontDirectory("/usr/X/lib/X11/fonts", BaseFont.EMBEDDED);
            fontResolver.addFontDirectory("/usr/openwin/lib/X11/fonts", BaseFont.EMBEDDED);
            fontResolver.addFontDirectory("/usr/share/fonts", BaseFont.EMBEDDED);
            fontResolver.addFontDirectory("/usr/X11R6/lib/X11/fonts", BaseFont.EMBEDDED);
            fontResolver.addFontDirectory("/Library/Fonts", BaseFont.EMBEDDED);
            fontResolver.addFontDirectory("/System/Library/Fonts", BaseFont.EMBEDDED);
        } catch (IOException e) {
            logger.error("字体路径读取异常", e);
            //相关处理
            return;
        } catch (DocumentException e) {
            logger.error("字体解析异常", e);
             //相关处理
            return;
        }
        iTextRenderer.setPDFVersion(PdfWriter.VERSION_1_7);
        iTextRenderer.setDocumentFromString(html);
        iTextRenderer.layout();
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            iTextRenderer.createPDF(outputStream);
        } catch (IOException e) {
            logger.error("输出pdf错误" + e.getMessage(), e);
        } catch (DocumentException e) {
            logger.error("生成pdf错误" + e.getMessage(), e);
        }
    

    开心的运行起来,哈哈哈哈,要好了!
    欸?还是一样,是我的问题?肯定是我的问题!那就继续看吧
    看了许多的博客(忘记记录下来了,( ̄_, ̄ ))
    看到许多添加字体文件的都是类似于 fontResolver.addFont("simsun.ttc",BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);,向解析器注册宋体字体;第三个参数是字体的嵌入行为,有嵌入和不嵌入两种。那第二个参数是什么,查看命名,也就是encoding,字体的编码方式,看看ITextFontResolver#addFontDirectory(Sting path,boolean embedded)这个方法中,调用了addFont(String path, boolean embedded),在这个方法中找到了问题的根源,上面调用fontResolver.addFontDirectory这个方法设置字体的默认编码为Cp1252,这个编码并不支持中文,也就很好的说明了为什么pdf上的中文字符都没有显示,那么我们设置为Identity-H,这个时候将上面的代码加以修改
    首先修改默认的addFontDirectory方法

    /**
     * 因为默认设置的字体编码为西文,需要重写改为<code>BaseFont.IDENTITY_H</code>
     *
     * @param dir
     * @param embedded
     * @param fontResolver
     * @throws DocumentException
     * @throws IOException
     */
    private void addFontDirectory(String dir, boolean embedded, ITextFontResolver fontResolver)
            throws DocumentException, IOException {
        File f = new File(dir);
        if (f.isDirectory()) {
            File[] files = f.listFiles((dir1, name) -> {
                String lower = name.toLowerCase();
                return lower.endsWith(".otf") || lower.endsWith(".ttf") || lower.endsWith(".ttc");
            });
            for (int i = 0; i < files.length; i++) {
                fontResolver.addFont(files[i].getAbsolutePath(), BaseFont.IDENTITY_H, embedded);
            }
        }
    }
    

    所以生成的代码改为

        ITextRenderer iTextRenderer = new ITextRenderer();
        
        try {
            ITextFontResolver fontResolver = iTextRenderer.getFontResolver();
            String windir = System.getenv("windir");
            String fileSeparator = System.getProperty("file.separator");
            if (windir != null && fileSeparator != null) {
                addFontDirectory(windir + fileSeparator + "fonts", BaseFont.NOT_EMBEDDED, fontResolver);
            }
            addFontDirectory("/usr/share/X11/fonts", BaseFont.EMBEDDED, fontResolver);
            addFontDirectory("/usr/X/lib/X11/fonts", BaseFont.EMBEDDED, fontResolver);
            addFontDirectory("/usr/openwin/lib/X11/fonts", BaseFont.EMBEDDED, fontResolver);
            addFontDirectory("/usr/share/fonts", BaseFont.EMBEDDED, fontResolver);
            addFontDirectory("/usr/X11R6/lib/X11/fonts", BaseFont.EMBEDDED, fontResolver);
            addFontDirectory("/Library/Fonts", BaseFont.EMBEDDED, fontResolver);
            addFontDirectory("/System/Library/Fonts", BaseFont.EMBEDDED, fontResolver);
         } catch (IOException e) {
            logger.error("字体路径读取异常", e);
            //相关处理
            return;
        } catch (DocumentException e) {
            logger.error("字体解析异常", e);
             //相关处理
            return;
        }
        iTextRenderer.setPDFVersion(PdfWriter.VERSION_1_7);
        iTextRenderer.setDocumentFromString(html);
        iTextRenderer.layout();
         try (ServletOutputStream outputStream = response.getOutputStream()) {
            iTextRenderer.createPDF(outputStream);
        } catch (IOException e) {
            logger.error("输出pdf错误" + e.getMessage(), e);
        } catch (DocumentException e) {
            logger.error("生成pdf错误" + e.getMessage(), e);
        }
    

    运行,ok了

    转载请注明出处 https://www.cnblogs.com/majianming/p/9539376.html

  • 相关阅读:
    rocketmq手工创建topic出错
    rocketmq
    redis基本操作命令key命令+string命令+事务操作命令
    Redis启动常用管理命令
    --环比去年,row_number() over()取最新版本
    二分查找
    使用Python实现的4种快速排序算法
    卷积神经网络的理解
    两个很赞的用法(count函数里的表达式+计算时间间隔)
    MySQL中exists和in的区别及使用场景
  • 原文地址:https://www.cnblogs.com/majianming/p/9539376.html
Copyright © 2020-2023  润新知