Springboot集成ItextPdf

news2024/11/20 2:25:32

目录

一、概述

二、Itext API

1.PDF文档生成

3.常用对象

一、文档对象

二、操作对象 

三、内容对象

四、表格对象

四、常用案例

一、水印

三、页眉页脚

四、合并多个PDF

五、表单PDF

六、模板PDF

一、html模板

 二、使用工具构建PDF模板

 7、HTML转PDF

8、删除页码

九、读取PDF内容

十、删除pdf内容

 十一、一张A4打印多个面单


一、概述

        因公司前段时间需要自定义面单,在原有的pdf上追加内容和编辑内容,水印,页码等需求,当时采用了itext5实现,因而最近闲下来总结一下。

        Apache iText 是一个开源 Java 库,支持 PDF 文档的开发和转换。其目前遵从AGPL开源协议,AGPL 可以说是最严格的 GPL 了,并且Itext有很多product开始收费,但所需的功能基本上开源的API都能满足。

当前使用版本:5.5.11

二、Itext API

1.PDF文档生成

构建步骤:

        创建文档对象、初始化PdfWriter、打开文档、填充内容、关闭文档。

/**
     * 创建pdf
     */
    public static void createPdf() throws Exception{
        // 1-创建文本对象 Document
        Document document = new Document(PageSize.A4, 500, 150, 50, 50);
        FileOutputStream out=new FileOutputStream("./doc/1.pdf");
        // 2-初始化 pdf输出对象 PdfWriter
        PdfWriter.getInstance(document, out);
        // 3-打开 Document
        document.open();
        // 4-往 Document 添加内容
        document.add(new Paragraph("test! PDF!!!"));
        // 5-关闭 Document
        document.close();
    }

 2.PDF内部结构

分为四层,第一层和第四层由低级操作来进行操作,第二层、第三层由高级对象操作

层对象: PdfContentByte:包含用户定位的文本和页面的图形内容的对象。

第一层操作只能使用PdfWriter.DirectContent操作,第四层使用DirectContentUnder操作。

第二层和第三层的PdfContentByte是由IText内部操作,没有提供api接口。

4、 操作:

 ⑴ PdfWriter 对象:写入PDF文档

第 1 层操作:PdfWriter. getDirectContent(),

第 2 层操作:getDirectContentUnder()。

⑵ PdfStamper 对象:将额外内容应用于PDF文档的页面。

第 1 层操作: PdfStamper. getUnderContent(1),-可以加页数

第 2 层操作: PdfStamper .getOverContent(1)。

5、作用:添加水印、背景、添加内容到绝对位置、合并PDF

3.常用对象

一、文档对象

Document、Rectangle、PageSize

1、 Document — 文档对象

构造方法: 有参构造、无参构造

无参构造:

        Document() :默认是A4大小,做左右上下外边距分别是36.

有参构造:

         Document(Rectangle pageSize, float marginLeft, float marginRight, float marginTop,

  float marginBottom)

marginLeft:左外边距  marginRight:右外边距
marginTop:上外边距  marginBottom:下外边距 

PDF文档对象跟html页面相似。

属性

Document document =new Document(); // 默认页面大小是A4
Document document =new Document(PageSize.A4); // 指定页面大小为A4
Document document =new Document(PageSize.A4,50,50,30,20); // 指定页面大小为A4,且自定义页边距(marginLeft、marginRight、marginTop、marginBottom)
其中页面大小PageSize也可自定义大小,例:new Document(new Rectangle(400, 500));

// 作者
document.addAuthor("作者");
// 创建日期
document.addCreationDate();
// 创建关键字
document.addKeywords("测试");
// 创建生产商,自动使用iText
document.addProducer();
// 创建程序
document.addCreator("创建人");
// 标题
document.addTitle("标题");
// 主题
document.addSubject("主题");

//页边空白
document.setMargins(10, 20, 30, 40);

方法

//PDF添加内容
document.add(new Paragraph("PDF添加内容"));
//添加Page
document.newPage();
//控制是否显示空白页
writer.setPageEmpty(true);
//监听器
document.addDocListener();
//添加js脚本
document.setJavaScript_onLoad(); 

//横向打印

document = new Document(PageSize.A4.rotate());

document = new Document(tRectangle.rotate());

2、PdfWriter-文档解析器

PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("./doc/11.pdf"));
//面框大小。允许的名称有:“crop”、“trim”、“art”和“bleach”。
writer.setBoxSize("crop",PageSize.A6);
writer.setPageEmpty(true);
//设置裁剪框大小
writer.setCropBoxSize(PageSize.A8);
// 设置密码为:"123" 需要根绝itext版本添加加密算法依赖:http://mvnrepository.com/artifact/com.itextpdf/itextpdf/
writer.setEncryption("密码".getBytes(), "123".getBytes(),PdfWriter.ALLOW_SCREENREADERS,PdfWriter.STANDARD_ENCRYPTION_128);
//PDF版本
writer.setPdfVersion(PdfWriter.PDF_VERSION_1_2); 

 2、 Rectangle— 页面对象

构造方法:

Rectangle(final float llx, final float lly, final float urx, final float ury)
Rectangle(PageSize.A4)
Rectangle(float urx, float ury)
//rotation旋转比例
Rectangle(float urx, float ury, int rotation)
Rectangle(Rectangle rect) 

四个参数:

        前面两个参数代表第一个点的 xy 坐标,后面两个参数代表第二个点的 xy 坐标值,Itext 将以这两个点作为对角点来创建一个矩形。

方法:

 //旋转比例
tRectangle.setRotation();
//背景
tRectangle.setBackgroundColor();
//边框
tRectangle.setBorder();
//边框背景
tRectangle.setBorderColor();
//边框宽度
tRectangle.setBorderWidth();
tRectangle.setTop();
tRectangle.setLeft();
tRectangle.setRight();
tRectangle.setBottom();

public static void getRectangle() throws Exception {
    // 1-创建一个pdf文档,document
    Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
    // 2-Rectangle(pdf页面)创建Document
    // 一般是四个参数表示:左下角的坐标和右上角的坐标
    Rectangle tRectangle = PageSize.A4;// PageSize封装了大量常用的Rectangle数据
    tRectangle = new Rectangle(800, 600);// 长宽
    tRectangle = new Rectangle(0, 0, 800, 600);// 等于上面

    //其他页面属性:不能和PageSize封装的静态一起使用
    tRectangle.setBackgroundColor(BaseColor.BLACK);// 背景色
    tRectangle.setBorder(1220);// 边框
    tRectangle.setBorderColor(BaseColor.BLUE);
    tRectangle.setBorderWidth(244.2f);
    document = new Document(tRectangle);
    // 解析器
    PdfWriter.getInstance(document, new FileOutputStream("./doc/3.pdf"));
    document.open();
    document.newPage();
    document.add(new Paragraph("New page"));
    document.close();

}

二、操作对象 

PdfWriter、PdfStamper、PdfReader、PdfCopy

三、内容对象

1.字体对象

1)BaseFont-确认支持中文

<!-- 字体 -->
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itext-asian</artifactId>
  <version>5.2.0</version>
</dependency>

2)Font-字体的设置,如颜色,字体,大小等

方式一:使用Windows系统字体(TrueType)
        BaseFont baseFont = BaseFont.createFont("C:/Windows/Fonts/SIMYOU.TTF", BaseFont.IDENTITY_H,
                BaseFont.NOT_EMBEDDED);
方式二:使用资源字体(ClassPath)
       BaseFont baseFont = BaseFont.createFont("/SIMYOU.TTF", BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
方式三:使用iTextAsian.jar中的字体
        BaseFont baseFont = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

Font font = new Font(baseFont);
// 设置字体大小
font.setSize(13);
// 设置字体颜色
font.setColor(new BaseColor(255, 0, 0));
// 设置类型,为正常
font.setStyle(Font.NORMAL);
// 设置类型,加粗
font.setStyle(Font.BOLD);
// 设置类型,倾斜
font.setStyle(Font.ITALIC);
// 设置类型,下划线
font.setStyle(Font.UNDERLINE);
// 设置类型,可组合,倾斜+删除线
font.setStyle(Font.ITALIC | Font.STRIKETHRU);

public static Font getChineseFont() {
    BaseFont bfChinese;
    Font fontChinese = null;
    try {
        //支持中文
        bfChinese = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        fontChinese = new Font(bfChinese, 12, Font.NORMAL, BaseColor.BLUE);
    } catch (DocumentException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    return fontChinese;

}

2.Element - 元素

        内容对象基本都实现这个接口。如Chunk、 Phrase、 Paragraph

        一般用于内容居中定位。

ALIGN_LEFT, ALIGN_CENTER、 ALIGN_RIGHT, ALIGN_JUSTIFIED 。

如设置居中对齐:setAlignment(Element.ALIGN_CENTER)

3.Chunk - 块对象

     能被添加到文档的文本的最小单位,块可以用于构建其他基础元素如短句、段落、锚点等,块是一个有确定字体的字符串,要添加块到文档中时,其他所有布局变量均要被定义。它有字体、大小、颜色、粗体,背景色、下划线,删除线等属性。

Chunk.NEWLINE-换行,
setUnderline(0.2f, -2f)- 下划线
setTextRise(6)-上浮

public static void testChunk() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter.getInstance(document, new FileOutputStream("./doc/4.pdf"));
        document.open();
        document.newPage();
        String[] contries = new String[] {"美国", "英甲", "中国", "朝鲜", "日本"};
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            Chunk chunk = new Chunk(contry, getChineseFont());
            //字间隔
//            chunk.setWordSpacing(10f);
            //行间距
//            chunk.setLineHeight(20f);
            document.add(chunk);
            //块之间设置间隔
            document.add(new Chunk(" "));
            Font font = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", 6, Font.BOLD, BaseColor.WHITE);
            Chunk id = new Chunk(index + "", font);
            // 设置块的背景色
            id.setBackground(BaseColor.BLACK, 1f, 0.5f, 1f, 1.5f);
            // 设置上标,其中参数表示,离开基线的距离,如果设置负数就表示设置下标
            id.setTextRise(6);
            document.add(id);
            //块之间设置间隔
            document.add(new Chunk("  "));
            // 换行 需要设置行间距 不然上移 覆盖
//            document.add(Chunk.NEWLINE);
        }
        document.close();
    }

3.Phrase - 短语对象

        短句(Phrase)是一系列以特定间距(两行之间的距离)作为参数的块,一个短句有一个主字体,但短句中的一些块具有不同于主字体的字体,你有更多的选择去创建短句。简单来说,就是由多个同一行间距块组成。

默认情况下,行间距是1.5倍字体大小,可以不用设置行间距,就比较好的显示文档。

 add(Element)-添加方法
add(Chunk.NEWLINE)-内部换行
setLeading(14f)-行间距

public static void testPhrase() throws Exception {
    // 1-创建一个pdf文档,document
    Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
    // 解析器
    PdfWriter.getInstance(document, new FileOutputStream("./doc/5.pdf"));
    document.open();
    document.newPage();
    String[] contries = new String[] {"美国", "英甲", "中国", "朝鲜", "日本"};
    Font BOLD_UNDERLINED = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", 12, Font.BOLD | Font.UNDERLINE);
    Font NORMAL = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", 12);
    for (int index = 1; index <= contries.length; index++) {
        String contry = contries[index - 1];
        Phrase director = new Phrase();
        director.add(new Chunk(contry, BOLD_UNDERLINED));
        director.add(new Chunk(",", BOLD_UNDERLINED));
        director.add(new Chunk(" ", NORMAL));
        director.add(new Chunk(contry, NORMAL));
        //设置行间距
        director.setLeading(66f);
        document.add(director);
        //内部换行
        document.add(Chunk.NEWLINE);
    }
    document.close();
}

4.Paragraph

        段落是一系列块和(或)短句。同短句一样,段落有确定的间距。用户还可以指定缩排;在边和(或)右边保留一定空白,段落可以左对齐、右对齐和居中对齐。添加到文档中的每一个段落将自动另起一行。

        说明:一个段落有一个且仅有一个间距,如果你添加了一个不同字体的短句或块,原来的间距仍然有效,你可以通过setLeading来改变间距,但是段落中所有内容将使用新的中的间距。

add(Element)-添加;

setLeading(20f)-行间距,一个Paragraph只有一个行间距;
setIndentationLeft()-左缩进,

setIndentationRight-右缩进,

setFirstLineIndent-首行缩进;
setSpacingBefore-设置上空白,

setSpacingAfter(10f)-设置段落下空;
setAlignment(Element.ALIGN_CENTER)-居中对齐;

//直线
Paragraph p1 =new Paragraph();
p1.add(new Chunk(new LineSeparator()));
doc.add(p1);

//点线
Paragraph p2 =new Paragraph();
p2.add(new Chunk(new DottedLineSeparator()));

//下滑线
LineSeparator UNDERLINE = new LineSeparator(1, 100, null, Element.ALIGN_CENTER, -2);
Paragraph p3 = new Paragraph("NNNNNNNNNNN");
p3.add(UNDERLINE);
document.add(p3);

public static void testParagraph() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter.getInstance(document, new FileOutputStream("./doc/6.pdf"));
        document.open();
        document.newPage();
        String[] contries = new String[] {"美国", "英甲", "中国", "朝鲜", "日本"};
        Font BOLD_UNDERLINED = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", 12, Font.BOLD | Font.UNDERLINE);
        Font NORMAL = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", 12);
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            Paragraph p = new Paragraph();
            p.add(new Chunk("年代: ", BOLD_UNDERLINED));
            p.add(new Phrase("标题: ", NORMAL));
            p.add(new Phrase(contry, NORMAL));
            // 设置行间距
            p.setLeading(20f);
            document.add(p);
            // 内部换行
            document.add(Chunk.NEWLINE);
        }
        Paragraph paragraph = new Paragraph("这是一个缩进演示:段落是一系列块和(或)短句。同短句一样,段落有确定的间距。用户还可以指定缩排;"
            + "在边和(或)右边保留一定空白,段落可以左对齐、右对齐和居中对齐。添加到文档中的每一个段落将自动另起一行。说明:一个段落有一个且仅有一个间距,"
            + "如果你添加了一个不同字体的短句或块,原来的间距仍然有效,你可以通过SetLeading来改变间距,但是段落中所有内容将使用新的中的间距。更改分割符 通常," +
                "当文本不能放在一行时,文本将被分割成不同的部分,iText首先会查找分割符,如果没有找到,文本将在行尾被截断。有一些预定的分割符如“ ”空格和“-”连字符," +
                "但是你可以使用setSplitCharacter方法来覆盖这些默认值。",
            NORMAL);
        // 默认情况下,文本的对齐方式为左对齐
        // paragraph.setAlignment(Element.ALIGN_JUSTIFIED);
        // 首行缩进(FirstLineIndent),左边缩进(indentationLeft),右边缩进(IndentationRight)
        paragraph.setFirstLineIndent(10f);
        paragraph.setIndentationLeft(10f);
        paragraph.setIndentationLeft(12f);
        document.add(paragraph);
        document.close();
    }

5、列表(List)

        列表就是一个有顺序的段落对象集合。

public static void testList() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter.getInstance(document, new FileOutputStream("./doc/7.pdf"));
        document.open();
        document.newPage();
        String[] contries = new String[] {"美国", "英甲", "中国", "朝鲜", "日本"};
        Font NORMAL = FontFactory.getFont("STSong-Light", "UniGB-UCS2-H", 12);

        document.add(new Chunk("默认列表演示1:", NORMAL));
        document.add(Chunk.NEWLINE);

        List list = new com.itextpdf.text.List();
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list.add(new ListItem(contry, NORMAL));
        }
        document.add(list);
        document.add(Chunk.NEWLINE);

        document.add(new Chunk("不显示数字演示2:", NORMAL));
        document.add(Chunk.NEWLINE);

        // 编号
        list = new List(false);
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list.add(new ListItem(contry, NORMAL));
        }
        document.add(list);
        document.add(Chunk.NEWLINE);

        document.add(new Chunk("使用#作为列表符号3:", NORMAL));
        document.add(Chunk.NEWLINE);

        list = new List();
        // 设置编号
        list.setListSymbol("#");
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list.add(new ListItem(contry, NORMAL));
        }
        document.add(list);
        // 换行
        document.add(Chunk.NEWLINE);

        document.add(new Chunk("显示数字演示4:", NORMAL));
        document.add(Chunk.NEWLINE);
        list = new List(true);
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list.add(new ListItem(contry, NORMAL));
        }
        document.add(list);
        document.add(Chunk.NEWLINE);
        document.add(new Chunk("罗马数字列表演示5:", NORMAL));
        document.add(Chunk.NEWLINE);

        List list1 = new RomanList();
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list1.add(new ListItem(contry, NORMAL));
        }
        document.add(list1);

        document.add(Chunk.NEWLINE);
        document.add(new Chunk("希腊字母列表演示6:", NORMAL));
        document.add(Chunk.NEWLINE);

        List list2 = new GreekList();
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list2.add(new ListItem(contry, NORMAL));
        }
        document.add(list2);
        document.add(Chunk.NEWLINE);


        document.add(new Chunk("ZapfDingbatsNumberList演示7:", NORMAL));
        document.add(Chunk.NEWLINE);

        List list3 = new ZapfDingbatsNumberList(10);
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list3.add(new ListItem(contry, NORMAL));
        }
        document.add(list3);

        document.add(Chunk.NEWLINE);
        document.add(new Chunk("ZapfDingbatsList演示8:", NORMAL));
        document.add(Chunk.NEWLINE);

        List list4 = new ZapfDingbatsList(43, 30);
        for (int index = 1; index <= contries.length; index++) {
            String contry = contries[index - 1];
            list4.add(new ListItem(contry, NORMAL));
        }
        document.add(list4);

        document.add(Chunk.NEWLINE);
        document.add(new Chunk("列表嵌套演示9:", NORMAL));
        document.add(Chunk.NEWLINE);

        List rootList = new List(List.UNORDERED);
        rootList.add(new ListItem("Item 1"));
        // 子列表
        List sublist = new List(true, false, 30);
        sublist.setListSymbol(new Chunk("", FontFactory.getFont(FontFactory.HELVETICA, 6)));
        sublist.add("A");
        sublist.add("B");
        rootList.add(sublist);
        rootList.add(new ListItem("Item 2"));
        // 子列表
        sublist = new List(true, false, 30);
        sublist.setListSymbol(new Chunk("", FontFactory.getFont(FontFactory.HELVETICA, 6)));
        sublist.add("C");
        sublist.add("D");
        rootList.add(sublist);
        document.add(rootList);

        document.close();
    }

参考来源:
一步一步 IText.Sharp Chunk Phrase Paragraph List使用 - cdboy - 博客园上面两篇介绍了PDF文档的创建和中文支持设置方法,下面对文档经常使用的对象时行介绍:块(Chunk)、短句(Phrase)、段落(Paragraph)、列表(List)文档中的对象UML图,如下:一、https://www.cnblogs.com/LifelongLearning/archive/2011/03/30/2000072.html

6.Image - 图像,抽象类 继承了Rectangle。 

Image img = Image.getInstance("./doc/a.png");

setAlignment(Image.LEFT)-对齐方式
setBorder(Image.BOX)-边框
setBorderWidth(10)-边框宽度
setBorderColor(BaseColor.WHITE)-边框颜色,  
scaleToFit(1000, 72)-大小
setRotation(-20);//旋转 弧度
setRotationDegrees(-30);// 旋转 角度
scalePercent(50);//依照比例缩放
setAbsolutePosition()-绝对位置

 public static void testImage() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter.getInstance(document, new FileOutputStream("./doc/9.pdf"));
        document.open();
        document.newPage();
        // 图片Image对象
        Image img = Image.getInstance("./doc/a.jpeg");
        img.setAlignment(Image.LEFT);
        img.setBorder(Image.BOX);
        img.setBorderWidth(10);
        img.setBorderColor(BaseColor.WHITE);
        img.scaleToFit(800, 72);// 大小
        img.setRotation(-20);//旋转 弧度
        img.setRotationDegrees(-30);// 旋转 角度
        img.scalePercent(30);//依照比例缩放
        document.add(img);
        document.close();
    }

7、Anchor(锚点、超链接)

//超链接
Anchor anchor =new Anchor("this is anchor");

//定位 点击后,跳到topline的位置
Anchor gotop =new Anchor("go top");
gotop.setReference("#us");

public static void testAnchor() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter.getInstance(document, new FileOutputStream("./doc/10.pdf"));
        document.open();
        document.newPage();
        // Anchor超链接和锚点对象: internal and external links  
        Paragraph country = new Paragraph();
        Anchor dest = new Anchor("我是锚点,也是超链接", getChineseFont());
        dest.setName("CN"); // 设置锚点的名字  
        dest.setReference("http://www.baidu.com");// 连接  
        country.add(dest);
        country.add(String.format(": %d sites", 10000));
        document.add(country);
        document.close();
    }

8、 Chapter、Section(大纲)

public static void testChapterAndSection() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter.getInstance(document, new FileOutputStream("./doc/11.pdf"));
        document.open();
        Paragraph title = new Paragraph("一级标题", getChineseFont());
        Chapter chapter = new Chapter(title, 1);

        Paragraph title2 = new Paragraph("二级标题-1", getChineseFont());
        Section section = chapter.addSection(title2);
        section.setBookmarkTitle("sectionName");// 左边目录显示的名字,不写就默认名
        section.setIndentation(30);
        section.setIndentationLeft(5);
        section.setBookmarkOpen(false);
        section.setNumberStyle(Section.NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT);

        Section section2 = chapter.addSection(new Paragraph("二级标题-2", getChineseFont()));
        section2.setIndentation(30);
        section2.setIndentationLeft(5);
        section2.setBookmarkOpen(false);
        section2.setNumberStyle(Section.NUMBERSTYLE_DOTTED_WITHOUT_FINAL_DOT);

        Section subsection = section.addSection(new Paragraph("三级标题-1", getChineseFont()));
        subsection.setIndentationLeft(10);
        // subsection.setNumberDepth(1);
        subsection.setNumberStyle(Section.NUMBERSTYLE_DOTTED);

        Section subsection2 = section2.addSection(new Paragraph("三级标题-2", getChineseFont()));
        subsection2.setIndentationLeft(10);
        subsection2.setNumberStyle(Section.NUMBERSTYLE_DOTTED);
        document.add(chapter);

        document.close();
    }

9、PdfOutline - 目录 / 书签 更每一页相关

PdfOutline(PdfOutline parent, PdfAction action, String title, boolean open)

parent:根目录

PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("./doc/11.pdf"));
PdfContentByte cb = writer.getDirectContent();

//获取外部根目录
PdfOutline root = cb.getRootOutline();

PdfAction:pdf点击事件,title:标题,open:是否打开

public static void testPdfOutline() throws Exception {
        // 1-创建一个pdf文档,document
        Document document = new Document();// 默认PageSize.A4, 36, 36, 36, 36
        // 解析器
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("./doc/11.pdf"));
        document.open();
        
        document.add(new Chunk("Chapter 1").setLocalDestination("1"));
        document.newPage();

        document.add(new Chunk("Chapter 2").setLocalDestination("2"));
        document.add(new Paragraph(new Chunk("Sub 2.1").setLocalDestination("2.1")));
        document.add(new Paragraph(new Chunk("Sub 2.2").setLocalDestination("2.2")));

        document.newPage();

        document.add(new Chunk("Chapter 3").setLocalDestination("3"));

        //内容对象
        PdfContentByte cb = writer.getDirectContent();
        //获取外部根目录
        PdfOutline root = cb.getRootOutline();
        //一级目录
        new PdfOutline(root, PdfAction.gotoLocalPage("1", false), "Chapter 1");
        //一级目录
        PdfOutline oline2 = new PdfOutline(root, PdfAction.gotoLocalPage("2", false), "Chapter 2");
        //是否打开
        oline2.setOpen(false);
        //添加二级子目录
        new PdfOutline(oline2, PdfAction.gotoLocalPage("2.1", false), "Sub 2.1");
        new PdfOutline(oline2, PdfAction.gotoLocalPage("2.2", false), "Sub 2.2");
        //添加三级目录
        new PdfOutline(root, PdfAction.gotoLocalPage("3", false), "Chapter 3");
        
        document.close();
    }

10、Header, Footer

public static void testHeadFooter() throws Exception{
        Document doc = new Document();
        PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream("./doc/12.pdf"));
        writer.setPageEvent(new PdfPageEventHelper() {
            public void onEndPage(PdfWriter writer, Document document) {
                PdfContentByte cb = writer.getDirectContent();
                cb.saveState();
                cb.beginText();
                BaseFont bf = null;
                try {
                    bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                cb.setFontAndSize(bf, 10);
                float x = document.top(-20);
                //左
                cb.showTextAligned(PdfContentByte.ALIGN_LEFT,"顶部-左",document.left(), x, 0);
                //中
                cb.showTextAligned(PdfContentByte.ALIGN_CENTER,writer.getPageNumber()+ " page",(document.right() + document.left())/2,x, 0);
                //右
                cb.showTextAligned(PdfContentByte.ALIGN_RIGHT,"顶部-右",document.right(), x, 0);
                //Footer
                float y = document.bottom(-20);
                //左
                cb.showTextAligned(PdfContentByte.ALIGN_LEFT,"底部-左",document.left(), y, 0);
                //中
                cb.showTextAligned(PdfContentByte.ALIGN_CENTER,writer.getPageNumber()+" page",(document.right() + document.left())/2, y, 0);
                //右
                cb.showTextAligned(PdfContentByte.ALIGN_RIGHT,"底部-右",document.right(), y, 0);
                cb.endText();
                cb.restoreState();
            }
        });
        doc.open();
        doc.add(new Paragraph("1 page"));
        doc.close();
    }

四、表格对象

PdfPTable、PdfPCell

1、构造方法:

//列数
PdfPTable datatable = new PdfPTable(6);
//每个单元格宽度
PdfPTable datatable = new PdfPTable(new float[]{1,2,3})
//表格嵌套
PdfPTable rootTable=new PdfPTable(new PdfPTable(6));

PdfPCell cell= new PdfPCell(new Paragraph(“表格”, 中文支持)
//默认单元格
PdfPCell cell1=new PdfPCell();
//单元格放入table
PdfPCell cell2=new PdfPCell(table,new PdfPCell())
//单元格放入图片
PdfPCell cell3=new PdfPCell(Image image);

2、结构:

PdfPTable[PdfPTable[PdfPCell[Paragraph]]]
PdfPTable[PdfPTable[PdfPCell[PdfPTable,PdfPCell]]]

3、方法:

PdfPTable:

 //设置表格上面空白行
setSpacingBefore(15f);
//设置表格下面空白行
setSpacingAfter(40f);
//第一行作为标题. 定义为标题的行应该保留在新页面上.
setHeaderRows(1);
// 设置列的宽度 百分比
setWidths(cellsWidth);
//表格的总宽度
setTotalWidth(300f);
// 表格的宽度百分比
setWidthPercentage(100);
//得到默认单元格
getDefaultCell()
添加单元格
addCell()

//写入绝对位置
pdfPTable.writeSelectedRows(0,-1, 0, -1, 100, 200, tContent);

PdfPCell:
//背景色
setBackgroundColor(BaseColor.CYAN)
//最小高度
2)setMinimumHeight(30f)
//固定高度。表格的高度通过单元格高度完成
setFixedHeight(40f)
//无边框
setBorder(Rectangle.NO_BORDER)
//无边框。不设,默认是有边框的
setBorderWidth(0)
setBorderWidthBottom(Rectangle.NO_BORDER);
setBorderWidthRight(Rectangle.NO_BORDER);
setBorderWidthTop(Rectangle.NO_BORDER);
setBorderWidthLeft(Rectangle.NO_BORDER);
//边框颜色
setBorderColor(new BaseColor(255, 0, 0))
//水平居中
setHorizontalAlignment(Element.ALIGN_CENTER)
//垂直居中。设置单元格内容的显示
setVerticalAlignment(Element.ALIGN_MIDDLE)
//跨2行
setRowspan(2)
//跨2列
setColspan(2)

public static void insertTableAndPdfPCell() throws Exception {

        Document document = new Document(PageSize.A4, 50, 50, 50, 50);
        // 使用PDFWriter进行写文件操作
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("./doc/13.pdf"));
        document.open();

        // 中文字体
        Font fontChinese = getChineseFont();

        Paragraph titlle=new Paragraph("测试表格",fontChinese);
        //中间
        titlle.setAlignment(Element.ALIGN_CENTER);
        document.add(titlle);


        PdfPTable pTable=new PdfPTable(2);


        PdfPCell pdfPCell=new PdfPCell(new Paragraph("编号:"+"111111111",fontChinese));
        pdfPCell.setBorder(Rectangle.NO_BORDER);
        pTable.addCell(pdfPCell);

        pdfPCell=new PdfPCell(new Paragraph("名称"+"aaaa",fontChinese));
        pdfPCell.setBorder(Rectangle.NO_BORDER);
        pTable.addCell(pdfPCell);

        float[] widths={1f,1f}; //设置列的宽度 百分比

        pTable.setWidths(widths);
        pTable.setSpacingBefore(15f);
        document.add(pTable);




        //设置表头
        String[] tableHeader=new String[6];
        String[] tableCont=new String[6];

        int colNumber = 6;
        for (int i = 0; i < 6; i++) {
            tableHeader[i]="表头"+(i+1);
            tableCont[i]="内容"+(i+1);
        }
        // 创建有6列的表格 可以设置重复表头
        PdfPTable datatable = new PdfPTable(colNumber);
        //设置表格上面空白行
        datatable.setSpacingBefore(15f);
        //每个单元格宽度
//        PdfPTable datatable = new PdfPTable(new float[]{1, 1, 1, 1, 1, 1})
        //第一行作为标题. 定义为标题的行应该保留在新页面上.
        datatable.setHeaderRows(1);
        // 定义表格的宽度
        int[] cellsWidth = {1, 1, 1, 1, 1, 1};
        // 设置列的宽度 百分比
        datatable.setWidths(cellsWidth);
        //表格的总宽度
        // datatable.setTotalWidth(300f);
        // 表格的宽度百分比
        datatable.setWidthPercentage(100);
        // 单元格的间隔
        datatable.getDefaultCell().setPadding(2);
        // 边框宽度
        datatable.getDefaultCell().setBorderWidth(2);
        // 设置表格的底色
        datatable.getDefaultCell().setBackgroundColor(BaseColor.GREEN);
        datatable.getDefaultCell().setHorizontalAlignment(Element.ALIGN_CENTER);

        // 添加表头元素
        for (int i = 0; i < colNumber; i++) {
            datatable.addCell(new Paragraph(tableHeader[i], fontChinese));
        }

        // 添加表格的内容
        for (int i = 0; i < colNumber; i++) {
            datatable.addCell(new Paragraph(tableCont[i], fontChinese));
        }

        // 空白表格
        for (int i = 0; i < colNumber; i++) {
            PdfPCell cell = new PdfPCell(new Paragraph(""));
            // 单元格高度
            cell.setFixedHeight(20);
            datatable.addCell(cell);
        }
        // 设置表格下面空白行
        datatable.setSpacingAfter(40f);
        // 把表格加入文档
        document.add(datatable);

        // 跨行跨列表格
        PdfPTable table = new PdfPTable(3);
        // 3列表格
        PdfPCell cell; // 单元格
        cell = new PdfPCell(new Phrase("跨三列", getChineseFont()));
        // 跨3列
        cell.setColspan(3);
        table.addCell(cell);

        cell = new PdfPCell(new Phrase("跨二行", getChineseFont()));
        // 跨2行
        cell.setRowspan(2);
        table.addCell(cell);
        table.addCell(new PdfPCell(new Phrase("第一行,第一列", getChineseFont())));
        table.addCell(new PdfPCell(new Phrase("第一行,第二列", getChineseFont())));
        table.addCell(new PdfPCell(new Phrase("第二行,第一列", getChineseFont())));
        table.addCell(new PdfPCell(new Phrase("第二行,第二列", getChineseFont())));

        document.add(table);

        // 表格的嵌套 4列
        PdfPTable tableFather = new PdfPTable(4);
        //设置表格上面空白行
        tableFather.setSpacingBefore(20f);
        // 1行2列
        PdfPTable nested1 = new PdfPTable(2);
        //设置无边框
        nested1.getDefaultCell().setBorderWidthBottom(Rectangle.NO_BORDER);
        nested1.getDefaultCell().setBorderWidthRight(Rectangle.NO_BORDER);
        nested1.getDefaultCell().setBorderWidthTop(Rectangle.NO_BORDER);
        nested1.getDefaultCell().setBorderWidthLeft(Rectangle.NO_BORDER);
        nested1.addCell("1.1");
        nested1.getDefaultCell().setBorderWidthLeft(1);
        nested1.addCell("1.2");

        // 2行1列
        PdfPTable nested2 = new PdfPTable(1);
        nested2.getDefaultCell().setBorderWidthBottom(Rectangle.NO_BORDER);
        nested2.getDefaultCell().setBorderWidthRight(Rectangle.NO_BORDER);
        nested2.getDefaultCell().setBorderWidthTop(Rectangle.NO_BORDER);
        nested2.getDefaultCell().setBorderWidthLeft(Rectangle.NO_BORDER);
        nested2.addCell("2.1");
        nested2.getDefaultCell().setBorderWidthTop(1);
        nested2.addCell("2.2");

        // 将表格插入到指定位置
        for (int i = 0; i < 12; i++) {
            switch (i){
                case 1:
                    tableFather.addCell(nested1);
                    break;
                case 6:
                    tableFather.addCell(nested2);
                    break;
                default:
                    tableFather.addCell("cell " + i);
                    break;
            }

        }

        // 设置表格下面空白行
        tableFather.setSpacingAfter(40f);
        // 把表格加入文档
        document.add(tableFather);


        //表格嵌套
        PdfPTable rootTable=new PdfPTable(tableFather);
        document.add(rootTable);


        PdfPTable pdfPTable=new PdfPTable(1);
        pdfPTable.setTotalWidth(300f);
        //得到层
        PdfContentByte tContent = writer.getDirectContent();


        PdfPCell pdfPCell1=new PdfPCell(new Paragraph("编号:"+"fsddd",fontChinese));
        pdfPTable.addCell(pdfPCell1);

        //写入绝对位置
        pdfPTable.writeSelectedRows(0,-1, 0, -1, 100, 200, tContent);
        pTable.setSpacingBefore(15f);
        document.add(pdfPTable);

        document.close();
    }

四、常用案例

        PDF分为四层,第一层和第四层由低级操作来进行操作,第二层、第三层由高级对象操作(从下往上) 

操作对象:PdfWriter、PdfStamper

PdfWriter writer = PdfWriter.getInstance(document, out);
//在内容下方
PdfContentByte under = writer.getDirectContentUnder();
//在内容上方
under = writer.getDirectContent();

PdfStamper stamp = new PdfStamper(reader, out);
//在内容下方添加
PdfContentByte under = stamp.getUnderContent(1);// 拿到层,页数
//在内容上方添加
PdfContentByte over = stamp.getOverContent(1);// 拿到层

一、水印

1.PdfWriter

public static void testShuiyin() throws Exception {
        FileOutputStream out = new FileOutputStream("./doc/13.pdf");
        Document document = new Document(PageSize.A4);
        PdfWriter writer = PdfWriter.getInstance(document, out);
        JLabel label = new JLabel();
        int textH = 0;
        int textW = 0;
        int interval = -5;
        String waterMarkName="测试水印";   //需要添加的水印文字
        label.setText(waterMarkName);
        FontMetrics  metrics = label.getFontMetrics(label.getFont());
        textH = metrics.getHeight();  //字符串的高,   只和字体有关
        textW = metrics.stringWidth(label.getText());  //字符串的宽
        float opacity=0.1f;//水印字体透明度
        int fontsize=12;  //水印字体大小
        int angle=30;   //水印倾斜角度(0-360)
        int heightRatio=2; //数值越大每页竖向水印越少
        int widthRatio=2;   //数值越大每页横向水印越少
        // 设置水印透明度
        PdfGState gs = new PdfGState();
        //这里是透明度设置
        gs.setFillOpacity(opacity);
        //这里是条纹不透明度
        gs.setStrokeOpacity(0.1f);
        document.open();
        // 在内容下方
        PdfContentByte under = writer.getDirectContentUnder();
        // 在内容上方
        // under = writer.getDirectContent();
        under.beginText();
        under.setGState(gs);
        BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.EMBEDDED);
        under.setFontAndSize(bf, fontsize);
        Rectangle rectangle = document.getPageSize();
        // under.setTextMatrix(30, 30);
        // 水印文字成45度角倾斜
        for (int height =  interval + textH; height < rectangle.getHeight()*2; height = height + textH * heightRatio) {
            for (int width =  interval + textW; width < rectangle.getWidth()*1.5 + textW; width = width + textW * widthRatio) {
                // rotation:倾斜角度
                under.showTextAligned(Element.ALIGN_LEFT, waterMarkName, width - textW, height - textH, angle);
            }
        }
        under.endText();
        document.add(new Paragraph("测试",getChineseFont()));
        document.close();
    }

 2.PdfStamper

public static void testShuiyin1() throws Exception {
        FileOutputStream out = new FileOutputStream("./doc/14.pdf");
        // 读取器
        PdfReader reader = new PdfReader("./doc/12.pdf");
        // 解析器与输出
        PdfStamper stamp = new PdfStamper(reader, out);
        // 图片水印
        Image img = Image.getInstance("./doc/aaa.jpg");
        img.setAbsolutePosition(100, 100);// 位置
        // 在内容下方添加
        PdfContentByte under = stamp.getOverContent(1);// 拿到层,页数
        under.addImage(img);
        // 文字水印
        // 在内容上方添加
        PdfContentByte over = stamp.getOverContent(1);// 拿到层
        over.beginText();
        BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        over.setFontAndSize(bf, 18);
        over.setTextMatrix(30, 30);
        over.showTextAligned(Element.ALIGN_LEFT, "文字水印", 230, 430, 45);
        over.endText();
        // 背景图
        Image img2 = Image.getInstance("./doc/a.jpeg");
        //水印与背景的区别:背景只需要把绝对置为从 文档左下角开始。即设置setAbsolutePosition(0, 0)
        img2.setAbsolutePosition(0, 0);
        PdfContentByte under2 = stamp.getUnderContent(1);
        under2.addImage(img2);
        // 关闭
        stamp.close();
        reader.close();
    }

 3.监听器

/**
     * 添加水印
     * @author ShaoMin
     * @throws IOException
     *
     */
    public static void testShuiyin2() throws Exception {
        FileOutputStream out = new FileOutputStream("./doc/15.pdf");
        Document doc = new Document();
        PdfWriter writer = PdfWriter.getInstance(doc, out);
        doc.open();
        writer.setPageEvent(new PdfPageHelper());
        doc.newPage();
        doc.add(new Chunk("aaa"));
        doc.close();
    }

class PdfPageHelper extends PdfPageEventHelper {

    @Override
    public void onOpenDocument(PdfWriter writer, Document document) {
        super.onOpenDocument(writer, document);
        writer.getDirectContent().createTemplate(30, 16);
    }

    @Override
    public void onStartPage(PdfWriter writer, Document document) {
        // 获取当前页码,以便我们可以自定义每个页面的标题。

        super.onStartPage(writer, document);
    }

    @Override
    public void onEndPage(PdfWriter writer, Document document) {
        BaseFont bf = null;
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (Exception e) {
            e.printStackTrace();
        }
        extracted(writer, document, bf);
        addWaterMark(writer, bf);
    }

    private void extracted(PdfWriter writer, Document document, BaseFont bf) {
        PdfContentByte cb = writer.getDirectContent();
        cb.saveState();
        cb.beginText();

        cb.setFontAndSize(bf, 10);
        float x = document.top(-20);
        // 左
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT, "顶部-左", document.left(), x, 0);
        // 中
        cb.showTextAligned(PdfContentByte.ALIGN_CENTER, writer.getPageNumber() + " page", (document.right() + document.left()) / 2, x, 0);
        // 右
        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT, "顶部-右", document.right(), x, 0);
        // Footer
        float y = document.bottom(-20);
        // 左
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT, "底部-左", document.left(), y, 0);
        // 中
        cb.showTextAligned(PdfContentByte.ALIGN_CENTER, writer.getPageNumber() + " page", (document.right() + document.left()) / 2, y, 0);
        // 右
        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT, "底部-右", document.right(), y, 0);
        cb.endText();
        cb.restoreState();
    }

    /**
     * 添加水印
     * @param writer
     * @param bf
     */
    private void addWaterMark(PdfWriter writer, BaseFont bf) {
        for (int i = 1; i < 7; i++) {
            for (int j = 1; j < 10; j++) {
                PdfContentByte cb = writer.getDirectContent();
                cb.saveState();
                cb.beginText();
                cb.setColorFill(BaseColor.GRAY);
                PdfGState gs = new PdfGState();
                gs.setFillOpacity(0.1f);
                cb.setGState(gs);
                cb.setFontAndSize(bf, 12);
                cb.showTextAligned(Element.ALIGN_MIDDLE, "添加水印", 75 * i, 80 * j, 30);
                cb.endText();
                cb.restoreState();
            }
        }
    }
}

三、页眉页脚

第一种方式:现有的PDF追加页眉页码

public static void insertHeadAndFoot2() throws Exception {
        FileOutputStream out = new FileOutputStream("./doc/17.pdf");
        Document doc = new Document(PageSize.A4);
        ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
        PdfWriter.getInstance(doc, outputStream);
        doc.open();
        doc.add(new Paragraph("1 page"));
        doc.newPage();
        doc.add(new Paragraph("2 page"));
        doc.close();


        BaseFont bf = null;
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //读取现存在的pdf文件 追加页码
        PdfReader reader = new PdfReader(outputStream.toByteArray());
        int numberOfPages = reader.getNumberOfPages();
        PdfStamper stamp = new PdfStamper(reader, out);
        for (int i = 1; i <= numberOfPages; i++) {
            PdfContentByte cb = stamp.getOverContent(i);// 页数
            cb.beginText();
            cb.setFontAndSize(bf, 10);
            cb.showTextAligned(PdfContentByte.ALIGN_CENTER, "- " + (i) + " -", (doc.right() + doc.left()) / 2, doc.bottom(-20), 0);
            cb.endText();

        }
        stamp.close();
        reader.close();
    }

第二种方式:   监听器 - PdfPageEvent. 如果无法确定总页数,可以该监听器重写onEndPage方法。

writer.setPageEvent() 方法要放在 document.open() 方法之前,这样才能确定总数

public static void insertHeadAndFoot() throws Exception {
        FileOutputStream out = new FileOutputStream("./doc/16.pdf");
        Document doc = new Document();
        PdfWriter writer = PdfWriter.getInstance(doc, out);
        // 内部类,处理器
        writer.setPageEvent(new PdfPageEventHelper() {
            // 模板
            public PdfTemplate total;
            private  BaseFont bf = null;
            private Font basefont = null;

            {
                try {
                    bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                    basefont = new Font(bf, 12, Font.NORMAL);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            /**
             * 文档打开时创建模板
             */
            public void onOpenDocument(PdfWriter writer, Document document) {
                total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
            }
            @Override
            public void onEndPage(PdfWriter writer, Document document) {
                PdfContentByte cb = writer.getDirectContent();
                // 写入页眉
                ColumnText.showTextAligned(cb, Element.ALIGN_LEFT, new Phrase("页眉", basefont), document.left(), document.top() + 20, 0);
                //写入页码
                int pageS = writer.getPageNumber();
                String foot1 = "第 " + pageS + " 页 /共";
                float len = bf.getWidthPoint(foot1, 12);
                Phrase footer = new Phrase(foot1, basefont);
                ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer,(document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F, document.bottom() - 20, 0);
                cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 20F, document.bottom() - 20);
            }

            /**
             * 关闭文档时,替换模板,完成整个页眉页脚组件
             */
            public void onCloseDocument(PdfWriter writer, Document document) {
                // 关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
                total.beginText();
                total.setFontAndSize(bf, 12);// 生成的模版的字体、颜色
                String foot2 = " " + (writer.getPageNumber()) + " 页";
                total.showText(foot2);// 模版显示的内容
                total.endText();
                total.closePath();
            }

        });
        doc.open();
        doc.add(new Paragraph("1 page"));
        doc.newPage();
        doc.add(new Paragraph("2 page"));
        doc.close();
    }

四、合并多个PDF

1、涉及的核心类:PdfReader,PdfWriter、PdfCopy(PdfWriter的子类)

2、实现:2次循环

1)第一层循环:PDF合并的文件个数,有几个PDF需要合并。

2)第二层循环:一个PDF文件的页数,一个PDF有几页。将其存放在新的PDF上。

1)使用PdfCopy

    public static boolean mergePdfFiles(String[] files, String newfile) {
        boolean retValue = false;
        Document document = null;
        try {
            document = new Document();
            PdfCopy copy = new PdfCopy(document, new FileOutputStream(newfile));
            document.open();
            for (int i = 0; i < files.length; i++) {// 几个pdf文件循环
                PdfReader reader = new PdfReader(files[i]);
                int n = reader.getNumberOfPages();
                for (int j = 1; j <= n; j++) {// 一个文件有多少页循环
                    document.newPage();
                    // 从reader读取原始pdf每一页的数据追加进新的pdf中
                    PdfImportedPage page = copy.getImportedPage(reader, j);
                    copy.addPage(page);
                }
            }
            retValue = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            document.close();
        }
        return retValue;
    }

2)使用PdfWriter实现PDF合并

public static void mergePdf(String[] files, String savepath) throws Exception {
        Document document = new Document();
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(savepath));
        document.open();
        PdfContentByte cb = writer.getDirectContent();// 得到层

        for (int i = 0; i < files.length; i++) {
            PdfReader reader = new PdfReader(files[i]);
            int n = reader.getNumberOfPages();
            for (int j = 1; j <= n; j++) {
                document.newPage();
                PdfImportedPage page = writer.getImportedPage(reader, j);
                // 使用writer需要使用pdf的层,然后后添加
                // 可以 放大 缩小
                cb.addTemplate(page, 0, 0);
                // 扩大比例
//                 cb.addTemplate(page, 0.6f, 0, 0, 0.6f, 0, page.getHeight() + 10);
//                 cb.addTemplate(page, 1.0f, 0, 0, 1.0f, 0, page.getHeight() + 10);
            }
        }
        document.close();
    }

五、表单PDF

package com.lean.itextpdf;

import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

public class PdfExampleDemo {

    public static void main(String[] args) {
        Document document = new Document(PageSize.A4, 10, 10, 36, 36);
        try {
            PdfWriter.getInstance(document, new FileOutputStream("./doc/12.pdf"));
            document.open();
            BaseFont bfChines = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            Font FontChinese = new Font(bfChines, 18, Font.BOLD);
            Font FontData = new Font(bfChines, 12, Font.UNDEFINED);
            Font FontTable = new Font(bfChines, 10, Font.NORMAL);
            Font FontTableHeader = new Font(bfChines, 8, Font.BOLD);

            Paragraph titlle = new Paragraph("项目成本单", FontChinese);
            titlle.setAlignment(Element.ALIGN_CENTER);
            document.add(titlle);

            PdfPTable tableHeader = new PdfPTable(2);
            PdfPCell cell = new PdfPCell(new Paragraph("项目编号: " + "0333333333", FontData));

            cell.setBorder(Rectangle.NO_BORDER);
            tableHeader.addCell(cell);
            cell = new PdfPCell(new Paragraph("项目名称: " + "xxxxx开发项目", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            tableHeader.addCell(cell);

            cell = new PdfPCell(new Paragraph("项目例属: " + "xxxxx", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            tableHeader.addCell(cell);

            cell = new PdfPCell(new Paragraph("负责人: " + "xxxxx", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            tableHeader.addCell(cell);

            cell = new PdfPCell(new Paragraph("时间: " + new SimpleDateFormat("yyyy-MM-dd").format(new Date()), FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            // 设置表上边的距离
            tableHeader.setSpacingBefore(30f);
            tableHeader.addCell(cell);

            float[] widths = {0.5f, 0.5f}; // 设置列的宽度 百分比

            tableHeader.setWidths(widths);
            tableHeader.setSpacingBefore(15f);
            document.add(tableHeader);

            PdfPTable table = new PdfPTable(7);
            cell = new PdfPCell(new Paragraph("编号", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            cell = new PdfPCell(new Paragraph("名称", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            cell = new PdfPCell(new Paragraph("数量", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            cell = new PdfPCell(new Paragraph("单位", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            cell = new PdfPCell(new Paragraph("单价(元)", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            cell = new PdfPCell(new Paragraph("折扣(%)", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            cell = new PdfPCell(new Paragraph("公式", FontTableHeader));
            cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 剧中显示
            table.addCell(cell);

            // 固定高度
            cell.setFixedHeight(20f);

            for (int i = 0; i < 40; i++) {
                cell = new PdfPCell(new Paragraph((i + 1) + "", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);
                cell = new PdfPCell(new Paragraph("JAVA", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);

                cell = new PdfPCell(new Paragraph("1", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);

                cell = new PdfPCell(new Paragraph("部", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);

                cell = new PdfPCell(new Paragraph("5000", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);

                cell = new PdfPCell(new Paragraph("80%", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);
                cell = new PdfPCell(new Paragraph("xxxx", FontTable));
                cell.setHorizontalAlignment(Element.ALIGN_CENTER);// 水平居中
                cell.setVerticalAlignment(Element.ALIGN_MIDDLE); // 垂直居中
                table.addCell(cell);
                // 每页设置表头
                table.setHeaderRows(1);
            }

            float[] width = {0.2f, 0.2f, 0.1f, 0.1f, 0.1f, 0.1f, 0.2f};
            table.setWidths(width);
            table.setSpacingBefore(15f);
            document.add(table);

            PdfPTable tableBottom = new PdfPTable(2);
            cell = new PdfPCell(new Paragraph("负责人:xxxx", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            tableBottom.addCell(cell);

            cell = new PdfPCell(new Paragraph("编号:xxxx", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            tableBottom.addCell(cell);

            cell = new PdfPCell(new Paragraph("最近修改人:xxxx", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            tableBottom.addCell(cell);

            cell = new PdfPCell(new Paragraph("业务归属人:xxxx", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            cell.setColspan(2);
            tableBottom.addCell(cell);

            cell = new PdfPCell(new Paragraph("备注:开发进度", FontData));
            cell.setBorder(Rectangle.NO_BORDER);
            cell.setColspan(2);
            tableBottom.addCell(cell);

            tableBottom.setSpacingBefore(15f);
            document.add(tableBottom);
            document.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

package com.lean.itextpdf.example;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import lombok.Builder;
import lombok.Data;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class PdfTest {
    public static void main(String[] args) throws Exception {

        Document document = new Document(PageSize.A4);
        PdfWriter pdfWriter = PdfWriter.getInstance(document, new FileOutputStream("./doc/test.pdf"));
        //
        pdfWriter.setPageEvent(new MyHeaderFooterPageEventHelper("左上标题", "右上标题", "左下标题", "测试水印"));

        document.open();

        document.addAuthor("作责");
        document.addCreationDate();
        document.addTitle("标题");
        document.addKeywords("关键字");
        document.addCreator("创建人");

        // Title
        document.add(createTitle("Java PDF生成"));

        // Chapter 1
        document.add(createChapter("1. 知识准备",22));
        document.add(createChapter("1.1 什么是TEXT",18));
        document.add(createParagraph(
            "Apache iText 是一个开源 Java 库,支持 PDF 文档的开发和转换。其目前遵从AGPL开源协议,AGPL 可以说是最严格的 GPL 了,并且Itext有很多product开始收费,但所需的功能基本上开源的API都能满足。"));
        document.add(createChapter("1.2 Apache iText中基础概念",18));
        document.add(createParagraph("基本上开源的API"));

        // Chapter 2
        document.add(createChapter("2. 实现案例",22));
        document.add(createChapter("2.1 用户列表示例",18));
        document.add(createParagraph("以导出用户列表为例"));

        // 表格
        List<User> userList = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            userList.add(User.builder().id(Long.parseLong(i + "")).userName("姓名" + i).email("邮箱" + i).phoneNumber(123456L)
                    .description("hello world" + i).build());
        }
        PdfPTable table = new PdfPTable(new float[] {20, 40, 50, 40, 40});
        table.setTotalWidth(500);
        table.setLockedWidth(true);
        table.setHorizontalAlignment(Element.ALIGN_CENTER);
        table.getDefaultCell().setBorder(1);

        for (int i = 0; i < userList.size(); i++) {
            table.addCell(createCell(userList.get(i).getId() + ""));
            table.addCell(createCell(userList.get(i).getUserName()));
            table.addCell(createCell(userList.get(i).getEmail()));
            table.addCell(createCell(userList.get(i).getPhoneNumber() + ""));
            table.addCell(createCell(userList.get(i).getDescription()));
        }
        document.add(table);

        document.add(createChapter("2.2 图片导出示例",18));
        document.add(createParagraph("以导出图片为例"));
        // 图片
        Image image = Image.getInstance("./doc/aaa.jpg");
        image.setAlignment(Element.ALIGN_CENTER);
        image.scalePercent(60); // 缩放
        document.add(image);

        // close
        document.close();
    }


    /**
     * 标题
     * @param content
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    public static Paragraph createTitle(String content) throws IOException, DocumentException {
        Font font = new Font(getBaseFont(), 24, Font.BOLD);
        Paragraph paragraph = new Paragraph(content, font);
        paragraph.setAlignment(Element.ALIGN_CENTER);
        return paragraph;
    }


    /**
     * @param content
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    public static Paragraph createChapter(String content,int fontSize) throws IOException, DocumentException {
        Font font = new Font(getBaseFont(), fontSize, Font.BOLD);
        Paragraph paragraph = new Paragraph(content, font);
        paragraph.setAlignment(Element.ALIGN_LEFT);
        return paragraph;
    }

    /**
     * 段落
     * @param content
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    public static Paragraph createParagraph(String content) throws IOException, DocumentException {
        Font font = new Font(getBaseFont(), 12, Font.NORMAL);
        Paragraph paragraph = new Paragraph(content, font);
        paragraph.setAlignment(Element.ALIGN_LEFT);
        paragraph.setIndentationLeft(12); //设置左缩进
        paragraph.setIndentationRight(12); //设置右缩进
        paragraph.setFirstLineIndent(24); //设置首行缩进
        paragraph.setLeading(20f); //行间距
        paragraph.setSpacingBefore(5f); //设置段落上空白
        paragraph.setSpacingAfter(10f); //设置段落下空白
        return paragraph;
    }

    /**
     * 单元格
     * @param content
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    public static PdfPCell createCell(String content) throws IOException, DocumentException {
        PdfPCell cell = new PdfPCell();
        cell.setVerticalAlignment(Element.ALIGN_MIDDLE);
        cell.setHorizontalAlignment(Element.ALIGN_CENTER);
        Font font = new Font(getBaseFont(), 12, Font.NORMAL);
        cell.setPhrase(new Phrase(content, font));
        return cell;
    }

    /**
     * 字体
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    public static BaseFont getBaseFont() throws IOException, DocumentException {
        return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
    }

}

@Data
@Builder
class User {
    private Long id;
    private String userName;
    private String email;
    private Long phoneNumber;
    private String description;
}
class MyHeaderFooterPageEventHelper extends PdfPageEventHelper {

    private String headLeftTitle;

    private String headRightTitle;

    private String footerLeft;

    private String waterMark;

    // 模板
    public PdfTemplate total;

    public BaseFont bf = null;
    private Font basefont = null;

    {
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            basefont = new Font(bf, 12, Font.NORMAL);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public MyHeaderFooterPageEventHelper(String headLeftTitle, String headRightTitle, String footerLeft, String waterMark) {
        this.headLeftTitle = headLeftTitle;
        this.headRightTitle = headRightTitle;
        this.footerLeft = footerLeft;
        this.waterMark = waterMark;
    }

    /**
     * 文档打开时创建模板
     */
    public void onOpenDocument(PdfWriter writer, Document document) {
        total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
    }

    // 一页加载完成触发,写入页眉和页脚
    @Override
    public void onEndPage(PdfWriter writer, Document document) {

        // page header and footer
        addPageHeaderAndFooter(writer, document, bf);

        // watermark
        if (waterMark != null) {
            addWaterMark(writer, document, bf);
        }
    }

    private void addPageHeaderAndFooter(PdfWriter writer, Document document, BaseFont bf) {

        PdfContentByte cb = writer.getDirectContent();
        cb.saveState();
        cb.beginText();
        cb.setColorFill(BaseColor.GRAY);
        cb.setFontAndSize(bf, 10);
        // header
        float x = document.top(-10);
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT, headLeftTitle, document.left(), x, 0);
        cb.showTextAligned(PdfContentByte.ALIGN_RIGHT, headRightTitle, document.right(), x, 0);

        // footer
        float y = document.bottom(-10);
        cb.showTextAligned(PdfContentByte.ALIGN_LEFT, footerLeft, document.left(), y, 0);
        cb.showTextAligned(PdfContentByte.ALIGN_CENTER, String.format("- %d /", writer.getPageNumber()), (document.right() + document.left()) / 2, y, 0);
        cb.endText();
        cb.restoreState();

        cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 10F, document.bottom() - 10);

    }

    private void addWaterMark(PdfWriter writer, Document document, BaseFont bf) {
        for (int i = 1; i < 7; i++) {
            for (int j = 1; j < 10; j++) {
                PdfContentByte cb = writer.getDirectContent();
                cb.saveState();
                cb.beginText();
                cb.setColorFill(BaseColor.GRAY);
                PdfGState gs = new PdfGState();
                gs.setFillOpacity(0.1f);
                cb.setGState(gs);
                cb.setFontAndSize(bf, 12);
                cb.showTextAligned(Element.ALIGN_MIDDLE, waterMark, 75 * i, 80 * j, 30);
                cb.endText();
                cb.restoreState();
            }
        }
    }

    // 全部完成后,将总页数的pdf模版写到指定位置
    @Override
    public void onCloseDocument(PdfWriter writer, Document document) {
        basefont.setColor(BaseColor.GRAY);
        basefont.setSize(10);
        Paragraph paragraph = new Paragraph((writer.getPageNumber()) + " -", basefont);
        ColumnText.showTextAligned(total, Element.ALIGN_LEFT, paragraph, 0, 0, 0);
    }
}

六、模板PDF

一、html模板

<dependency>
            <groupId>com.itextpdf.tool</groupId>
            <artifactId>xmlworker</artifactId>
            <version>5.5.11</version>
        </dependency>

        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.31</version>
        </dependency>

        <!-- flying saucer,支持对CSS高级特性的解析 -->
        <dependency>
            <groupId>org.xhtmlrenderer</groupId>
            <artifactId>flying-saucer-pdf</artifactId>
            <version>9.1.6</version>
        </dependency>

        <!-- html 模板生成pdf -->

模板:freemarker.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
    <style>
        body {
            font-family: SimHei;
        }

        input {
            -webkit-appearance: checkbox;
        }

        table{
            border: 1px solid black;
            width: 100%;
            margin: 0 auto;
            border-spacing:0 ;
            background-color: #bab3b3;
            border-collapse: collapse;
        }
        td{
            border: 1px solid black;
            padding: 10px;
        }
        #div-head {
            line-height: 50px;
            text-align: center
        }
    </style>
</head>
<body>
<div class="color pos">

    <div style="color: #4787ed">你好,${name}</div>

    <img src="${imgUrl}" width="600px"/>

    <div id="div-head">
        <span style="font-size: 24px;">HTML模板生成PDF文件111</span>
    </div>

    <div style="margin-bottom: 18px">
        <div style="margin: 0;padding: 0;display: inherit;float: left">
            <span style="font-size: 15px" >级别:123456</span>
        </div>
        <div style="margin: 0;padding: 0;display: inherit;float: right;">
            <span style="font-size: 15px" >编号:123456</span>
        </div>
    </div>

    <!-- 重复表头表尾的table标签设置css属性“repeat-header:yes”或“repeat-footer:yes” -->
    <!-- 文本太长需要另起一页  page-break-after:always和page-break-before:always -->

    <table style="margin-top: 12px">
        <tr>
            <td colspan="9">2013年度图书销售统计</td>
        </tr>
        <tr>
            <td rowspan="2">图书分类</td> <td colspan="2">一季度</td><td colspan="2">二季度</td><td colspan="2">三季度</td><td colspan="2">四季度</td>
        </tr>
        <tr>
            <td>销售量</td> <td>销售额</td> <td>销售量</td> <td>销售额</td> <td>销售量</td> <td>销售额</td> <td>销售量</td> <td>销售额</td>
        </tr>
        <tr>
            <td>小说</td> <td>23521</td> <td>?599,940.00</td> <td>18423</td> <td>?44,841.00</td> <td>32125</td> <td>?829,870.00</td><td>25100</td> <td>?586,564.00</td>
        </tr>
        <tr>
            <td>文艺</td> <td>7643</td><td>?180,423.00</td><td>8010</td><td>?192,420.00</td><td>10289</td><td>?321,717.00</td><td>9012</td><td>?266,134.00</td>
        </tr>
        <tr>
            <td>励志/成功</td> <td>13328</td><td>?428,371.00</td><td>15021</td><td>?592,987.00</td><td>13496</td><td>?471,620.00</td><td>10130</td><td>?386,845.00</td>
        </tr>
        <tr>
            <td>童书</td> <td>20328</td><td>?358,891.00</td><td>24030</td><td>?392,713.00</td><td>27444</td><td>?461,770.00</td><td>18027</td><td>?328,451.00</td>
        </tr>
        <tr>
            <td>小说</td> <td>23521</td> <td>?599,940.00</td> <td>18423</td> <td>?44,841.00</td> <td>32125</td> <td>?829,870.00</td><td>25100</td> <td>?586,564.00</td>
        </tr>
        <tr>
            <td>文艺</td> <td>7643</td><td>?180,423.00</td><td>8010</td><td>?192,420.00</td><td>10289</td><td>?321,717.00</td><td>9012</td><td>?266,134.00</td>
        </tr>
        <tr>
            <td>励志/成功</td> <td>13328</td><td>?428,371.00</td><td>15021</td><td>?592,987.00</td><td>13496</td><td>?471,620.00</td><td>10130</td><td>?386,845.00</td>
        </tr>
        <tr>
            <td>童书</td> <td>20328</td><td>?358,891.00</td><td>24030</td><td>?392,713.00</td><td>27444</td><td>?461,770.00</td><td>18027</td><td>?328,451.00</td>
        </tr>
        <tr>
            <td>小说</td> <td>23521</td> <td>?599,940.00</td> <td>18423</td> <td>?44,841.00</td> <td>32125</td> <td>?829,870.00</td><td>25100</td> <td>?586,564.00</td>
        </tr>
        <tr>
            <td>文艺</td> <td>7643</td><td>?180,423.00</td><td>8010</td><td>?192,420.00</td><td>10289</td><td>?321,717.00</td><td>9012</td><td>?266,134.00</td>
        </tr>
        <tr>
            <td>励志/成功</td> <td>13328</td><td>?428,371.00</td><td>15021</td><td>?592,987.00</td><td>13496</td><td>?471,620.00</td><td>10130</td><td>?386,845.00</td>
        </tr>
        <tr>
            <td>童书</td> <td>20328</td><td>?358,891.00</td><td>24030</td><td>?392,713.00</td><td>27444</td><td>?461,770.00</td><td>18027</td><td>?328,451.00</td>
        </tr>
        <tr>
            <td>小说</td> <td>23521</td> <td>?599,940.00</td> <td>18423</td> <td>?44,841.00</td> <td>32125</td> <td>?829,870.00</td><td>25100</td> <td>?586,564.00</td>
        </tr>
        <tr>
            <td>文艺</td> <td>7643</td><td>?180,423.00</td><td>8010</td><td>?192,420.00</td><td>10289</td><td>?321,717.00</td><td>9012</td><td>?266,134.00</td>
        </tr>
        <tr>
            <td>励志/成功</td> <td>13328</td><td>?428,371.00</td><td>15021</td><td>?592,987.00</td><td>13496</td><td>?471,620.00</td><td>10130</td><td>?386,845.00</td>
        </tr>
        <tr>
            <td>童书</td> <td>20328</td><td>?358,891.00</td><td>24030</td><td>?392,713.00</td><td>27444</td><td>?461,770.00</td><td>18027</td><td>?328,451.00</td>
        </tr>
        <tr>
            <td>小说</td> <td>23521</td> <td>?599,940.00</td> <td>18423</td> <td>?44,841.00</td> <td>32125</td> <td>?829,870.00</td><td>25100</td> <td>?586,564.00</td>
        </tr>
        <tr>
            <td>文艺</td> <td>7643</td><td>?180,423.00</td><td>8010</td><td>?192,420.00</td><td>10289</td><td>?321,717.00</td><td>9012</td><td>?266,134.00</td>
        </tr>
        <tr>
            <td>励志/成功</td> <td>13328</td><td>?428,371.00</td><td>15021</td><td>?592,987.00</td><td>13496</td><td>?471,620.00</td><td>10130</td><td>?386,845.00</td>
        </tr>
        <tr>
            <td>童书</td> <td>20328</td><td>?358,891.00</td><td>24030</td><td>?392,713.00</td><td>27444</td><td>?461,770.00</td><td>18027</td><td>?328,451.00</td>
        </tr>
        <tr>
            <td>小说</td> <td>23521</td> <td>?599,940.00</td> <td>18423</td> <td>?44,841.00</td> <td>32125</td> <td>?829,870.00</td><td>25100</td> <td>?586,564.00</td>
        </tr>
        <tr>
            <td>文艺</td> <td>7643</td><td>?180,423.00</td><td>8010</td><td>?192,420.00</td><td>10289</td><td>?321,717.00</td><td>9012</td><td>?266,134.00</td>
        </tr>
        <tr>
            <td>励志/成功</td> <td>13328</td><td>?428,371.00</td><td>15021</td><td>?592,987.00</td><td>13496</td><td>?471,620.00</td><td>10130</td><td>?386,845.00</td>
        </tr>
        <tr>
            <td>童书</td> <td>20328</td><td>?358,891.00</td><td>24030</td><td>?392,713.00</td><td>27444</td><td>?461,770.00</td><td>18027</td><td>?328,451.00</td>
        </tr>

    </table>
</div>
</body>
</html>
package com.lean.itextpdf.html;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;

import com.itextpdf.tool.xml.XMLWorkerFontProvider;
import com.itextpdf.tool.xml.XMLWorkerHelper;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * html 模板 生成pdf
 */
public class HtmlToPdfTest {

    /**
     * html渲染为pdf
     *
     * @param data 变量
     * @param htmlTmp 模板文件名
     * @param pdftemp pdf导出路径
     * @return
     */
    public static void freeMarkerRender(Map<String, Object> data, String htmlTmp, String pdftemp) throws Exception {
        // 获取模板,并设置编码方式
        Configuration  freemarkerCfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        //文件夹目录位置
        freemarkerCfg.setDirectoryForTemplateLoading(new File("./doc"));
        Template template = freemarkerCfg.getTemplate(htmlTmp,"UTF-8");
        StringWriter out = new StringWriter();
        // 合并模板跟数据
        template.process(data, out);
        // htmlData 模板字符流
        String htmlData = out.toString();
        // 设置文档格式,数字边距
        Document document = new Document(PageSize.A4);
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(pdftemp));
        // 添加页码
        writer.setPageEvent(new PdfReportM1HeaderFooter());
        // 打开文档
        document.open();

        // HTML 转成pdf
        XMLWorkerHelper.getInstance().parseXHtml(writer, document, new ByteArrayInputStream(htmlData.getBytes()), Charset.forName("UTF-8"),
            new MyFontsProvider(12));
        // 关闭文档
        document.close();
    }

    /**
     * @param file
     * @return
     */
    public static String fileToBase64Str(File file) {
        byte[] data = null;
        InputStream inputStream = null;
        if (file != null) {
            try {
                inputStream = new FileInputStream(file);
                data = new byte[inputStream.available()];
                inputStream.read(data);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            BASE64Encoder encoder = new BASE64Encoder();
            return Base64.encodeBase64String(data);
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> data = new HashMap();
        data.put("name",123);
        data.put("imgUrl","https://ccdn.goodq.top/caches/4a7b7daf436f3e5cb2e29433375ccd5d/aHR0cHM6Ly81NTlhNDQ3YzczMmVlLnQ3My5xaWZlaXllLmNvbS9xZnktY29udGVudC91cGxvYWRzLzIwMTQvMDYvYjU5YzY3YmYxOTZhNDc1ODE5MWU0MmY3NjY3MGNlYmEuanBn.jpg");
        freeMarkerRender(data, "freemarker.html", "./doc/ad.pdf");
    }

}

class MyFontsProvider extends XMLWorkerFontProvider {

    private int fontSize;

    public MyFontsProvider() {
        this.fontSize = 0;
    }

    public MyFontsProvider(int fontSize) {
        this.fontSize = fontSize;
    }

    @Override
    public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size, final int style, final BaseColor color) {
        BaseFont bf = null;
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Font font = null;
        if (fontSize != 0) {
            font = new Font(bf, fontSize, style, color);
        } else {
            font = new Font(bf, size, style, color);
        }
        font.setColor(color);
        return font;
    }

    public int getFontSize() {
        return fontSize;
    }

    public void setFontSize(int fontSize) {
        this.fontSize = fontSize;
    }

}
class PdfReportM1HeaderFooter extends PdfPageEventHelper {

    /**
     * 页眉
     */
    public String header = "页眉";

    /**
     * 文档字体大小,页脚页眉最好和文本大小一致
     */
    public int presentFontSize = 12;

    // 模板
    public PdfTemplate total;

    private  BaseFont bf = null;

    public PdfReportM1HeaderFooter() {

    }

    /**
     * 文档打开时创建模板
     */
    public void onOpenDocument(PdfWriter writer, Document document) {
        total = writer.getDirectContent().createTemplate(50, 50);// 共 页 的矩形的长宽高
    }

    /**
     * 关闭每页的时候,写入页眉,写入'第几页共'这几个字。
     *
     * @see com.itextpdf.text.pdf.PdfPageEventHelper#onEndPage(com.itextpdf.text.pdf.PdfWriter, com.itextpdf.text.Document)
     */
    public void onEndPage(PdfWriter writer, Document document) {
        Font fontDetail = null;
        try {
            bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", false);
            fontDetail = new Font(bf, presentFontSize, Font.NORMAL);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 1.写入页眉
        ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT, new Phrase(header, fontDetail), document.left(), document.top() + 20, 0);
        // 2.写入前半部分的 第 X页/共
        int pageS = writer.getPageNumber();
        String foot1 = "第 " + pageS + " 页 /共";
        Phrase footer = new Phrase(foot1, fontDetail);
        // 3.计算前半部分的foot1的长度,后面好定位最后一部分的'Y页'这俩字的x轴坐标,字体长度也要计算进去 = len
        float len = bf.getWidthPoint(foot1, presentFontSize);
        // 4.拿到当前的PdfContentByte
        PdfContentByte cb = writer.getDirectContent();
        // 5.写入页脚
        ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer,(document.rightMargin() + document.right() + document.leftMargin() - document.left() - len) / 2.0F + 20F, document.bottom() - 20, 0);

        cb.addTemplate(total, (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F + 20F, document.bottom() - 20);

    }

    /**
     * 关闭文档时,替换模板,完成整个页眉页脚组件
     */
    public void onCloseDocument(PdfWriter writer, Document document) {
        // 关闭文档的时候,将模板替换成实际的 Y 值,至此,page x of y 制作完毕,完美兼容各种文档size。
        total.beginText();
        total.setFontAndSize(bf, presentFontSize);// 生成的模版的字体、颜色
        String foot2 = " " + (writer.getPageNumber()) + " 页";
        total.showText(foot2);// 模版显示的内容
        total.endText();
        total.closePath();
    }
}

 二、使用工具构建PDF模板

工具:

Adobe Acrobat DC 工具 - 免费

        链接: https://pan.baidu.com/s/1c7C2ZLmDXfAWWmpoPevD1w?pwd=ivn8 提取码: ivn8

迅捷PDF编辑器 - 收费

       下载地址

如下是迅捷PDF编辑:

 

package com.lean.itextpdf.fremarker;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import lombok.Data;
import org.springframework.util.CollectionUtils;

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.*;
import org.springframework.util.StringUtils;

/**
 * @ClassName: PdfUtils
 * @Description:
 * @Author: zhanghongwei
 * @Date: 2022/5/17 14:11
 */
public class PdfUtils {

    public static void main(String[] args) {
        try {
            PdfDataModel pdfDataModel = new PdfDataModel();
            pdfDataModel.setText1("511300T15163");//条形码
            pdfDataModel.setText2("xxxxx业务员自费账户");//送检单位
            pdfDataModel.setText3("xxxx");//姓名
            pdfDataModel.setText4("男");//性别
            pdfDataModel.setText14("27");//年龄
            pdfDataModel.setText5("xxxxxxxxxxxxxxxxxxxx");//身份证号
            pdfDataModel.setText6("送检医师");//送检医师
            pdfDataModel.setText7("病 员 号");//病 员 号
            pdfDataModel.setText8("科    别");//科    别
            pdfDataModel.setText9("xxxxxxxxx");//病人电话
            pdfDataModel.setText10("咽拭子");//标本类型
            pdfDataModel.setText11("肉眼未见异常");//标本性状
            pdfDataModel.setText12("2022-04-27 17:35 ");//采样时间
            pdfDataModel.setText13("2022-04-27 17:35 ");
            pdfDataModel.setText15("xxx xxx Ao xxx Laboratory Results Report");//英文主题
            pdfDataModel.setText22("xxxxxxxxxx检验实验室检验报告单");//汉语主题
            pdfDataModel.setText23("此结果仅为2019-nCoV新型冠状病毒核酸定性筛查; \n" +
                    "本检测结果所使用的检测方法为实时荧光RT-PCR技术,最低检测限: 500 拷贝/mL\n" +
                    ",检测新型冠状病毒(2019-nCoV)的ORF1ab和编码核衣壳蛋白基因N。");//建议与解释:
            pdfDataModel.setText16("xxxxx");//备 注
            pdfDataModel.setText17("LABxxxxxxxx");//院方条形码
            pdfDataModel.setText18("www.xxxxx.com");//网站地址:
            pdfDataModel.setText19("xxxxxxxxxxxxxxxxxxxxxxxxxx");//地址:
            pdfDataModel.setText20("xxxxxxxxxx/xxxxxxxxx(转602)");//客服电话:
            pdfDataModel.setText21(" 2022-11-2301:58");//报告日期

            //图片
            PdfDataImgaModel pdfDataImgaModel = new PdfDataImgaModel();
            pdfDataImgaModel.setImage1("doc/1.png");//左上角图片
            pdfDataImgaModel.setImage2("doc/2.jpeg");//专用章图片
            pdfDataImgaModel.setImage3("doc/3.png");//右下角图片
            pdfDataImgaModel.setImage5("doc/4.png");//检验者
            pdfDataImgaModel.setImage6("doc/5.png");//审核者
            pdfDataImgaModel.setImage7("doc/6.png");//批准人
            pdfDataImgaModel.setImage4("doc/7.png");//条形码图片

            List<TestItemsModel> testItemsModels = new ArrayList<>();
            for (int i = 0; i < 10; i++) {
                TestItemsModel testItemsModel = new TestItemsModel();
                testItemsModel.setTitle("新型冠状病毒核酸检测(10合1)(2019-nCoV)" + i);
                testItemsModel.setResult("阴性");
                testItemsModel.setTag("阴性");
                testItemsModel.setUnit("单位");
                testItemsModels.add(testItemsModel);
            }
            ByteArrayOutputStream outputStream = getPdfUrl(testItemsModels, pdfDataModel, pdfDataImgaModel);

            FileOutputStream fos = new FileOutputStream("./doc/re.pdf");
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            bos.write(outputStream.toByteArray());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 检测报告
     * @param testItemsModels
     * @param pdfDataModel
     * @param pdfDataImgaModel
     * @return
     */
    public static ByteArrayOutputStream getPdfUrl(List<TestItemsModel> testItemsModels, PdfDataModel pdfDataModel, PdfDataImgaModel pdfDataImgaModel) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try {
            BaseFont chinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
            ByteArrayOutputStream outputStream = generateTempPDF("./doc/pdf_template.pdf", chinese, pdfDataModel, pdfDataImgaModel);
            List<PdfReader> pdfReaderList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(testItemsModels)) {
                byte[] bytes = outputStream.toByteArray();
                List<List<TestItemsModel>> splitList = splitList(testItemsModels, 20);
                for (List<TestItemsModel> itemsModels : splitList) {
                    //填充数据
                    PdfReader pdfReader = new PdfReader(filleTestItemsData(chinese, itemsModels, bytes).toByteArray());
                    pdfReaderList.add(pdfReader);
                }
            } else {
                PdfReader pdfReader = new PdfReader(outputStream.toByteArray());
                pdfReaderList.add(pdfReader);
            }
            mergePdfFiles(pdfReaderList, byteArrayOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return byteArrayOutputStream;
    }

    /**
     * 填充检测数据
     *
     * @param chinese
     * @param testItemsModels
     * @param bytes
     * @throws IOException
     * @throws DocumentException
     */
    private static ByteArrayOutputStream filleTestItemsData(BaseFont chinese, List<TestItemsModel> testItemsModels, byte[] bytes) throws IOException, DocumentException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        PdfReader reader = new PdfReader(bytes);
        Rectangle rectangle = reader.getPageSize(1);
        Document doc = new Document(rectangle, 50, 50, 50, 50);
        PdfWriter writer = PdfWriter.getInstance(doc, byteArrayOutputStream);
        doc.open();
        doc.newPage();
        PdfContentByte cb = writer.getDirectContent();
        PdfImportedPage page = writer.getImportedPage(reader, 1);
        cb.addTemplate(page, 0, 0);
        //首页追加信息
        PdfContentByte directContent = writer.getDirectContent();
        directContent.beginText();
        directContent.setFontAndSize(chinese, 10);
        directContent.setColorFill(BaseColor.BLACK);
        for (int i = 1; i <= testItemsModels.size() - 1; i++) {
            TestItemsModel testItemsModel = testItemsModels.get(i - 1);
            directContent.showTextAligned(Element.ALIGN_LEFT, testItemsModel.getTitle(), 20, 630 - (i * 20), 0);
            directContent.showTextAligned(Element.ALIGN_LEFT, testItemsModel.getResult(), 280, 630 - (i * 20), 0);
            directContent.showTextAligned(Element.ALIGN_LEFT, testItemsModel.getTag(), 380, 630 - (i * 20), 0);
            directContent.showTextAligned(Element.ALIGN_LEFT, testItemsModel.getUnit(), 520, 630 - (i * 20), 0);
        }
        directContent.endText();
        doc.close();
        return byteArrayOutputStream;
    }

    public static ByteArrayOutputStream generateTempPDF(String fileName, BaseFont chinese, PdfDataModel pdfDataModel, PdfDataImgaModel pdfDataImgaModel) {
        PdfReader reader = null;
        PdfStamper ps = null;
        ByteArrayOutputStream bos = null;
        try {
            reader = new PdfReader(fileName);
            bos = new ByteArrayOutputStream();
            ps = new PdfStamper(reader, bos);
            //填充文字
            AcroFields fields = ps.getAcroFields();
            fillData(fields, beanToMap(pdfDataModel), chinese);//渲染
            //填充图片
            Map<String, String> imageMap = beanToMap(pdfDataImgaModel);
            // 处理图片
            for (String key : imageMap.keySet()) {
                String value = imageMap.get(key);
                String imgpath = value;
                if (!org.springframework.util.StringUtils.isEmpty(imgpath)) {
                    int pageNo = fields.getFieldPositions(key).get(0).page;
                    Rectangle signRect = fields.getFieldPositions(key).get(0).position;
                    float x = signRect.getLeft();
                    float y = signRect.getBottom();
                    // 根据路径读取图片
                    Image image = Image.getInstance(imgpath);
                    // 获取图片页面
                    PdfContentByte under = ps.getOverContent(pageNo);
                    // 图片大小自适应
                    image.scaleToFit(signRect.getWidth(), signRect.getHeight());
                    // 设置图片位置,以为我们以左下角为起始点,所以这里x、y加上偏移量,偏移量为计算的居中量
                    image.setAbsolutePosition(x + (signRect.getWidth() - image.getScaledWidth()) / 2, y + (signRect.getHeight() - image.getScaledHeight()) / 2);
                    // 添加图片
                    under.addImage(image);
                }
            }
            //必须要调用这个,否则文档不会生成的
            ps.setFormFlattening(true);
            if (ps != null) {
                ps.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return bos;
    }

    /**
     * PDF文件合并
     *
     * @param pdfReaderList
     * @author
     */
    public static boolean mergePdfFiles(List<PdfReader> pdfReaderList, ByteArrayOutputStream outputStream) {
        boolean retValue = false;
        Document document = null;
        try {
            document = new Document();
            PdfCopy copy = new PdfCopy(document, outputStream);
            document.open();
            for (PdfReader reader : pdfReaderList) {
                int n = reader.getNumberOfPages();
                for (int j = 1; j <= n; j++) {// 一个文件有多少页循环
                    document.newPage();
                    PdfImportedPage page = copy.getImportedPage(reader, j);
                    copy.addPage(page);
                }
            }
            retValue = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            document.close();
        }
        return retValue;
    }

    /**
     * 填充模板中的数据
     */
    public static void fillData(AcroFields fields, Map<String, String> data, BaseFont chinese) {
        try {
            // 默认字体
            float fontSize = 10.0f;
            for (String key : data.keySet()) {
                String value = data.get(key);
                if (!StringUtils.isEmpty(value)) {
                    // 文本框宽度
                    Rectangle position = fields.getFieldPositions(key).get(0).position;
                    float textBoxWidth = position.getWidth();
                    // 文本框高度
                    float textBoxHeight = position.getHeight();
                    float ascent = chinese.getFontDescriptor(chinese.ASCENT, fontSize);
                    // baseFont渲染后的文字宽度
                    float textWidth = chinese.getWidthPoint(value, fontSize);
                    // 文本框高度只够写一行,并且文字宽度大于文本框宽度,则缩小字体
                    if (textBoxHeight < ascent * 1.6) {
                        while (textWidth > textBoxWidth) {
                            fontSize--;
                            textWidth = chinese.getWidthPoint(value, fontSize);
                        }
                    }
                    fields.setFieldProperty(key, "textsize", 10.0f, null);
                    if (key.equalsIgnoreCase("Text15")) {
                        fields.setFieldProperty(key, "textsize", 13.0f, null);
                    }
                    if(!key.equalsIgnoreCase("Text15")
                            || !key.equalsIgnoreCase("Text22")
                            || !key.equalsIgnoreCase("Text18")
                            || !key.equalsIgnoreCase("Text19")
                            || !key.equalsIgnoreCase("Text20")  ){
                        fields.setFieldProperty(key, "textfont", chinese, null);
                    }

                    // 为字段赋值,注意字段名称是区分大小写的
                    fields.setField(key, value);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static <T> List<List<T>> splitList(List<T> list, int splitSize) {
        //判断集合是否为空
        if (CollectionUtils.isEmpty(list)) {
            return Collections.emptyList();
        }

        //计算分割后的大小
        int maxSize = (list.size() + splitSize - 1) / splitSize;
        //开始分割
        return Stream.iterate(0, n -> n + 1)
                .limit(maxSize)
                .parallel()
                .map(a -> list.parallelStream().skip(a * splitSize).limit(splitSize).collect(Collectors.toList()))
                .filter(b -> !b.isEmpty())
                .collect(Collectors.toList());
    }

    /**
     * 对象转Map
     *
     * @param object
     * @return
     * @throws IllegalAccessException
     */
    public static Map beanToMap(Object object) {
        Map<String, Object> map = new HashMap<String, Object>();
        try {
            Field[] fields = object.getClass().getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                map.put(field.getName(), field.get(object));
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return map;
    }
}
@Data
class PdfDataImgaModel {
    private String Image1 = null;//左上角图片
    private String Image2 = null; //专用章图片
    private String Image3 = null; //右下角图片
    private String Image5 = null; //检验者
    private String Image6 = null; //审核者
    private String Image7 = null;//批准人
    private String Image4 = null; //条形码图片
}
@Data
class PdfDataModel {
    private String Text1 = null; //条形码
    private String Text2 = null; //送检单位
    private String Text3 = null; //姓名
    private String Text4 = null; //性别
    private String Text14 = null;//年龄
    private String Text5 = null; //身份证号
    private String Text6 = null; //送检医师
    private String Text7 = null; //病 员 号
    private String Text8 = null; //科    别
    private String Text9 = null; //病人电话
    private String Text10 = null; //标本类型
    private String Text11 = null;//标本性状
    private String Text12 = null; //采样时间
    private String Text13 = null; //接收时间
    private String Text15 = null; //英文主题
    private String Text22 = null; //汉语主题
    private String Text23 = null; //建议与解释:
    private String Text16 = null; //备 注
    private String Text17 = null; //院方条形码
    private String Text18 = null; //网站地址:
    private String Text19 = null;//地址:
    private String Text20 = null; //客服电话
    private String Text21 = null; //报告日期
}
@Data
class TestItemsModel {
    //检测项目
    private String title;
    //结果
    private String result;
    //提示参考范围
    private String tag;

    //单位
    private String unit;
}

 7、HTML转PDF

<dependency>
  <groupId>com.itextpdf.tool</groupId>
  <artifactId>xmlworker</artifactId>
  <version>5.5.11</version>
</dependency>
 /**
     * html 转成pdf
     * @param src
     * @param target
     * @throws IOException
     * @throws DocumentException
     */
    public static void htmlToPdf(String src, String target) throws IOException, DocumentException {
        Document document = new Document(PageSize.B5, 20, 20, 30, 20);
        PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(target));
        document.open();
        XMLWorkerHelper.getInstance().parseXHtml(writer, document, new FileInputStream(src), null, Charset.forName("UTF-8"), new XMLWorkerFontProvider() {
            @Override
            public Font getFont(final String fontname, final String encoding, final boolean embedded, final float size, final int style,
                final BaseColor color) {
                BaseFont bf = null;
                try {
                    bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                } catch (DocumentException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                Font font = new Font(bf, 6, style, color);
                font.setColor(color);
                return font;
            }
        });
        document.close();
    }

8、删除页码

思路:读取pdf文档,然后页码,然后输出到新的PDF

    public static void deletePage() throws Exception {
        FileOutputStream out = new FileOutputStream("./doc/a2.pdf");
        // 删除的方法在于读取,然后选择页数,然后在输出到另一个pdf
        PdfReader reader = new PdfReader("./doc/a1.pdf");// 读取pdf
        reader.selectPages("1,3");// 选择页数
        PdfStamper stamp = new PdfStamper(reader, out);// 输出
        stamp.close();
        reader.close();
    }

九、读取PDF内容

<dependency>
 <groupId>org.apache.pdfbox</groupId>
 <artifactId>pdfbox</artifactId>
 <version>2.0.25</version>
</dependency>
/**
     * 读取PDF 内容
     * @return
     * @throws Exception
     */
    public static String testPdfContent() throws Exception{
        PDDocument document = PDDocument.load(new FileInputStream("doc/99.pdf"));
        document.getClass();
        //使用PDFTextStripper 工具
        PDFTextStripper tStripper = new PDFTextStripper();
        //设置文本排序,有规则输出
        tStripper.setSortByPosition(true);
        //获取所有文字信息
        String info = tStripper.getText(document);
        return  info;
    }

十、删除pdf内容

    public static void deletePdfContent(String srcUrl, String outputPdfFile) throws Exception {
        PdfReader reader = new PdfReader(srcUrl);
        Document doc = new Document(reader.getPageSize(1));
        PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(outputPdfFile));
        doc.open();
        doc.newPage();
        PdfContentByte cb = writer.getDirectContent();
        PdfImportedPage page = writer.getImportedPage(reader, 1);
        cb.addTemplate(page, 0, 0);
        //??底的覆盖层
        cb.saveState();
        cb.setColorFill(BaseColor.WHITE);
        cb.rectangle(0f, 0f, doc.getPageSize().getWidth(), doc.bottom(85));
        cb.fill();
        cb.restoreState();
        doc.close();
    }

                                  

 十一、一张A4打印多个面单

public static void mergePdf(java.util.List<String> orginPdfList, String outputPdfFile) throws Exception {
        Document doc = new Document(PageSize.A4);
        PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(outputPdfFile));
        writer.setPageEvent(new PdfPageEventHelper(){
            @Override
            public void onStartPage(PdfWriter writer, Document document) {
                PdfContentByte cb = writer.getDirectContent();
                cb.saveState();
                cb.beginText();
                BaseFont bf = null;
                try {
                    bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                cb.setFontAndSize(bf, 10);
                float y = document.bottom(-20);
                cb.showTextAligned(PdfContentByte.ALIGN_CENTER,  "-"+writer.getPageNumber()+"-", (document.right() + document.left()) / 2, y, 0);
                cb.endText();
                cb.restoreState();
            }
        });
        doc.open();
        float height = 0f;
        for (int i = 0; i < orginPdfList.size(); i++) {
            PdfReader reader = new PdfReader(orginPdfList.get(i));
            PdfContentByte cb = writer.getDirectContent();
            PdfImportedPage page = writer.getImportedPage(reader, 1);
            height = page.getHeight();
            if (i == 0) {
                //设置 比例 放大或者缩小 以及防止位置
                cb.addTemplate(page, 0.95, 0, 0, 0.95, 0, height);
            }
            if (i == 1) {
                cb.addTemplate(page, 0.95, 0, 0, 0.95, 300, height);
            }
            if (i == 2) {
                cb.addTemplate(page, 0.95, 0, 0, 0.95, 0, height - 410);
            }
            if (i == 3) {
                cb.addTemplate(page, 0.95, 0, 0, 0.95, 300, height - 410);
            }
        }
        doc.close();
    }

官方地址

官方源码

代码地址 

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

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

相关文章

同花顺_知识_庄家技法_4拉升技法

写在前面&#xff1a; 文中知识内容来自书籍《同花顺炒股软件从入门到精通》 目录 1. 拉升时机的选择 1&#xff09;大盘走势稳健时 2&#xff09;重大利好出台前后 3&#xff09;进行一次凶狠的打压之后 4&#xff09;大市偏弱时 5&#xff09;图形及技术指标修好之时 …

FPGA开发(4)——AXI_LITE总线协议

一、AXI总线简介 对于axi总线的学习我主要是参考了赛灵思的ug1037文档以及arm的INI0022D手册&#xff0c;对其中的内容做了总结。 AXI是amba总线的一种&#xff0c;包含三种&#xff0c;axi full、axi lite和axi stream。 AXI工作&#xff1a;axi接口包含了五组通道&#xf…

如何快速用一条命令配置好本地yum源(6/7/8版本)

一&#xff0c;挂载ISO安装镜像 挂载方式分两种&#xff1a; 1.上传iso安装镜像到服务器主机的指定目录&#xff0c;比如/setup/os为例 mount -o loop /setup/os/iso镜像包名称 /mnt 2.直接虚拟机或者物理主机挂载iso安装镜像 mount /dev/cdrom /mnt mount/dev/sr0 /mnt 3.挂载…

【计算机网络】网络层:路由器的构成

路由器工作在网络层&#xff0c;用于互连网络&#xff0c;主要工作是转发分组。 把某个输入端口收到的分组&#xff0c;按照分组要去的目的网络&#xff0c;把该分组从路由器的某个合适的输出端口转发给下一跳路由器。 &#xff08;根据目的网络的IP地址转发分组&#xff09;…

[附源码]java毕业设计学生档案管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

使用Umi 遇到的错误

今天想使用ui&#xff0c;出现了很多错误&#xff0c;刚开始安装的时候没有一点事&#xff0c;就是运行的时候报错&#xff0c;好像和umi版本不匹配了&#xff0c;后来又把umi删除了又安装一遍&#xff0c;然后还是运行不了&#xff0c;后来我又把umijs/preset-ui卸了&#xff…

通过制作4个游戏学习Python

前言 学习编程最好玩的方法 你会学到什么 &#xff08;文末送读者福利&#xff09; 您将学习如何有效地使用Python 您将创建一个python游戏组合 你将学会如何管理好大型项目 你将学习面向对象编程 您将学习并实现高级Python特性 你将对Python有一个透彻的理解 类型:电…

Spark并行度和任务调度

文章目录并行度如何设置并行度如何规划我们自己群集环境的并行度&#xff1f;Spark的任务调度并行度 Spark之间的并行就是在同一时间内&#xff0c;有多少个Task在同时运行。并行度也就是并行能力的设置&#xff0c;假设并行度设置为6&#xff0c;就是6个task在并行跑&#xf…

蒙特卡洛原理及实例(附Matlab代码)

文章目录一、理论基础1.1 伯努利大数定理1.2 辛钦大数定理1.3 切比雪夫大数定理1.4 三者区别和联系二、蒙特卡洛法2.1 蒙特卡洛的起源2.2 蒙特卡洛的解题思路2.2 蒙特卡洛法的应用三、几个小栗子3.1 求解定积分3.1.1 解析法3.1.2 蒙特卡洛法3.2 求解六边形面积3.2.1 解析法3.2.…

[附源码]SSM计算机毕业设计基于的高校学生考勤管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

支持向量机

目录 支持向量机 0. 由来 1. 核心思想 2. 硬间隔支持向量机 2.1 间隔最大化 2.1.1 函数间隔2.1.2 几何间隔2.1.2 间隔最大化 2.2 转换为拉格朗日对偶问题 2.2.1 拉格朗日对偶问题2.2.2 将问题转换为拉格朗日对偶问题 3. 软间隔支持向量机 4. 泛函基础 4.1 度量&#xff…

Flutter 8 个优秀动画 Packages

Flutter 8 个优秀动画 Packages 前言 动画对于使移动应用程序的用户界面感觉自然流畅至关重要。加上交互式元素和平滑的过渡&#xff0c;它们使应用程序简单易用。 正文 Flutter Animate 组件 Package https://pub.dev/packages/flutter_animate 一个 performant 库&#xff0c…

springboot simple (9) springboot jpa(Hibernate)

返回目录 1 JPA Hibernate Hibernate是一个全自动的ORM框架&#xff08;Object Relational Mapping ,对象关系映射&#xff09;。 Spring Data JPA&#xff1a; 是Spring Data的子模块&#xff0c;JPA默认使用hibernate作为ORM实现。 2 springboot继承Hibernate 第1步&…

【Servlet】7:监听器和过滤器的原理和应用

目录 | 监听器 监听器 基本概述 ServletContextListener监听器 ServletContextAttributeListener监听器 监听器的应用场景 | 过滤器 过滤器 基本概述 过滤器 实现步骤 过滤器 应用场景 本文章属于后端全套笔记的第三部分 &#xff08;更新中&#xff09;【后端入门到入…

leetcode 494.目标和 动态规划背包问题 (c++版本)

题目描述 说白了就是让一部分数减去剩下的一部数使得差值为target&#xff0c;计算有多少中组合的方法 下面来个数学公式推导一下 leftrightsumleft−righttargetleftsum−lefttargetleft(sumtarget)/2leftright sum\\ left-righttarget\\ leftsum-lefttarget\\ left(sumtarge…

用户行为分析-如何用数据驱动增长

用户行为分析-如何用数据驱动增长 2022-11-22 看完书才知道是 GrowingIO 公司出的一本书&#xff0c;干货还是挺多的。 第一章从商业进化的角度认识用户行为数据的重要性&#xff0c;帮助大家了解什么是用户行为数据&#xff0c;以及用户行为数据怎么发挥价值。接着四章详细…

【操作系统】2.2 操作系统的调度

2.2.1 操作系统之处理机调度的概念及层次 2.2.1操作系统之处理机调度的概念及层次_StudyWinter的博客-CSDN博客_操作系统调度的层次 高级调度&#xff08;作业调度&#xff09;&#xff1a;外存-》内存 中级调度&#xff08;内存调度&#xff09;&#xff1a;外存-》内存 低…

用最少的代码模拟gRPC四种消息交换模式

我们知道&#xff0c;建立在HTTP2/3之上的gRPC具有四种基本的通信模式或者消息交换模式&#xff08;MEP&#xff1a; Message Exchange Pattern&#xff09;&#xff0c;即Unary、Server Stream、Client Stream和Bidirectional Stream。本篇文章通过4个简单的实例演示它们在.NE…

HTML+CSS大作业 格林蛋糕(7个页面) 餐饮美食网页设计与实现

&#x1f380; 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业…

编写第一个Qt程序和分析第一个Qt程序

文章目录编写第一个Qt程序新建一个项目项目的文件组成和管理项目的编译、调试与运行分析第一个Qt程序创建项目1) main.cpp2) mainwindow.h和mainwindow.cpp编码实现简易的窗口界面编写第一个Qt程序 已剪辑自: http://c.biancheng.net/view/1817.html 学习一种编程语言或编程环…