JAVA-创建PDF文档

news2024/11/25 11:26:51

目录

一、前期准备

1、中文字体文件

2、maven依赖

二、创建PDF文档方法

三、通过可填充PDF模板将业务参数进行填充

1、 设置可填充的PDF表单

2、代码开干,代码填充可编辑PDF并另存文件



一、前期准备

1、中文字体文件

本演示使用的是iText 7版本,如果没有中文字体,那生成的PDF文档涉及中文的区域都无法显示。

现有查找到的PDF免费下载网址如下:

  • 阿里巴巴矢量图标库:除了图标库,该网站还提供了一些免费的字体库供下载和使用。
  • 字由:字由是一个专注于中文字体的网站,提供了一些优质的免费字体供下载。
  • 字体中国:字体中国是一个提供中文字体下载的网站,包含了许多中文设计师的作品。
  • 站长之家字体库:站长之家提供了大量的免费字体库,包含了各种中文字体和英文字体。

2、maven依赖

    <dependencies>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.2.5</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>kernel</artifactId>
            <version>7.2.5</version>
        </dependency>

    </dependencies>

二、创建PDF文档方法

import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Cell;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.properties.HorizontalAlignment;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;

public class CreatePdf {

    //文件根目录
    public static String RootPath = "D:/files-pdf/";
    // 设置字体文件路径
    //注意:如果没有中文字体,那PDF内容涉及中文的区域都不显示
    public static String fontPath = RootPath + "ZiTiQuanWeiJunHei-W1-2.ttf";

    public static void main(String[] args) {

        String pdfName = "testContent.pdf";

        // 创建 PdfWriter 和 PdfDocument
        PdfWriter writer = null;
        try {
            writer = new PdfWriter(pdfName + ".pdf");
        } catch (FileNotFoundException e) {
            System.out.println("创建PdfWriter失败。。。。。");
            e.printStackTrace();
            return;
        }

        PdfDocument pdfDocument = new PdfDocument(writer);

        // 创建 Document
        Document document = new Document(pdfDocument);

        // 设置页面的边距
        documentMargins(document);

        // 设置页眉和页脚
        headerAndFooter(pdfDocument);

        //编写PDF主体的文档内容 , 这一块是主要编写位置
        setContent(document);

        //添加水印
        addWatermark(pdfDocument);

        // 关闭对象
        document.close();   //document文档要在输出前关闭,不然会提示“java.nio.file.NoSuchFileException: editable.pdf”
        pdfDocument.close();

        // 将生成的 PDF 文件移动到指定目录下
        downloadPdf(RootPath, pdfName);
    }

    /**
     * 获取设置的字体
     *
     * @return PdfFont 字体
     */
    private static PdfFont getFont() {
        // 设置中文字体
        PdfFont font = null;
        try {
            font = PdfFontFactory.createFont(fontPath);
        } catch (IOException e) {
            System.out.println("字体获取失败。。。。。。。。。。。");
            e.printStackTrace();
            return null;
        }
        return font;
    }

    /**
     * 设置页面的边距
     *
     * @param document 内容文档
     */
    private static void documentMargins(Document document) {
        // 上、右、下、左
        int margins = 80;
        document.setMargins(margins, margins, margins, margins);
    }

    /**
     * 设置页眉页脚
     *
     * @param pdfDocument PDF文档
     */
    private static void headerAndFooter(PdfDocument pdfDocument) {

        pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, new IEventHandler() {

            @Override
            public void handleEvent(Event event) {
                PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
                PdfPage page = docEvent.getPage();
                PdfCanvas canvas = new PdfCanvas(page);

                // 创建页眉
                Rectangle pageSize = page.getPageSize();

                canvas.beginText()
                        .setFontAndSize(getFont(), 10)
                        .moveText(pageSize.getWidth() / 2, pageSize.getTop() - 20)
                        .showText("这是页眉")
                        .endText();

                // 创建页脚
                canvas.beginText()
                        .setFontAndSize(getFont(), 10)
                        .moveText(pageSize.getWidth() / 2, pageSize.getBottom() + 20)
                        .showText("这是页脚")
                        .endText();

                canvas.release();
            }
        });
    }


    /**
     * 添加水印
     *
     * @param pdfDocument PDF文档
     * @throws MalformedURLException
     */
    private static void addWatermark(PdfDocument pdfDocument) {

        // 加载水印图片
        String watermarkImage = RootPath + "zm5.jpg";
        Image image = null;
        try {
            image = new Image(ImageDataFactory.create(watermarkImage));
        } catch (MalformedURLException e) {
            System.out.println("获取水印图片失败 , e : " + e);
            e.printStackTrace();
            return;
        }

        // 获取 PDF 页面的大小,此处是要以页面区域做参考,后续好设置对应的坐标填充水印
        Rectangle rectanglePageSize = pdfDocument.getDefaultPageSize();

        // 遍历每个页面,添加水印
        for (int i = 1; i <= pdfDocument.getNumberOfPages(); i++) {
            //创建PDF画布
            PdfCanvas pdfCanvas = new PdfCanvas(pdfDocument.getPage(i));

            // 创建 Canvas 画布对象,设置位置和大小
            Canvas canvas = new Canvas(pdfCanvas, rectanglePageSize, true);

            // 在水印画布上添加图片,并设置透明度和位置
            // 上、右、下、左
            image.setOpacity(0.8f).setMargins(600, 200, 0, 300);
            canvas.add(image);

            // 关闭水印画布
            canvas.close();
        }
    }

    /**
     * 生成的 PDF 文件移动到指定目录下
     *
     * @param rootPath 存储目录
     * @param pdfName  PDF文件名
     * @return
     */
    private static String downloadPdf(String rootPath, String pdfName) {

        // 假设生成的 PDF 文件路径为 sourcePath
        Path sourcePath = Paths.get(pdfName + ".pdf");
        // 假设目标目录路径为 targetDirectoryPath
        Path targetDirectoryPath = Paths.get(rootPath);
        // 移动文件到目标目录
        Path targetPath = null;
        try {
            targetPath = Files.move(sourcePath, targetDirectoryPath.resolve(sourcePath.getFileName()), StandardCopyOption.REPLACE_EXISTING);
            // 输出成功信息
            System.out.println("文件移动成功,目标路径:" + targetPath);
            return targetPath.toString();
        } catch (IOException e) {
            // 输出失败信息
            System.out.println("文件移动失败,目标路径:" + targetPath);
            e.printStackTrace();
            return null;
        }
    }

    /**
     * PDF主体内容
     *
     * @param document 文档
     */
    private static void setContent(Document document) {

        // 设置中文字体
        PdfFont font = getFont();

        /********************************  段落内容由 Paragraph 区域编写 ******************************************/
        // 创建段落标题
        Paragraph paragraphTitle = new Paragraph().setFont(font);
        // setBold:设置粗体,setItalic:斜体,setUnderline:下划线
        paragraphTitle.add("这是一份PDF测试文档").setBold().setFontSize(12);
        // 设置段落的对齐方式为居中
        paragraphTitle.setTextAlignment(TextAlignment.CENTER);
        document.add(paragraphTitle);

        // 创建一个可编辑的段落
        Paragraph nameOfStaff = new Paragraph().setFont(font).setFontSize(10);
        nameOfStaff.add("Name of Developer / 开发人员:");
        document.add(nameOfStaff);

        Paragraph fullname = new Paragraph().setFont(font).setFontSize(10);
        fullname.add("____________________(Note: 请写全名)");
        document.add(fullname);

        Paragraph paragraph1 = new Paragraph().setFont(font).setFontSize(9);
        paragraph1.add("这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
                "这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
                "这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
                "这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!" +
                "这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!这是段落内容!");
        document.add(paragraph1);

        Paragraph declare = new Paragraph().setFont(font).setFontSize(9);
        declare.add("I hereby declare that:");
        document.add(declare);

        Paragraph section = new Paragraph().setFont(font).setFontSize(9).setBold().setUnderline();
        section.add("多选项声明确认!Multiple option declaration confirmation!");
        document.add(section);

        // 创建一个带有复选框的列表
        Paragraph paragraph2 = new Paragraph().setFont(font).setFontSize(9);
        paragraph2.add("(Please tick in the box as appropriate)").add("\n");
        paragraph2.add("口 声明内容1。。。声明内容1。。。声明内容1。。。声明内容1。。。声明内容1。。。声明内容1。。。").add("\n");
        paragraph2.add("口 声明内容2。。。声明内容2。。。声明内容2。。。声明内容2。。。声明内容2。。。声明内容2。。。").add("\n");
        document.add(paragraph2);


        /********************************  表格由 Table 区域编写 ******************************************/
        // 创建表格并设置列数和默认宽度
        Table table = new Table(4);
//            table.setMaxWidth(1000);  //固定式宽度
//            table.setAutoLayout();    //根据内容自适应宽度
        table.setWidth(UnitValue.createPercentValue(100));  //页面总宽固定
        // 添加表格标题(合并4列)
        Cell titleCell = new Cell(1, 4);
        // 创建文本对象
        titleCell.add(new Paragraph("Table Title"));
        titleCell.setTextAlignment(TextAlignment.CENTER);
        table.addHeaderCell(titleCell);

        // 添加表头
        table.addHeaderCell("Header 1");
        table.addHeaderCell("Header 2");
        table.addHeaderCell("Header 3");
        table.addHeaderCell("Header 4");

        // 添加表格内容
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 4; j++) {
                Cell cell = new Cell()
                        .setFont(font)
                        .add(new Paragraph("行 " + (i + 1) + ", Col " + (j + 1)))
                        .setWidth(UnitValue.createPercentValue(25));
                table.addCell(cell);
            }
        }
        // 设置表格样式
        table.setHorizontalAlignment(HorizontalAlignment.CENTER);

        // 将表格添加到文档中
        document.add(table);

        /********************************  内容区域编写END ******************************************/
    }

}

三、通过可填充PDF模板将业务参数进行填充

1、 设置可填充的PDF表单

至于编辑器自行查找,免费的基本大多会添加水印。

设置成功后,如下图,可编辑区高亮显示

 

2、代码开干,代码填充可编辑PDF并另存文件

import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class FillingPdfTemplate {

    //文件根目录
    public static String RootPath = "D:/files-pdf/";
    // 设置字体文件路径
    //注意:如果没有中文字体,那PDF内容涉及中文的区域都不显示
    public static String fontPath = RootPath + "ZiTiQuanWeiJunHei-W1-2.ttf";


    public static void main(String[] args) {
        Map<String, String> mapParam = new HashMap<>();
        mapParam.put("fullname", "某某某");
        mapParam.put("Check Box1","On");
        mapParam.put("Check Box2", "Off");
        mapParam.put("account1", "account1");
        mapParam.put("broker1", "broker1");
        mapParam.put("number1", "number1");
        mapParam.put("security1", "security1");
        mapParam.put("sharehold1", "sharehold1");
        mapParam.put("clp1", "clp1");
        mapParam.put("shares1", "shares1");
        mapParam.put("sharehold3", "");
        mapParam.put("clp3", "");
        mapParam.put("shares3", "");
        mapParam.put("name1", "某某某");
        mapParam.put("position", "XXX高级");
        mapParam.put("company", "深圳市XXX科技有限公司");
        mapParam.put("date", "2023-05-24");

        String templatePdfPath = RootPath + "Acrobat-demo.pdf";
        String destPdfPath = RootPath + "Acrobat-demo-result.pdf";
        replaceTextFieldPdf(templatePdfPath, destPdfPath, mapParam);
    }

    /**
     * 获取设置的字体
     *
     * @return PdfFont 字体
     */
    private static PdfFont getFont() {
        // 设置中文字体
        PdfFont font = null;
        try {
            font = PdfFontFactory.createFont(fontPath);
        } catch (IOException e) {
            System.out.println("字体获取失败。。。。。。。。。。。");
            e.printStackTrace();
            return null;
        }
        return font;
    }

    /**
     * 替换PDF文本表单域变量
     *
     * @param templatePdfPath 要替换的pdf全路径
     * @param params          替换参数
     * @param destPdfPath     替换后保存的PDF全路径
     * @throws IOException
     */
    public static final void replaceTextFieldPdf(String templatePdfPath, String destPdfPath,
                                                 Map<String, String> params) {

        PdfDocument pdfDoc = null;
        try {
            pdfDoc = new PdfDocument(new PdfReader(templatePdfPath), new PdfWriter(destPdfPath));
        } catch (IOException e) {
            e.printStackTrace();
        }

        //获取表单信息
        PdfAcroForm pdfAcroForm = PdfAcroForm.getAcroForm(pdfDoc, true);

        //遍历填充预设的值
        Set<Map.Entry<String, String>> entries = params.entrySet();
        entries.stream().forEach(entry -> {
            if(pdfAcroForm.getField(entry.getKey()) != null){
                // 设置表单字段的值
                //复选框类型的:1勾选,2圆圈,3叉叉,4菱形,5方块,6星星
                //如果不想复选框被选中,要设置为"Off",选中设置为"On",注意大小写
                pdfAcroForm.getField(entry.getKey()).setCheckType(1).setValue(entry.getValue() , true).setFont(getFont());
            }
        });
        // 添加表单字段
//        PdfTextFormField textField = PdfFormField.createText(pdfDoc, new Rectangle(100, 100, 200, 20), "newField", "");
//        pdfAcroForm.addField(textField);
        //表单扁平化,设置后生成的文档不可再编辑
//        pdfAcroForm.flattenFields();
        pdfDoc.close();
    }


}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Jeddak-DPSQL 首次开源!基于差分隐私的 SQL 代理保护能力

动手点关注 干货不迷路 ‍ ‍1. 背景 火山引擎对于用户敏感数据尤为重视&#xff0c;在火山引擎提供的数据分析产品中&#xff0c;广泛采用差分隐私技术对用户敏感信息进行保护。此类数据产品通常构建于 ClickHouse 等数据引擎之上&#xff0c;以 SQL 查询方式来执行计算逻辑&a…

【计算机网络复习】第六章 关系数据理论 1

关系模式的设计 按照一定的原则从数量众多而又相互关联的数据中&#xff0c;构造出一组既能较好地反映现实世界&#xff0c;而又有良好的操作性能的关系模式 ●冗余度高 ●修改困难 ●插入问题 ●删除问题 ★产生问题的原因 属性间约束关系&#xff08;即数据间的依赖关系&…

【JavaSE】Java基础语法(十):构造方法

文章目录 ⛄1. 构造方法的格式和执行时机⛄2. 构造方法的作用⛄3. 构造方法的特点⛄4. 构造方法的注意事项⛄5. 构造方法为什么不能被重写 在面向对象编程的思想中&#xff0c;构造方法&#xff08;Constructor&#xff09;是一个特殊的函数&#xff0c;用于创建和初始化类的对…

“数字”厨电成新宠?“传统”厨电如何凭实力年销破百亿?|厨房电器SMI社媒心智品牌榜

Social Power 核心解读 AI加持&#xff0c;数字厨电新物种持续走红 传统厨电发力社媒&#xff0c;“有范儿”实力吸睛 4月中下旬的“魔都”可谓热闹非凡&#xff0c;上海车展喧嚣未落&#xff0c;隔壁2023AWE&#xff08;中国家电及消费电子博览会&#xff09;的群雄逐鹿紧随…

Electron 小白介绍,你能看懂吗?

目录 前言一、Electron是什么1.官网介绍2.小白介绍 二、Electron开发了哪些应用三、Electron的优势在哪里1.优势2.带给我们什么优势 四、Electron如何学习1.前置知识2.学习建议 五、Electron的乐趣总结 前言 在最近的学习中&#xff0c;我接触了 Electron 这个前端框架&#x…

总结加载Shellcode的各种方式(更新中!)

1.内联汇编加载 使用内联汇编只能加载32位程序的ShellCode&#xff0c;因为64位程序不支持写内联汇编 #pragma comment(linker, "/section:.data,RWE") //将data段的内存设置成可读可写可执行 #include <Windows.h>//ShellCode部分 unsigned char buf[] &qu…

Hadoop的基础操作

Hadoop的基础操作 HDFS是Hadoop的分布式文件框架&#xff0c;它的实际目标是能够在普通的硬件上运行&#xff0c;并且能够处理大量的数据。HDFS采用主从架构&#xff0c;其中由一个NameNode和多个DataNode NameNode负责管理文件系统的命名空间和客户端的访问DataNode负责存储实…

企业为什么要做数字化转型,应该如何进行转型?

企业需要数字化转型才能在当今快速发展的商业环境中保持竞争力和相关性。数字化转型涉及利用数字技术和战略从根本上改变企业的运营方式、为客户创造价值并实现他们的目标。以下是企业进行数字化转型的一些关键原因&#xff1a; 提高运营效率&#xff1a;数字技术可实现自动化、…

如何使用ArcGIS标注上下标

&#xff08;本文首发于“水经注GIS”公号&#xff0c;关注公号免费领取地图数据&#xff09; 在某些情况下除了需要普通的标注之外还需要上下标注&#xff0c;对于这一需求&#xff0c;ArcGIS是支持的&#xff0c;这里为大家介绍一下ArcGIS标注上下标的方法&#xff0c;希望能…

初阶数据结构之栈的实现(五)

文章目录 &#x1f60f;专栏导读&#x1f916;文章导读&#x1f640;什么是栈&#xff1f;&#x1f640;画图描述 &#x1f633;栈的代码实现及其各类讲解&#x1f633;栈的初始化代码实现及其讲解&#x1f633;栈的初始化 &#x1f633;栈的销毁代码实现及其讲解&#x1f633;…

【面试题】2023vue面试题

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 1、说说你对 SPA 单页面的理解&#xff0c;它的优缺点分别是什么&#xff1f; SPA&#xf…

【运维知识进阶篇】集群架构-Nginx高可用Keepalived

高可用是指2台机器启动着完全相同的业务系统&#xff0c;一台机器宕机后&#xff0c;另一台可以快速启用&#xff0c;用户是无感知的。高可用硬件通常使用F5&#xff0c;软件通常使用keepalived。keepalived软件是基于VRRP协议实现的&#xff0c;VRRP虚拟路由冗余协议&#xff…

详解Node.js开发中不可或缺的7个库

在Node.js开发中&#xff0c;选择合适的库对于提高开发效率和优化应用程序性能至关重要。本文将介绍七个备受关注的Node.js库&#xff0c;它们在各自的领域中展现了出色的功能和性能。这些库分别是&#xff1a;Config、Fetch、Ioredis、Multer、Cache、Fast-xml-parser和Cron。…

一图看懂 pkg_resources 模块:包资源API,资料整理+笔记(大全)

本文由 大侠(AhcaoZhu)原创&#xff0c;转载请声明。 链接: https://blog.csdn.net/Ahcao2008 一图看懂 pkg_resources 模块&#xff1a;包资源API&#xff0c;资料整理笔记&#xff08;大全&#xff09; &#x1f9ca;摘要&#x1f9ca;模块图&#x1f9ca;类关系图&#x1f9…

JavaEE(系列15) -- 多线程(JUC中常见的类)

JUC----- java.util.concurrent(并发) 1. ReentrantLock 1. 可重入互斥锁. 和 synchronized 定位类似, 都是用来实现互斥效果, 保证线程安全. 2. ReentrantLock 也是可重入锁. "Reentrant" 这个单词的原意就是 "可重入". 1. ReentrantLock 的用法: lock():…

【花雕学AI】微软 Bing 图像魔法师:让你的描述变成图像,让你的图像变成现实

你有没有想过&#xff0c;如果你能够用语言来创造图像&#xff0c;那该有多么神奇和有趣&#xff1f;你有没有想过&#xff0c;如果你能够看到你想象中的图像&#xff0c;那该有多么震撼和美妙&#xff1f;现在&#xff0c;这一切都可以实现了&#xff0c;因为微软 Bing 图像魔…

NetApp EF 系列全闪存阵列——性能极佳,性价比优势突出

NetApp EF 系列全闪存阵列——性能极佳&#xff0c;性价比优势突出 如果您需要为实时分析、HPC 和数据库等性能敏感型工作负载提供强劲动力&#xff0c;NetApp EF 系列全闪存阵列的性价比优势不言自明。其可为要求最苛刻的应用程序提供微秒级响应&#xff0c;最大限度地延长正…

电源方案对比

电源 1.方案选择&#xff1a;1 LM2596 2 MP1584 3&#xff1a;TPS54301LM25962.MP1584&#xff1a;3.TPS5430 2.1输出2A电流的纹波2.2 输出3A电流的纹波3.动态响应4.发热5.电源转换效率6.综合指标reference 1.方案选择&#xff1a;1 LM2596 2 MP1584 3&#xff1a;TPS5430 1LM…

Unity之ShaderGraph节点介绍 Channel通道

目录 Channel&#xff08;通道&#xff09;  1、Combine&#xff08;合并&#xff09;  2、Flip&#xff08;反转&#xff09;  3、Split&#xff08;拆分&#xff09;  4、Swizzle&#xff08;通道调配&#xff09; Channel&#xff08;通道&#xff09; 1、Combine&am…

win可以上网,但是右下方显示“无internet链接“

使用了下面链接的方法&#xff0c;成功解决 Win10可以联网但右下角显示无法连接到Internet怎办 首先&#xff0c;打开控制面板(control)&#xff0c;右上角&#xff0c;将查看方式切换为小图标 调整计算机的设置下&#xff0c;找到并点击网络和共享中心 网络和共享中心窗口&a…