java 创建 编辑 PDF


JAVA操作PDF开源项目

以下使用可能在windows下感觉没问题,其它上服务器,就会遇到字库的问题,一直未解决,服务器没权限,放弃了,功能再好没用上就是白费呀

  1. 操作PDF的JAR主要分成两类,分别是iText系列和Apache PDFBox系列
    1.1 iText PDF :iText是著名的开放项目,是用于生成PDF文档的一个java类库。通过iText不仅可以生成PDF或rtf的文档,而且可以将XML、Html文件转化为 PDF 文件

iText 7 与iText 5是两个不同的体系。 iText 5已经暂停维护, iText 5与 iText 7都分为商业版和社区版

1.2 Apache PDFBox,Github

Apache PDFBox库是用于处理PDF文档的开源Java工具。该项目允许创建新的PDF文档,操纵现有文档以及从文档中提取内容的功能。
Apache PDFBox还提供了几个命令行实用程序。可以说Apache出品必是精品。

1.3 OpenPDF

OpenPDF是一个Java库,使用LGPL和MPL开源许可证。可以拥有创建和编辑PDF文件。
OpenPDF是iText的LGPL/MPL开源后继产品,它基于iText 4 svn标签的一个分支。

1.4 x-easypdf

x-easypdf基于pdfbox构建而来,极大降低使用门槛,以组件化的形式进行pdf的构建。
简单易用,仅需一行代码,便可完成pdf的操作。是国人开源的一个基于Apache PDFBox的JAR包。

  1. 操作PDF相关工具
    2.1 Jasper Report :是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成 PDF

JasperReport是一个强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成 PDF,HTML,或者 XML格式。该库完全由Java写成,可以用于在各种Java应用程序,包括 J2EE,Web应用程序中生成动态内容。
只需要将JasperReport引入工程中即可完成PDF报表的编译、显示、输出等工作。数据源支持更多,
常用JDBC SQL查询、XML文件、CSV文件 、HQL(Hibernate 查询),HBase,JAVA集合等。还允许你义自己的数据源,通过JASPER文件及数据源,JASPER 就能生成最终用户想要的文档格式。

  1. 2.2 Openoffice :openoffice是开源软件且能在windows和linux平台下运行,可以灵活的将word或者Excel转化为PDF文档。
  2. 生成可替换pdf
    3.1 freemarker

FreeMarker是一款模板引擎:即一种基于模板和要改变的数据,并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,
而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。

  1. Word转换为Pdf的几种方式
    4.1 利用各种Office应用进行转换

JobConverter + OpenOffice ,支持跨平台,免费 (一般格式实现效果还行,复杂格式容易有错位)
SaveAsPDFandXPS + msOfficeWord + jacob ,仅支持Windows,免费 (完美保持原doc格式,效率最慢)

  1. 4.2 利用jar进行转换

poi读取doc + Jsoup格式化Html + itext生成pdf (实现最方便,效果最差,跨平台)
jdoctopdf - Dead as of 2016-02-11 Uses Apache POI to read the Word document and iText to write the PDF
Docx4jdocx4j-export-FO ,需要依赖 plutext ,已经停止对外服务。
Spire.Doc for Java ,收费(免费的有限制)
itext-7-pdfoffice ,收费
Aspose.Words ,收费(免费的有限制)

  1. 4.3 命令行及图形工具

OfficeToPDFdocx2pdfdocs-to-pdf-converterxdocreportMS Word

JAVA 生成pdf x-easypdf

  1. 介绍

只需一行代码搞定pdf的框架,x-easypdf基于pdfbox构建而来,极大降低使用门槛,以组件化的形式进行pdf的构建。简单易用,仅需一行代码,便可完成pdf的操作。

  1. 特性
    2.1 轻量级

仅添加pdfbox相关依赖,无其他任何依赖

  1. 2.2 简单易用

仅需一行代码,便可完成pdf的操作

  1. 2.3 自动换行分页

文本超出单行显示时,即可自动换行;内容超出单页显示时,即可自动分页

  1. 2.4 模板填充

提供内置方法,可轻松实现模板填充

  1. 2.5 组件化

页面所有内容均采用组件化形式进行构建,使用不同的组件组合方式,即可构造出理想的文档

  1. 2.6 扩展灵活

只需实现系统提供的接口,即可完成自定义的组件扩展

  1. 软件架构

java 服务器开发 pdf java操作pdf的开源工具_Apache

java 服务器开发 pdf java操作pdf的开源工具_Apache_02

document(文档):PDF文档

page(页面):若干个页面组成PDF文档

watermark(水印):每个页面可设置页面级别的独立水印,也可设置文档级别的全局水印,优先级为:页面级别>文档级别

header(页眉):每个页面可设置页面级别的独立页眉,也可设置文档级别的全局页眉,优先级为:页面级别>文档级别

footer(页脚):每个页面可设置页面级别的独立页脚,也可设置文档级别的全局页脚,优先级为:页面级别>文档级别

component(组件):核心,每个页面由若干个组件构成

text(文本组件):已提供,文本写入组件
line(线条组件):已提供,线条写入组件
image(图片组件):已提供,图片写入组件
table(表格组件):已提供,表格写入组件,cell(单元格)->row(行)->table(表格)
rect(方形组件):已提供,方形写入组件
后续将添加更多其他方便实用的组件。。。
  1. 文档
    EasyPdf官网EasyPdf官方文档EasyPdf源代码
  2. 例子
@Test
    public void document() {
        // 定义输出路径
        String outputPath = "E:\\www\\java\\study\\_2020\\target\\template-demo.pdf";
        // 定义书签
        XEasyPdfTemplateBookmark bookmark = XEasyPdfTemplateHandler.Bookmark.build()
                // 设置标题
                .setTitle("目录")
                // 设置内部地址(对应组件id)
                .setInternalDestination("title");
        // 创建标题
        XEasyPdfTemplateText title = XEasyPdfTemplateHandler.Text.build()
                // 设置id
                .setId("title")
                // 设置文本
                .setText("贵阳市简介")
                // 设置字体大小
                .setFontSize("20pt")
                // 设置水平居中
                .setHorizontalStyle("center");
        // 创建扩展文本
        XEasyPdfTemplateTextExtend createText = XEasyPdfTemplateHandler.TextExtend.build()
                // 设置字体大小
                .setFontSize("12pt")
                // 设置段前空白
                .setSpaceBefore("12pt")
                // 设置段前缩进
                .setStartIndent("60pt")
                // 设置文本间隔
                .setTextSpacing("80pt")
                // 设置文本
                .addText(
                        XEasyPdfTemplateHandler.Text.build().setText("创建时间:2022-11-11 00:00:00"),
                        XEasyPdfTemplateHandler.Text.build().setText("创建人:x-easypdf")
                );

        // 创建二维码
        XEasyPdfTemplateBarcode barcode = XEasyPdfTemplateHandler.Barcode.build()
                // 设置条形码类型
                .setType("qr_code")
                // 设置条形码内容
                .setContent("https://baike.baidu.com/item/贵阳/438289")
                // 设置条形码说明文字
                .setWords("扫一扫")
                // 设置条形码图像宽度
                .setWidth("60pt")
                // 设置条形码图像高度
                .setHeight("60pt")
                // 设置水平居右
                .setHorizontalStyle("right")
                // 设置上移62pt
                .setMarginTop("-62pt");
        // 创建文本
        XEasyPdfTemplateText text = XEasyPdfTemplateHandler.Text.build()
                // 设置id
                .setId("text")
                // 设置文本
                .setText(
                        "贵阳,简称“筑”,别称林城、筑城,贵州省辖地级市、省会、Ⅰ型大城市,国务院批复确定的中国西南地区重要的中心城市之一、" +
                                "重要的区域创新中心和全国重要的生态休闲度假旅游城市。" +
                                "贵阳地处黔中山原丘陵中部,东南与黔南布依族苗族自治州的瓮安、龙里、惠水、长顺4县接壤," +
                                "西靠安顺市的平坝区和毕节市的织金县,北邻毕节市的黔西市、金沙县和遵义市的播州区," +
                                "截至2020年,全市下辖6个区、3个县,代管1个县级市。截至2021年末,贵阳市常住人口610.23万人。"
                )
                // 设置段前空白
                .setSpaceBefore("12pt")
                // 设置文本缩进
                .setTextIndent("24pt");
        // 创建文本
        XEasyPdfTemplateText remark = XEasyPdfTemplateHandler.Text.build()
                // 设置文本
                .setText("-- 摘自百度百科")
                // 设置字体大小
                .setFontSize("12pt")
                // 设置水平居右
                .setHorizontalStyle("right");
        // 创建子书签
        XEasyPdfTemplateBookmark child = XEasyPdfTemplateHandler.Bookmark.build()
                // 设置标题
                .setTitle("贵阳市行政区划")
                // 设置内部地址(对应组件id)
                .setInternalDestination("tableText");
        // 添加子书签
        bookmark.addChild(child);
        // 创建文本
        XEasyPdfTemplateText tableText = XEasyPdfTemplateHandler.Text.build()
                // 设置id
                .setId("tableText")
                // 设置文本
                .setText("贵阳市行政区划")
                // 设置字体大小
                .setFontSize("20pt")
                // 设置当前位置分页
                .setBreakBefore("page")
                // 设置段前空白
                .setSpaceBefore("20pt")
                // 设置水平居右
                .setHorizontalStyle("center");
        // 创建表格
        XEasyPdfTemplateTable table = XEasyPdfTemplateHandler.Table.build().setHeader(
                // 设置表头
                XEasyPdfTemplateHandler.Table.Header.build().addRow(
                        this.createRow("#7C7D7D", "center", "名称", "区划代码", "人口(万人)", "面积(平方千米)")
                )
        ).setBody(
                // 设置表格主体
                XEasyPdfTemplateHandler.Table.Body.build().addRow(
                        this.createRow(null, "left", "贵阳市", "520100", "599", "8035"),
                        this.createRow(null, "left", "南明区", "520102", "105", "271"),
                        this.createRow(null, "left", "云岩区", "520103", "106", "94"),
                        this.createRow(null, "left", "花溪区", "520111", "97", "964"),
                        this.createRow(null, "left", "乌当区", "520112", "34", "686"),
                        this.createRow(null, "left", "白云区", "520113", "46", "270"),
                        this.createRow(null, "left", "观山湖区", "520115", "64", "309"),
                        this.createRow(null, "right", "清镇市", "520181", "63", "1302"),
                        this.createRow(null, "right", "开阳县", "520121", "34", "2026"),
                        this.createRow(null, "right", "息烽县", "520122", "22", "1037"),
                        this.createRow(null, "right", "修文县", "520123", "29", "1076")
                )
        ).setMinRowHeight("30pt").setVerticalStyle("center");
        // 创建页面
        XEasyPdfTemplatePage page = XEasyPdfTemplateHandler.Page.build()
                // 设置页面id
                .setId("pageId")
                // 设置字体
                .setFontFamily("微软雅黑")
                // 设置字体大小
                .setFontSize("15pt")
                // 设置边距(上下左右)
                .setMargin("20pt")
                // 设置主体内容
                .addBodyComponent(title, createText, barcode, text, remark, tableText, table)
                // 设置页脚高度
                .setFooterHeight("20pt")
                // 设置页脚内容
                .addFooterComponent(
                        // 创建块容器并添加内容
                        XEasyPdfTemplateHandler.BlockContainer.build().addComponent(
                                // 创建文本
                                XEasyPdfTemplateHandler.Text.build().setText("第 "),
                                // 创建当前页码
                                XEasyPdfTemplateHandler.CurrentPageNumber.build(),
                                XEasyPdfTemplateHandler.Text.build().setText(" 页,共 "),
                                // 创建总页码并设置页面id
                                XEasyPdfTemplateHandler.TotalPageNumber.build().setPageId("pageId"),
                                XEasyPdfTemplateHandler.Text.build().setText(" 页")
                        ).setHorizontalStyle("center")
                )
                // 设置主体水印
                .setBodyWatermark(
                        // 创建文字水印
                        XEasyPdfTemplateHandler.Watermark.build()
                                // 设置水印图片目录
                                .setTempDir("E:\\www\\java\\study\\_2020\\target")
                                // 设置水印id
                                .setId("watermark")
                                // 设置水印内容
                                .setText("贵阳", "x-easypdf")
                                // 设置水印图像宽度
                                .setWidth("600pt")
                                // 设置水印图像高度
                                .setHeight("300pt")
                                // 设置水印显示宽度
                                .setShowWidth("200pt")
                                // 设置文字大小
                                .setFontSize("100pt")
                                // 设置文字颜色
                                .setFontColor("gray")
                                // 设置文字透明度
                                .setFontAlpha("100")
                                // 设置逆时针旋转
                                .setRadians("-45")
                                // 开启文件覆盖
                                .enableOverwrite()
                );
        // 添加页面、书签并转换
        XEasyPdfTemplateHandler.Document.build().addPage(page).addBookmark(bookmark).transform(outputPath);

    }

    private XEasyPdfTemplateTableRow createRow(String backgroundColor, String style, String... texts) {
        XEasyPdfTemplateTableRow row = XEasyPdfTemplateHandler.Table.Row.build();
        for (int i = 0; i < texts.length; i++) {
            row.addCell(
                    XEasyPdfTemplateHandler.Table.Cell.build().addComponent(
                            XEasyPdfTemplateHandler.Text.build().setText(texts[i])
                    ).setBorderStyle("solid").setBackgroundColor(backgroundColor).setHorizontalStyle(style)
            );
        }
        return row;
    }

生成的对应模板(格式化后):

<?xml version="1.0" encoding="UTF-8"?>
<!--根标签-->
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:fox="http://xmlgraphics.apache.org/fop/extensions">
    <!--页面模板-->
    <fo:layout-master-set>
        <fo:simple-page-master margin-bottom="20pt" margin-left="20pt" margin-right="20pt" margin-top="20pt" master-name="page1" page-height="29.7cm" page-width="21cm">
            <fo:region-body background-image="url('/E:/pdf/test/fo/watermark.png')" fox:background-image-width="200pt"/>
            <fo:region-before/>
            <fo:region-after extent="20pt"/>
            <fo:region-start/>
            <fo:region-end/>
        </fo:simple-page-master>
    </fo:layout-master-set>
    <fo:bookmark-tree>
        <fo:bookmark internal-destination="title">
            <fo:bookmark-title>目录</fo:bookmark-title>
            <fo:bookmark internal-destination="tableText">
                <fo:bookmark-title>贵阳市行政区划</fo:bookmark-title>
            </fo:bookmark>
        </fo:bookmark>
    </fo:bookmark-tree>
    <fo:page-sequence font-family="微软雅黑" font-size="15pt" id="pageId" master-reference="page1">
        <fo:static-content flow-name="xsl-region-after">
            <fo:block-container>
                <fo:block text-align="center">
                    <fo:inline>第 </fo:inline>
                    <fo:page-number/>
                    <fo:inline>页,共 </fo:inline>
                    <fo:page-number-citation-last ref-id="pageId"/>
                    <fo:inline>页</fo:inline>
                </fo:block>
            </fo:block-container>
        </fo:static-content>
        <fo:flow flow-name="xsl-region-body">
            <fo:block id="title" text-align="center">
                <fo:inline font-size="20pt">贵阳市简介</fo:inline>
            </fo:block>
            <fo:block space-before="12pt" start-indent="60pt">
                <fo:inline font-size="12pt">创建时间:2022-11-11 00:00:00</fo:inline>
                <fo:leader leader-length="80pt"/>
                <fo:inline font-size="12pt">创建人:x-easypdf</fo:inline>
            </fo:block>
            <fo:block margin-top="-62pt" text-align="right">
                <fo:instream-foreign-object>
                    <fo:barcode content="https://baike.baidu.com/item/贵阳/438289" height="60pt" type="qr_code" width="60pt" words="扫一扫" xmlns:fo="http://www.x-easypdf.cn/ns"/>
                </fo:instream-foreign-object>
            </fo:block>
            <fo:block id="text" space-before="12pt" text-indent="24pt">
                <fo:inline text-indent="24pt">贵阳,简称“筑”,别称林城、筑城,贵州省辖地级市、省会、Ⅰ型大城市,国务院批复确定的中国西南地区重要的中心城市之一、重要的区域创新中心和全国重要的生态休闲度假旅游城市。贵阳地处黔中山原丘陵中部,东南与黔南布依族苗族自治州的瓮安、龙里、惠水、长顺4县接壤,西靠安顺市的平坝区和毕节市的织金县,北邻毕节市的黔西市、金沙县和遵义市的播州区,截至2020年,全市下辖6个区、3个县,代管1个县级市。截至2021年末,贵阳市常住人口610.23万人。</fo:inline>
            </fo:block>
            <fo:block text-align="right">
                <fo:inline font-size="12pt">-- 摘自百度百科</fo:inline>
            </fo:block>
            <fo:block break-before="page" id="tableText" space-before="20pt" text-align="center">
                <fo:inline font-size="20pt">贵阳市行政区划</fo:inline>
            </fo:block>
            <fo:block>
                <fo:table display-align="center">
                    <fo:table-header>
                        <fo:table-row>
                            <fo:table-cell background-color="#7c7d7d" border-style="solid" height="30pt" text-align="center">
                                <fo:block>
                                    <fo:inline>名称</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell background-color="#7c7d7d" border-style="solid" height="30pt" text-align="center">
                                <fo:block>
                                    <fo:inline>区划代码</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell background-color="#7c7d7d" border-style="solid" height="30pt" text-align="center">
                                <fo:block>
                                    <fo:inline>人口(万人)</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell background-color="#7c7d7d" border-style="solid" height="30pt" text-align="center">
                                <fo:block>
                                    <fo:inline>面积(平方千米)</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </fo:table-header>
                    <fo:table-body>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>贵阳市</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520100</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>599</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>8035</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>南明区</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520102</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>105</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>271</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>云岩区</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520103</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>106</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>94</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>花溪区</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520111</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>97</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>964</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>乌当区</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520112</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>34</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>686</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>白云区</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520113</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>46</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>270</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>观山湖区</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>520115</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>64</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="left">
                                <fo:block>
                                    <fo:inline>309</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>清镇市</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>520181</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>63</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>1302</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>开阳县</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>520121</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>34</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>2026</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>息烽县</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>520122</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>22</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>1037</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                        <fo:table-row>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>修文县</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>520123</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>29</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                            <fo:table-cell border-style="solid" height="30pt" text-align="right">
                                <fo:block>
                                    <fo:inline>1076</fo:inline>
                                </fo:block>
                            </fo:table-cell>
                        </fo:table-row>
                    </fo:table-body>
                </fo:table>
            </fo:block>
        </fo:flow>
    </fo:page-sequence>
</fo:root>

java 服务器开发 pdf java操作pdf的开源工具_java_03

PDFBOX详解
摘要
  自从Adobe公司1993年第一次发布公共PDF参考以来,支持各种语言和平台的PDF工具和类库就如雨后春笋般涌现。然而,Java应用开发中Adobe技术的支持相对滞后了。
  自从Adobe公司1993年第一次发布公共PDF参考以来,支持各种语言和平台的PDF工具和类库就如雨后春笋般涌现。然而,Java应用开发中Adobe技术的支持相对滞后了。这是个奇怪的现象,因为PDF文档是企业信息系统存储和交换信息的大势所趋,而Java技术特别适合这种应用。然而,Java开发人员似乎直到最近才获得成熟可用的PDF支持。
  PDFBox(一个BSD许可下的源码开放项目)是一个为开发人员读取和创建PDF文档而准备的纯Java类库。它提供如下特性:

提取文本,包括Unicode字符。
和Jakarta Lucene等文本搜索引擎的整合过程十分简单。
加密/解密PDF文档。
从PDF和XFDF格式中导入或导出表单数据。
向已有PDF文档中追加内容。
将一个PDF文档切分为多个文档。
覆盖PDF文档。
  

PDFBox API
  PDFBox设计时采用面向对象的方式来描述PDF文档。PDF文档的数据是一系列基本对象的集合:数组,布尔型,字典,数字,字符串和二进制流。PDFBox在org.pdfbox.cos包(COS模型)中定义这些基本对象类型。你可以使用这些对象与PDF文档进行任何交互,但你应该先对PDF文档内部结构以及高层概念作一些深入的了解。例如,页面和字体都是带有特殊属性的字典对象;PDF参考手册提供这些特殊属性的含义和类型的说明,但这是一个枯燥的文档查阅过程。
  于是,org.pdfbox.pdfmodel包(PD模型)应运而生,它的基础是COS模型,但提供了以一种熟悉的方式访问PDF文档对象的高层API(如图1)。对底层COS模型进行了封装的PDPage和PDFont等类就在这个包中。

  注意,虽然PD模型提供了一些优秀的功能,但它依然是一个开发中的模型。在有些实例中,你可能需要借助于COS模型才能访问PDF的特定功能性。所有的PD模型对象都提供返回相应的COS模型对象的方法。所以,在一般情况下,你都会使用PD模型,但PD模型鞭长莫及时你可以直接操作底层的COS模型。
  上文对PDFBox作了大体上的介绍,现在是举一些例子的时候了。我们从如何读已存在的PDF文档开始:

PDDocument document =
PDDocument.load( "./test.pdf" );
  上面的语句解析指定的PDF文件并在内存中创建其文档对象。考虑到处理大文档时的效率问题,PDFBox只在内存中存储文档结构,图像、内嵌字体和页面内容等对象将被缓存在一个临时文件中。
  注意:PDDocument对象使用完毕时需要调用其close()方法来释放创建时使用的资源。
  

文本提取和Lucene整合
  这是一个信息展现时代(an information retrieval age),不管信息存放在哪种媒体中,应用程序都应该支持检索和索引。对信息进行组织和分类从而形成可检索的格式是很关键的。这对于文本文档和HTML文档来说是很简单的,但PDF文档包含大量的结构和元信息,提取文档内容决不是一件简单的事情。PDF语言和Postscript相似,二者中的对象都是作为矢量绘制在页面的某些位置。例如:

/Helv 12 Tf
0 13.0847 Td
(Hello World) Tj
  上面的指令将字体设为12号的Helvetica,移到下一行然后打印“Hello World”。这些命令流通常是经过压缩的,文字在屏幕上的显示顺序并不一定是文件中的字符出现顺序。因此,你有时无法直接从原始PDF文档中提取字符串。然而,PDFBox成熟的文本提取算法使得开发人员可以提取文档内容,就像在阅读器中呈现的那样。
  Lucene是Apache Jakarta项目的子项目,它是一个流行的源代码开放的搜索引擎库。开发人员可以使用Lucene来创建索引,并基于该索引对大量的文本内容进行复杂的检索。Lucene只支持文本内容的检索,所以开发人员需要将其他形式的数据转换为文本形式才能使用Lucene。例如,Microsoft Word和StarOffice文档都必须先转换为文本形式才能添加到Lucene索引中。
  PDF文件也不例外,但PDFBox提供一个特殊的整合对象,这让在Lucene索引中包含PDF文档变得非常容易。将一个基本PDF文档转换为Lucene文档只需要一条语句:

Document doc = LucenePDFDocument.getDocument( file );
  这条语句解析指定的PDF文档,提取其内容并创建一个Lucene文档对象。然后你就可以将该对象添加到Lucene索引中了。如上文所述,PDF文档中也包含作者信息和关键词等元数据,在索引PDF文档时对这些元数据进行跟踪时很重要的。表1列出了创建Lucene文档时PDFBox将填写(populate)的字段。

  这种整合使得开发人员可以轻松地使用Lucene来支持PDF文档的检索和索引。当然,有些应用程序要求更成熟的文本提取方法。此时可以直接使用PDFTextStripper类,或继承该类来满足这种复杂的需求。
  通过继承PDFTextStripper并覆盖showCharacter()方法,你可以从许多方面对文本提取进行控制。例如,使用x、y位置信息进行限制以提取特定文本块。你可以有效地忽略所有的y坐标大于某个值的文本,这样文档头部内容就会被排除。
  另一个例子。常常有这种情况:从表单创建了一组PDF文档,但这些原始数据被丢失了。也就是说,这些文档都包含一些你感兴趣的文本,而且这些文本都在相似的位置上,但填充文档的表单数据丢失了。例如,你有一些信封,在相同的位置上都有名字和地址信息。这时,你就可以使用PDFTextStripper的派生类来提取期望的字段,这个类就像一种截取屏幕区域的设备。
  

加密/解密
  PDF的一个流行特性是允许对文档内容进行加密、对访问进行控制,限制只能阅读未加密文档。PDF文档加密时采用一个主密码和一个可选的用户密码。如果设定了用户密码,那么PDF阅读器(如Acrobat)将在显示文档之前提示输入密码。而主密码则用于授权修改文档内容。
  PDF规范允许PDF文档的创建者对用户使用Acrobat阅读器查看文档时的某些操作进行限制。这些限制包括:

打印
修改内容
提取内容
  PDF文档安全的讨论不在本文范畴之内,有兴趣的读者可以参考PDF规范的相关部分。PDF文档的安全模型是可插拔式的(pluggable),你可以在加密文档时使用不同的安全处理器(security handler)。对本文而言,PDFBox支持标准的安全处理器,它是大多数PDF文档所使用的。
  加密文档时必须先指定一个安全处理器,然后使用一个主密码和用户密码进行加密。在下面的代码中,文档被加密,用户不需要敲入就可以在Acrobat中打开它(没有设置用户密码),但是该文档不可被打印。

//load the document
PDDocument pdf =
PDDocument.load( "test.pdf");
//create the encryption options
PDStandardEncryption encryptionOptions =
new PDStandardEncryption();
encryptionOptions.setCanPrint( false);
pdf.setEncryptionDictionary(
encryptionOptions );
//encrypt the document
pdf.encrypt( "master", null);
//save the encrypted document
//to the file system
pdf.save( "test-output.pdf");
 更详细的示例参见PDFBox发布版中包含的加密工具类源代码:org.pdfbox.Encrypt。
  许多应用程序可以生成PDF文档,但不支持控制文档的安全选项。这时PDFBox就可以用来在发送给用户之前截获并加密PDF文档

注意

PDF规范共有1172页之多,其实现的确是一浩大工程。同样,PDFBox发布版中说它“正在进行中”,新的功能会慢慢地添加上去。它的主要弱点是从零开始创建PDF文档。然而,有一些源码开放的Java项目可用于填补这个缺口。例如,Apache FOP项目支持从特殊的XML文档生成PDF,这个XML文档描述了要生成的PDF文档。此外,iText提供一个高层API用于创建表格和列表。
  PDFBox的下一个版本将支持新的PDF 1.5 对象流和交叉引用流。然后将提供内嵌字体和图像的支持。在PDFBox的努力下,Java应用程序中的PDF技术有望得到充分的支持。

参考资源
PDFBox: www.pdfbox.org   

Apache FOP: Apache(tm) FOP - a print formatter driven by XSL formatting objects (XSL-FO) and an output independent formatter.

iText: www.lowagie.com/iText/   

PDF Reference: http://partners.adobe.com/asn/tech/pdf/specifications.jsp   

Jakarta Lucene: http://jakarta.spache.org/lucene/

PDFBOX详解 - 走看看: http://t.zoukankan.com/yjd_hycf_space-p-7942444.html

实战

  • 实战中遇到的问题
    windows下测试的没有问题,上线后没有字体文件,要么是框框,要么是## 字符问题 pdfbox 字体问题
  • 网上找到的解决办法
  1. 在项目里建一个与源码包名一样的路径及文件FontMapperImpl.java

防止maven打包时将字体库和pdf模板文件编码打包,导致字体包和pdf模板被编译转码破坏不可用。

java 服务器开发 pdf java操作pdf的开源工具_Text_04

  1. 复写源码的FontMapperImpl.java文件

java 服务器开发 pdf java操作pdf的开源工具_java_05

方法总结:

  1. 在项目里建一个与源码包名一样的路径及文件 复写源码,打包字体,坏处 扰乱项目结构,包体积变大
  2. 重写需要修改的源码部分并重新打包,使用特殊的版本号上传至maven私服,仅供自己项目使用
  3. 推荐 在对应系统环境安装所需字体包,这种亦是最方便的。
package pdf;

import wiki.xsx.core.pdf.template.doc.XEasyPdfTemplateDocument;
import wiki.xsx.core.pdf.template.doc.component.XEasyPdfTemplateComponent;
import wiki.xsx.core.pdf.template.doc.component.block.XEasyPdfTemplateBlockContainer;
import wiki.xsx.core.pdf.template.doc.component.table.*;
import wiki.xsx.core.pdf.template.doc.component.text.XEasyPdfTemplateText;
import wiki.xsx.core.pdf.template.doc.component.text.XEasyPdfTemplateTextExtend;
import wiki.xsx.core.pdf.template.doc.page.XEasyPdfTemplatePage;
import wiki.xsx.core.pdf.template.doc.watermark.XEasyPdfTemplateWatermark;
import wiki.xsx.core.pdf.template.handler.XEasyPdfTemplateHandler;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class XeasypdfUtils {

    private final static String LEFT = "left";
    private final static String RIGHT = "right";
    private final static String CENTER = "center";

    private final static String B_FONT_SIZE = "20pt";
    private final static String S_FONT_SIZE = "12pt";
    private final static String S_MARGIN = "2pt";


    private final static String TEXT_SPACING500 = "650pt";
    private final static String TEXT_SPACING250 = "300pt";


    private final static String T_HEADER_BGCOLOR = "#D1CECE";
    private final static String T_ROW_HEIGHT = "20pt";
    private final static String BORDER_STYLE = "solid";
    private final static String FONT_FAMILY = "微软雅黑";
    private final static String FONT_WEIGHT = "600";

    public static XEasyPdfTemplateDocument createPdf(List<VerifyPlatform> verifyPlatforms) {
        
        String title = "应收对账单";
        String watermark = "北京百a";
        String row1 = "公司名称:北京百a小公司";
        String row2 = "北京百a小公司,截至     2022-08-31,与北京百a大公司产有828366元(应收合计总数)未结清款项,并产生0元(资金占用费总数)资金占用费。";
        String row3 = "制表人:哥哥";
        String row4 = "制表日期:2022/8/31(当前季度月末自然日)";
        String row6_1 = "截至2022-08-31";
        String row6_2 = "单位:元";
        String row10_1 = "法人主体盖章:";
        String row10_2 = "平创盖章签字:";
        
        // 创建页面组件
        List<XEasyPdfTemplateComponent> components = new ArrayList<>();
        components.add(createTitleText(null, title, B_FONT_SIZE, null, CENTER));
        components.add(createStandardText(null, row1, S_FONT_SIZE, S_MARGIN, LEFT));
        components.add(createStandardText(null, row2, S_FONT_SIZE, S_MARGIN, LEFT));
        components.add(createStandardText(null, row3, S_FONT_SIZE, S_MARGIN, LEFT));
        components.add(createStandardText(null, row4, S_FONT_SIZE, S_MARGIN, LEFT));
        components.add(createStandardText(null, "", S_FONT_SIZE, "14pt", LEFT));
        components.add(createExtendStandardText(null, S_FONT_SIZE, S_MARGIN, TEXT_SPACING500, 
                createStandardText(null, row6_1, S_FONT_SIZE, S_MARGIN, CENTER), 
                createStandardText(null, row6_2, S_FONT_SIZE, S_MARGIN, CENTER))
        );
        // 创建表格
        components.add(createTable(verifyPlatforms));
        components.add(createStandardText(null, "", S_FONT_SIZE, "20pt", LEFT));
        components.add(createExtendStandardText(null, S_FONT_SIZE, S_MARGIN, TEXT_SPACING250, 
                createStandardText(null, row10_1, S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 
                createStandardText(null, row10_2, S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT))
        );

        return XEasyPdfTemplateHandler.Document.build().addPage(createXEasyPdfTemplatePage("pageId", components, watermark));
    }

    /**
     * 创建页面
     *
     * @param id
     * @param components
     * @param watermark
     * @return
     */
    private static XEasyPdfTemplatePage createXEasyPdfTemplatePage(String id, List<XEasyPdfTemplateComponent> components, String watermark) {
        return XEasyPdfTemplateHandler.Page.build()
                .setId(id)
                .setFontFamily(FONT_FAMILY)
                .setFontSize(S_FONT_SIZE)
                .setMargin("20pt")
                .addBodyComponent(components)
                .setFooterHeight("-15pt")
                .addFooterComponent(createXEasyPdfTemplateBlockContainer(id))
                .setBodyWatermark(createWatermark(watermark, watermark))
                .changeLandscape();
    }

    /**
     * 创建文字水印
     *
     * @return
     */
    private static XEasyPdfTemplateWatermark createWatermark(String id, String text) {
        return XEasyPdfTemplateHandler.Watermark.build()
                .setId(id)
                .setText(text)
                .setWidth("600pt")
                .setHeight("300pt")
                .setShowWidth("200pt")
                .setFontSize("100pt")
                .setFontColor("gray")
                .setFontAlpha("100")
                .setRadians("-45")
                .enableOverwrite();
    }

    /**
     * 设置页脚内容
     *
     * @return
     */
    private static XEasyPdfTemplateBlockContainer createXEasyPdfTemplateBlockContainer(String pageId) {
        return XEasyPdfTemplateHandler.BlockContainer.build().addComponent(
                createStandardText(null, "第 ", S_FONT_SIZE, S_MARGIN, CENTER),
                XEasyPdfTemplateHandler.CurrentPageNumber.build(),
                createStandardText(null, " 页,共 ", S_FONT_SIZE, S_MARGIN, CENTER),
                XEasyPdfTemplateHandler.TotalPageNumber.build().setPageId(pageId),
                createStandardText(null, " 页", S_FONT_SIZE, S_MARGIN, CENTER)
        ).setHorizontalStyle(CENTER);
    }


    /**
     * 创建表格表体行row
     *
     * @param verifyPlatform
     * @return
     */
    private static XEasyPdfTemplateTableRow createBodyRow(VerifyPlatform verifyPlatform) {
        return XEasyPdfTemplateHandler.Table.Row.build().addCell(
                createBodyCell(createStandardText(null, verifyPlatform.getCcustomeridcode(), S_FONT_SIZE, S_MARGIN, CENTER), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getCcustomeridname(), S_FONT_SIZE, S_MARGIN, CENTER), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getReceivesum().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getReceivestandard().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getReceivejar().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getOverduesum().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getOverduestandard().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getOverduejar().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
                , createBodyCell(createStandardText(null, verifyPlatform.getOccupancymny().toString(), S_FONT_SIZE, S_MARGIN, RIGHT), null, CENTER)
        );
    }


    /**
     * 创建标题文本
     *
     * @param id
     * @param text
     * @return
     */
    private static XEasyPdfTemplateText createTitleText(String id, String text, String fontSize, String margin, String style) {
        return XEasyPdfTemplateHandler.Text.build()
                .setId(id)
                .setText(text)
                .setFontSize(fontSize)
                .setMargin(margin)
                .setHorizontalStyle(style);
    }


    /**
     * 创建普通文本
     *
     * @param id
     * @param text
     * @return
     */
    private static XEasyPdfTemplateText createStandardText(String id, String text, String fontSize, String margin, String style) {
        return XEasyPdfTemplateHandler.Text.build()
                .setId(id)
                .setText(text)
                .setFontSize(fontSize)
                .setMargin(margin)
                .setHorizontalStyle(style);
    }

    /**
     * 创建普通扩展文本
     *
     * @param id
     * @param xEasyPdfTemplateText
     * @return
     */
    private static XEasyPdfTemplateTextExtend createExtendStandardText(String id, String fontSize, String mergin, String textSpacing, XEasyPdfTemplateText... xEasyPdfTemplateText) {
        return XEasyPdfTemplateHandler.TextExtend.build()
                .setId(id)
                .setFontSize(fontSize)
                .setMargin(mergin)
                // 设置文本间隔
                .setTextSpacing(textSpacing)
                // 设置文本
                .addText(xEasyPdfTemplateText);
    }


    /**
     * 创建表格表头cell
     *
     * @param xEasyPdfTemplateText
     * @return
     */
    private static XEasyPdfTemplateTableCell createHeaderCell(XEasyPdfTemplateText xEasyPdfTemplateText, int rowSpan, int colSpan) {
        return XEasyPdfTemplateHandler.Table.Cell.build()
                .setRowSpan(rowSpan)
                .setColumnSpan(colSpan)
                .addComponent(xEasyPdfTemplateText)
                .setBorderStyle(BORDER_STYLE)
                .setBackgroundColor(T_HEADER_BGCOLOR)
                .setHorizontalStyle(CENTER);
    }

    /**
     * 创建表格表体cell
     *
     * @param xEasyPdfTemplateText
     * @return
     */
    private static XEasyPdfTemplateTableCell createBodyCell(XEasyPdfTemplateText xEasyPdfTemplateText, String backgroundColor, String style) {
        return XEasyPdfTemplateHandler.Table.Cell.build()
                .addComponent(xEasyPdfTemplateText)
                .setBackgroundColor(backgroundColor)
                .setBorderStyle(BORDER_STYLE)
                .setHorizontalStyle(style);
    }

    private static XEasyPdfTemplateTable createTable(List<VerifyPlatform> list) {
        // 设置表头
        XEasyPdfTemplateTableHeader xEasyPdfTemplateTableHeader = createXEasyPdfTemplateTableHeader();

        // 设置表格主体
        XEasyPdfTemplateTableBody xEasyPdfTemplateTableBody = createXEasyPdfTemplateTableBody(list);

        // 创建表格
        return XEasyPdfTemplateHandler.Table.build()
                .setHeader(xEasyPdfTemplateTableHeader)
                .setBody(xEasyPdfTemplateTableBody)
                .setMinRowHeight(T_ROW_HEIGHT)
                .setVerticalStyle(CENTER)
                .setBorderCollapse("collapse");
    }

    /**
     * 创建表头
     *
     * @return
     */
    private static XEasyPdfTemplateTableHeader createXEasyPdfTemplateTableHeader() {
        return XEasyPdfTemplateHandler.Table.Header.build().addRow(
                XEasyPdfTemplateHandler.Table.Row.build()
                        .addCell(
                                createHeaderCell(createStandardText(null, "客户编码", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 2, 0)
                                , createHeaderCell(createStandardText(null, "客户名称", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 2, 0)
                                , createHeaderCell(createStandardText(null, "剩余应收情况", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 3)
                                , createHeaderCell(createStandardText(null, "订单逾期情况", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 3)
                                , createHeaderCell(createStandardText(null, "资金占用费", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 2, 0)
                        )
                , XEasyPdfTemplateHandler.Table.Row.build()
                        .addCell(
                                createHeaderCell(createStandardText(null, "剩余应收合计", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 0)
                                , createHeaderCell(createStandardText(null, "标准瓶剩余应收", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 0)
                                , createHeaderCell(createStandardText(null, "千斤坛剩余应收", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 0)
                                , createHeaderCell(createStandardText(null, "订单逾期合计", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 0)
                                , createHeaderCell(createStandardText(null, "标准瓶逾期金额", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 0)
                                , createHeaderCell(createStandardText(null, "千斤坛逾期", S_FONT_SIZE, S_MARGIN, CENTER).setFontWeight(FONT_WEIGHT), 0, 0)
                        )
        );
    }

    /**
     * 创建表体
     *
     * @return
     */
    private static XEasyPdfTemplateTableBody createXEasyPdfTemplateTableBody(List<VerifyPlatform> list) {
        List<XEasyPdfTemplateTableRow> rows = new ArrayList<>();
        for (VerifyPlatform verifyPlatform : list) {
            if (BigDecimal.ZERO.compareTo(verifyPlatform.getReceivesum()) < 0) {
                rows.add(createBodyRow(verifyPlatform));
            }
        }
        return XEasyPdfTemplateHandler.Table.Body.build().addRow(rows);
    }
}
@Test
    public void document1() {
        // 定义输出路径
        String outputPath = "E:\\www\\java\\study\\_2020\\target\\template-demo2.pdf";

        // 数据源
        List<VerifyPlatform> verifyPlatforms = new ArrayList<>();
        VerifyPlatform verifyPlatform = new VerifyPlatform();
        verifyPlatform.setCcustomeridcode("00001");
        verifyPlatform.setCcustomeridname("北京百a小公司");
        verifyPlatform.setReceivesum(new BigDecimal("100"));
        verifyPlatform.setReceivestandard(new BigDecimal("80"));
        verifyPlatform.setReceivejar(new BigDecimal("20"));
        verifyPlatform.setOverduesum(new BigDecimal("212345678.90"));
        verifyPlatform.setOverduestandard(new BigDecimal("199"));
        verifyPlatform.setOverduejar(new BigDecimal("1"));
        verifyPlatform.setOccupancymny(new BigDecimal("50"));
        for (int i = 0; i < 4; i++) {
            if (i == 0) {
                VerifyPlatform verifyPlatform1 = new VerifyPlatform();
                CglibUtil.copy(verifyPlatform, verifyPlatform1);
                verifyPlatform1.setReceivesum(new BigDecimal("0"));
                verifyPlatforms.add(verifyPlatform1);
            } else {
                verifyPlatforms.add(verifyPlatform);
            }

        }
        XeasypdfUtils.createPdf(verifyPlatforms).transform(outputPath);
    }
package pdf;


import lombok.Data;


import java.math.BigDecimal;

@Data
public class VerifyPlatform {
	private static final long serialVersionUID = 1L;

	/**
	 * 客户编码
	 */
	private String ccustomeridcode;
	/**
	 * 客户名称
	 */
	private String ccustomeridname;
	/**
	 * 应收千斤坛
	 */
	private BigDecimal receivejar;
	/**
	 * 应收标准瓶
	 */
	private BigDecimal receivestandard;
	/**
	 * 应收合计
	 */
	private BigDecimal receivesum;
	/**
	 * 逾期千斤坛
	 */
	private BigDecimal overduejar;
	/**
	 * 逾期标准瓶
	 */
	private BigDecimal overduestandard;
	/**
	 * 逾期合计
	 */
	private BigDecimal overduesum;

}

java 服务器开发 pdf java操作pdf的开源工具_java 服务器开发 pdf_06