使用IText导出复杂pdf

1、问题描述 

        需要将发票导出成pdf,要求每页都必须包含发票信息和表头行。

 

 

2、解决方法

        使用IText工具实现PDF导出

        IText8文档:Examples (itextpdf.com)

 

3、我的代码

      

 

 

        引入Itext依赖,我这里用的是8.0.1版本

 <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>8.0.1</version>
            <type>pom</type>
        </dependency>
MyItextpdfUtils.java
package com.easyexcel.util;


import com.easyexcel.handler.PaginationEventHandler;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.borders.Border;
import com.itextpdf.layout.element.*;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.properties.AreaBreakType;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import java.awt.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;


/**
 * @author Wulc
 * @date 2023/8/10 17:08
 * @description
 */
@Component
public class MyItextpdfUtils {

    public void createPDF() throws java.io.IOException {
        Resource resource = new ClassPathResource("/");
        String path = resource.getFile().getPath();
        //设置中文字体 C:\Windows\Fonts
        //PdfFont chineseFont =getFont();
        //PdfFont chineseFont = PdfFontFactory.createFont(this.getClass().getClassLoader().getResource("simsun.ttf").getPath());
        PdfFont chineseFontForTemplate = PdfFontFactory.createFont("D:\\学习资料\\后端\\STSONG.TTF");
        PdfFont chineseFontForContent = PdfFontFactory.createFont("D:\\学习资料\\后端\\STSONG.TTF");
        //创建每页的共有模板
        //*********************每页的共有模板*********************************
        String templatePath = path + "\\template.pdf";
        PdfDocument pdfDocumentTemplate = new PdfDocument(new PdfWriter(templatePath));
        //Document documentTemplate = new Document(pdfDocumentTemplate, PageSize.A4).setFont(chineseFontForTemplate);
        Document documentTemplate = new Document(pdfDocumentTemplate, PageSize.A4);
        //插入logo图片
        Table logoTemplateTable = new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(Border.NO_BORDER);
        ImageData imageData = ImageDataFactory.create(this.getClass().getClassLoader().getResource("logo.png"));
        Image image = new Image(imageData);
        image.setHeight(50);
        image.setWidth(100);
        logoTemplateTable.addCell(new Cell().setBorder(Border.NO_BORDER).add(image));
        //插入logo图片下方的一些信息
        Table logoInfoTable = new Table(UnitValue.createPercentArray(1)).useAllAvailableWidth().setBorder(Border.NO_BORDER);
        logoInfoTable.addCell(new Cell().setBorder(Border.NO_BORDER).setPadding(1).setFontSize(10).add(new Paragraph("Description1")));
        logoInfoTable.addCell(new Cell().setBorder(Border.NO_BORDER).setPadding(1).setFontSize(10).add(new Paragraph("Description2")));
        logoInfoTable.addCell(new Cell().setBorder(Border.NO_BORDER).setPadding(1).setFontSize(10).add(new Paragraph("Description3")));
        //插入标题
        Table titleTable = new Table(UnitValue.createPercentArray(4)).useAllAvailableWidth().setBorder(Border.NO_BORDER);
        titleTable.addCell(new Cell(1, 4).setBorder(Border.NO_BORDER).setPadding(1).setFontSize(15).add(new Paragraph("TITLE")).setTextAlignment(TextAlignment.CENTER));
        //插入标题下的一些信息
        Table titleInfoTable = new Table(UnitValue.createPercentArray(4)).useAllAvailableWidth();
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("QuestionA")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("AnswerA")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("QuestionB")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("AnswerB")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("QuestionC")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("AnswerC")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("QuestionD")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("AnswerD")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("QuestionE")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("AnswerE")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("QuestionF")));
        titleInfoTable.addCell(new Cell().setPadding(1).setFontSize(10).add(new Paragraph("AnswerF")));

        documentTemplate.add(logoTemplateTable);
        documentTemplate.add(logoInfoTable);
        documentTemplate.add(titleTable);
        documentTemplate.add(titleInfoTable);
        //*********************每页的共有模板*********************************


        //*********************每页的内容************************************
        String contentPath = path + "\\content.pdf";
        PdfDocument pdfDocumentContent = new PdfDocument(new PdfWriter(contentPath));
        //把内容使用共有模板
        pdfDocumentContent.addEventHandler(PdfDocumentEvent.END_PAGE, new PaginationEventHandler(pdfDocumentTemplate.getFirstPage().copyAsFormXObject(pdfDocumentContent)));
        Document documentContent = new Document(pdfDocumentContent, PageSize.A4).setFont(chineseFontForContent);
        //每页的content距离上面的template的距离
        documentContent.setTopMargin(250);
        Table contentTable = new Table(UnitValue.createPercentArray(6)).useAllAvailableWidth();
        //插入清单表格标题
        contentTable.addHeaderCell(new Cell().setFontSize(8).add(new Paragraph("No")));
        contentTable.addHeaderCell(new Cell().setFontSize(8).add(new Paragraph("title1")));
        contentTable.addHeaderCell(new Cell().setFontSize(8).add(new Paragraph("title2")));
        contentTable.addHeaderCell(new Cell().setFontSize(8).add(new Paragraph("title3")));
        contentTable.addHeaderCell(new Cell().setFontSize(8).add(new Paragraph("title4")));
        contentTable.addHeaderCell(new Cell().setFontSize(8).add(new Paragraph("title5")));
        for (int i = 0; i < 300; i++) {
            contentTable.addCell(new Cell().setFontSize(8).add(new Paragraph(String.valueOf(i))));
            contentTable.addCell(new Cell().setFontSize(8).add(new Paragraph("content1")));
            contentTable.addCell(new Cell().setFontSize(8).add(new Paragraph("content2")));
            contentTable.addCell(new Cell().setFontSize(8).add(new Paragraph("content3")));
            contentTable.addCell(new Cell().setFontSize(8).add(new Paragraph("content4")));
            contentTable.addCell(new Cell().setFontSize(8).add(new Paragraph("content5")));
        }
        //尾页
        Table lastInfoTable = new Table(UnitValue.createPercentArray(3)).setWidth(300);
        lastInfoTable.addCell(new Cell(1, 3).setPadding(1).setFontSize(8).add(new Paragraph("Total:")));
        lastInfoTable.addCell(new Cell(1, 1).setPadding(1).setFontSize(8).add(new Paragraph("统计A:")));
        lastInfoTable.addCell(new Cell(1, 2).setPadding(1).setFontSize(8).add(new Paragraph("1234567")));
        lastInfoTable.addCell(new Cell(1, 1).setPadding(1).setFontSize(8).add(new Paragraph("统计B:")));
        lastInfoTable.addCell(new Cell(1, 2).setPadding(1).setFontSize(8).add(new Paragraph("7654321")));
        //*********************每页的内容************************************

        documentContent.add(contentTable);
        //尾页新开一页
        documentContent.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
        documentContent.add(lastInfoTable);
        documentTemplate.close();
        documentContent.close();
    }


}

 

PDFTest.java​​​​​​​
package com.easyexcel;

import com.easyexcel.util.MyItextpdfUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;

/**
 * @author Wulc
 * @date 2023/8/10 17:52
 * @description
 */
@SpringBootTest(classes = SpringbootApplication.class)
@RunWith(SpringRunner.class)
public class PDFTest {
    @Autowired
    private MyItextpdfUtils myItextpdfUtils;

    @Test
    public void test6() throws IOException {
        myItextpdfUtils.createPDF();
    }
}

        测试一下:

 

4、总结

        IText8不支持中文,需要引入外部字体文件,如果是以其中一个pdf作为每页的背景模板生成PDF这种方式(copyAsFormXObject),它只能支持其中一个pdf中文,另一个就不支持了。

Document documentTemplate = new Document(pdfDocumentTemplate, PageSize.A4).setFont(chineseFontForTemplate);
Document documentContent = new Document(pdfDocumentContent, PageSize.A4).setFont(chineseFontForContent);

如上代码,虽然我同时把背景版和内容同时都设置了中文字体,但是template和content合一块的时候,template的背景版pdf的中文字体就会失效了。

         不过还好,因为是海外的发票都是英文的,因此不需要考虑支持中文的问题。

        希望哪位大佬能帮忙解决一下IText8 copyAsFormXObject中文兼容性问题!!!

5、参考资料

https://www.cnblogs.com/sky-chen/p/13026203.html#autoid-1-4-5-0-0-0
https://kb.itextpdf.com/home/it7kb/examples/repeating-parts-of-a-form
https://zhuanlan.zhihu.com/p/537723847
https://blog.csdn.net/weixin_43409994/article/details/118157694
https://blog.csdn.net/u012397189/article/details/126345744
https://blog.csdn.net/Thinkingcao/article/details/84988392

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/81225.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[线程/C]基础

文章目录 1. 线程介绍2. 创建线程2.1 线程函数2.2 创建线程 3. 线程退出4. 线程回收4.1 线程函数4.2 回收子线程数据4.2.1 使用子线程栈4.2.2 使用全局变量4.2.3 使用主线程栈 5. 线程分离6. 其他线程函数6.1 线程取消6.2 线程ID的比较 1. 线程介绍 线程是轻量级的进程&#x…

【HarmonyOS】【DevEco Studio】ohpm安装失败该如何解决?

【关键词】 HarmonyOS、DevEco Studio、ohpm安装失败 【问题背景及解决方案】 最近遇到很多DevEco Studio安装ohpm失败的问题&#xff0c;下面给大家介绍几种出现的问题以及解决方案&#xff1a; 1、ohpm not set up&#xff0c;报错截图如下&#xff1a; ​ 解决方案&…

T113-S3-TCA6424-gpio扩展芯片调试

目录 前言 一、TCA6424介绍 二、原理图连接 三、设备树配置 四、内核配置 五、gpio操作 总结 前言 TCA6424是一款常用的GPIO&#xff08;通用输入输出&#xff09;扩展芯片&#xff0c;可以扩展微控制器的IO口数量。在T113-S3平台上&#xff0c;使用TCA6424作为GPIO扩展芯…

Linux Mint 21.3 计划于 2023 年圣诞节发布

导读Linux Mint 项目近日公布了基于 Ubuntu 的 Linux Mint 发行版下一个重要版本的一些初步细节&#xff0c;以及备受期待的基于 Debian 的 LMDE 6&#xff08;Linux Mint Debian Edition&#xff09;版本。 近日&#xff0c;Linux Mint 项目负责人克莱门特-勒菲弗&#xff08;…

腾讯云GPU服务器GN7实例NVIDIA T4 GPU卡

腾讯云GPU服务器GN7实例搭载1颗 NVIDIA T4 GPU&#xff0c;8核32G配置&#xff0c;系统盘为100G 高性能云硬盘&#xff0c;自带5M公网带宽&#xff0c;系统镜像可选Linux和Windows&#xff0c;地域可选广州/上海/北京/新加坡/南京/重庆/成都/首尔/中国香港/德国/东京/曼谷/硅谷…

win11安装ubuntu 子系统安装过程及注意事项

第一步 &#xff1a;安装系统必须组件 由于子系统是系统自带组件&#xff0c;需要安装软件支持 第二步&#xff1a;应用商店安装 ubuntu 编辑 编辑 这个时候打开会报错 第三步&#xff0c;运行linux子系统 选择Windows PowerShell 以管理员身份运行&#xff09; 输入&#…

You don‘t have an extension for debugging html.

flutter在vscode点 菜单栏的run 下的 start debugging后出现下面的错误。 点到main.dart文件&#xff0c;再执行运行按钮。

企业如何开展个人信息安全影响评估(PIA)二

基本概念 根据《信息安全 技术个人信息安全影响评估指南》&#xff08;GB/T 39335—2020&#xff1b;personal information security impact assessment&#xff0c;简称“PIA”&#xff09;&#xff0c;个人信息安全影响评估是指针对个人信息处理活动&#xff0c;检验其合法合…

jvm-运行时数据区概述及线程

1.运行时数据区内部结构 不同的jvm对于内存的划分方式和管理机制存在着部分差异 java虚拟机定义了若干种程序运行期间会使用到的运行时数据区&#xff0c;其中有一些会随着虚拟机的启动而创建&#xff0c;随着虚拟机的退出而销毁&#xff0c;另外一些则是与线程一一对应的&…

Spring Boot 统一功能处理

目录 1.用户登录权限效验 1.1 Spring AOP 用户统一登录验证的问题 1.2 Spring 拦截器 1.2.1 自定义拦截器 1.2.2 将自定义拦截器加入到系统配置 1.3 拦截器实现原理 1.3.1 实现原理源码分析 2. 统一异常处理 2.1 创建一个异常处理类 2.2 创建异常检测的类和处理业务方法 3. 统一…

【C# 基础精讲】LINQ to XML查询

LINQ to XML 是 C# 中用于查询和操作 XML 数据的强大工具。它允许您使用 LINQ 查询语法对 XML 文档进行查询、过滤、投影等操作&#xff0c;从而更加方便地处理 XML 数据。本文将详细介绍 LINQ to XML 的基本概念、常见操作以及示例&#xff0c;帮助您了解如何在 C# 中使用 LIN…

微软Win11 Dev预览版Build23526发布

近日&#xff0c;微软Win11 Dev预览版Build23526发布&#xff0c;修复了不少问题。牛比如斯Microsoft&#xff0c;也有这么多bug&#xff0c;所以你写再多bug也不作为奇啊。 主要更新问题 [开始菜单&#xff3d; 修复了在高对比度主题下&#xff0c;打开开始菜单中的“所有应…

泛型编程 学习笔记

#include "iostream"using namespace std;template<typename T> void Print(T a) {cout << a << endl; }int main() {int a 5;double b 2.3;char c e;string d "sdfasd";Print(a);Print(b);Print(c);Print(d);return 0; } 它可以不用…

LLaMA-7B微调记录

Alpaca&#xff08;https://github.com/tatsu-lab/stanford_alpaca&#xff09;在70亿参数的LLaMA-7B上进行微调&#xff0c;通过52k指令数据&#xff08;https://github.com/tatsu-lab/stanford_alpaca/blob/main/alpaca_data.json&#xff09;&#xff0c;在8个80GB A100上训…

【Java转Go】快速上手学习笔记(一)之环境安装篇

前言 前两天开始学习Go&#xff0c;需要写篇笔记记录总结一下。 Go它也是可以做web开发的&#xff0c;就像Java一样&#xff0c;做JavaWeb项目&#xff0c;Go也可以做GoWeb项目。当然Go的作用用处肯定不止这个&#xff0c;还有很多&#xff0c;只是因为我目前的话&#xff0c…

threejs使用gui改变相机的参数

调节相机远近角度 定义相机的配置&#xff1a; const cameraConfg reactive({ fov: 45 }) gui中加入调节fov的方法 const gui new dat.GUI();const cameraFolder gui.addFolder("相机属性设置");cameraFolder.add(cameraConfg, "fov", 0, 100).name(…

代码随想录-字符串01 344.反转字符串541. 反转字符串II替换空格翻转字符串里的单词左旋转字符串

题目目录 ● 344.反转字符串 ● 541. 反转字符串II ● 剑指Offer 05.替换空格 ● 151.翻转字符串里的单词 ● 剑指Offer58-II.左旋转字符串 344.反转字符串 344.反转字符串 很经典的字符串考察点&#xff0c;考察对双指针的熟悉程度。 解法是通过双指针从字符串数组两边向中…

ajax-axios-url-form-serialize 插件

AJAX AJAX 概念 1.什么是 AJAX ? mdn 使用浏览器的 XMLHttpRequest 对象 与服务器通信 浏览器网页中&#xff0c;使用 AJAX技术&#xff08;XHR对象&#xff09;发起获取省份列表数据的请求&#xff0c;服务器代码响应准备好的省份列表数据给前端&#xff0c;前端拿到数据数…

Vue3 setup中使用$refs

在 Vue 3 中的 Composition API 中&#xff0c;$refs 并不直接可用于 setup 函数。这是因为 $refs 是 Vue 2 的实例属性&#xff0c;而在 Vue 3 中&#xff0c;setup 函数是与模板实例分离的&#xff0c;不再使用实例属性。 实际工作中确实有需求&#xff0c;在setup 函数使用…

windows服务器下java程序健康检测及假死崩溃后自动重启应用、开机自动启动

前两天由于项目需要&#xff0c;一个windows上的批处理任务&#xff08;kitchen.bat&#xff09;&#xff0c;需要接到mq的消息通知后执行&#xff0c;为了快速实现这里我们通过springboot写了一个jar程序&#xff0c;用于接收mq的消息&#xff0c;并调用bat文件。 本程序需要实…