java关于itext5生成pdf的完整方法

Itext5API生成文pdf文档

前言

本文为本人开发时使用过的itext5生成pdf时的一些使用心得,本文仅供参考,有错误之处望读者友善提出。

Itext5生成api主要是后端在生成pdf的模板的同时,将数据填入pdf中,本文提供生成pdf页眉页脚、页码、表格、段落、背景颜色、字体设置、水印,多个pdf合并等的方法。

 

 

引入相关依赖

    <!-- https://mvnrepository.com/artifact/com.itextpdf/itextpdf -->

    <dependency>

      <groupId>com.itextpdf</groupId>

      <artifactId>itextpdf</artifactId>

      <version>5.5.13</version>

    </dependency>

    <!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian 字体-->

    <dependency>

      <groupId>com.itextpdf</groupId>

      <artifactId>itext-asian</artifactId>

      <version>5.2.0</version>

    </dependency>

正文内容

一、    创建pdf文档

总结一下用java生成PDF的方法:
A、itext-PdfStamper pdfStamper(俗称抠模板)
B、itext-Document document(正常代码撰写)
C、wkhtmltopdf(使用工具)

分析比较

方法

优点

缺点

A

代码简单

模板要先提供,且字段长度固定、不

灵活

B

模板可根据代码调整、但样式不如C灵活

要维护的后台代码较多

C

模板样式可根据前端随意调整

要维护的前台代码较多

本文采用B方式生成pdf

1、 创建文档对象Document

创建文档有三个构造方法

(1)  Document document =new Document(); // 默认页面大小是A4;

(2)  Document document =new Document(PageSize.A4); // 指定页面大小为A4;

(3)  Document document =new Document(PageSize.A4,50,50,30,20); // 指定页面大小为A4,且自定义页边距(marginLeft(左边距)、marginRight(右边距)、marginTop(上边距)、marginBottom(下边距))

 

也可以自定义页面大小 Document document = new Document(new RectangleReadOnly(宽,高));

 

2、 创建文档书写器(Writer)

.建立一个书写器(Writer)与document对象关联,通过书写器(Writer)可以将文档写入到磁盘中。

创建 PdfWriter 对象 第一个参数是对文档对象的引用,第二个参数是文件的实际名称,在该名称中还会给出其输出路径

PdfWriter writer = PdfWriter.getInstance(document,new FileOutputStream(filePath));

filePath为输出路径字符串。

 

3、 打开文档

document.open();//在写入内容之前需要先打开文档。

document.newPage();//用于一页填不满转下一页时

4、 添加内容

document.add();//往文档中添加内容

5、 关闭文档

document.close();

 

 

一个完整的pdf文档生成有5个步骤

二、    字体设置

Itext字体设置一共有三种方式:

第一种是引用window本地系统字体(这里以常见的宋体为例,只能在window系统上面使用)

BaseFont bf =BaseFont.createFont("C:/WINDOWS/Fonts/simsun.ttf", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);//本地系统字体

Font f = new Font(bf,18,Font.NORMAL);//字体类型、大小、风格样式

 

第二种是读取路径下的ttf字体文件,以项目路径下的字体为例(必须要有许可才能使用)

String path = getClass().getResource("/").getPath();

if (path.indexOf("WEB-INF/classes") != -1) {

   path = path.substring(1, path.indexOf("WEB-INF/classes"));

   path = "/"+path + "font/simsun.ttf";

   }

BaseFont bf =BaseFont.createFont(path , BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);

 Font f = new Font(bf,18,Font.NORMAL);

 

第三种是用jar包(itext-asian)中的常规字体

BaseFont chinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

font = new Font(chinese, 10, Font.NORMAL);

注意:font = new Font(chinese, 10, Font.NORMAL| UNDEFINED);

样式可以写多种样式中间用|隔开

 

三、    文本对象元素添加

文本有三个基础对象

  1. Chunk:块(Chunk)是能被添加到文档的文本的最小单位。
  2. Phrase:短句(Phrase)是一系列以特定间距(两行之间的距离)作为参数的块。
  3. Paragraph:段落是一系列块和(或)短句。同短句一样,段落有确定的间距。用户还可以指定缩排;在边和(或)右边保留一定空白,段落可以左对齐、右对齐和居中对齐。添加到文档中的每一个段落将自动另起一行。

 

四、    文档属性

文档属性的设置不需要在文档打开之后设置

document.addTitle("标题");// 标题

document.addAuthor("作者");// 作者

document.addSubject("主题");// 主题

document.addKeywords("关键字");// 关键字

document.addCreator("创建者");// 创建者

 

五、    文档内容

段落

Paragraph pt=new Paragraph(name,headfont);//设置字体样式

pt.setAlignment(1);//设置文字居中 0靠左   1,居中     2,靠右

pt.setIndentationLeft(12);// 左缩进 

pt.setIndentationRight(12);// 右缩进 

pt.setFirstLineIndent(24);// 首行缩进

paragraph.setLeading(20f); //行间距

paragraph.setSpacingBefore(5f); //设置段落上空白

paragraph.setSpacingAfter(10f); //设置段落下空白

表格

表格可以指定列数(每列宽度相同),也可以指定不同宽度的列

//创建一个 4列的表格

PdfPTable table = new PdfPTable(4);

//创建一个2列的表格宽度分别为25,425

PdfPTable table = new PdfPTable(new float[] { 25, 425 });

//设置列宽

table.setTotalWidth(540);// 如果只设置总宽度,那么表格每列宽度就是均分的。

int width[] = {10,45,45};//设置每列宽度比例

table.setWidths(width);

//锁定列宽

table.setLockedWidth(true);

//添加单元格

table.addCell(cell);

 

单元格(表格的每一格)

PdfPCell cell = new PdfPCell(new Phrase(val1, font));

//水平居中

cell.setHorizontalAlignment(Element.ALIGN_CENTER);

//垂直居中 

cell.setVerticalAlignment(Element.ALIGN_MIDDLE);

//单元格高度

cell.setFixedHeight(20);

//背景颜色

cell.setBackgroundColor();

//单元格跨列列数

cell.setColspan();

//单元格跨行行数

cell.setRowspan();//设置跨行行数

//单元格嵌套表格

PdfPCell cell2 = new PdfPCell(new PdfPTable(2));

直线 

Paragraph p1 =new Paragraph(); 

p1.add(new Chunk(new LineSeparator()));  

doc.add(p1);

点线 

Paragraph p2 =new Paragraph(); 

p2.add(new Chunk(new DottedLineSeparator()));

超链接

Anchor anchor =new Anchor("this is anchor");

添加图片

Image image =Image.getInstance(imgPath);

image.setAlignment(Image.ALIGN_CENTER);

image.scalePercent(40);//依照比例缩放

六、    页眉页脚水印页码设置

页眉 页脚 水印调用文档生命周期函数,需要继承PdfPageEventHelper类,重写

public void onEndPage(PdfWriter writer, Document document) {}方法,在onEndPage方法中调用table. WriteSelectedRows()方法。

注意:onEndPage方法的源码注释如下:

Called when a page is finished, just before being written to the document.

译为:在页面写完文档之前,当页面完成时调用。

页眉页脚实际上是将文字或者图片放在页眉、页脚(固定坐标)位置。

 

 

页码设置格式为第N页/共M页。Pdf写入页码我采用的方法是整个pdf生成之后,再读入pdf,在规定位置写好页码,写出新的pdf文档。

 

七、    生成pdf实例

 

private static final String DEST = "E:/pdfModel";

    private static final String SPACE = "                         ";

 

    private static final String SPACE3 ="      ";

 

 

    public static void main(String[] args) throws IOException, DocumentException {

 

        //封面pdf

        String cover = DEST+"/"+System.currentTimeMillis()+"cover.pdf";

        File file  = new File(cover);

 

 

 

//        String font2 = "src/main/resources/font.TTF";

//        BaseFont bf =BaseFont.createFont(font2 , BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);

        BaseFont bf =BaseFont.createFont("C:/WINDOWS/Fonts/STCAIYUN.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);//本地系统字体

        BaseFont chinese = BaseFont.createFont("STSongStd-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);

        Font fontTital = new Font(bf, 22, Font.NORMAL);

        Font font = new Font(chinese, 12, Font.NORMAL);

 

        Document tDoc = new Document(PageSize.A4, 50, 50, 50, 50); //创建文档

        PdfWriter tWriter = PdfWriter.getInstance(tDoc, new FileOutputStream(file)); //创建输出流

        //加密 注意加密过的文档是无法再处理的,如果生成pdf之后还需要对生成的pdf做处理则不能加密

//        tWriter.setEncryption(null,"1".getBytes(), PdfWriter.ALLOW_SCREENREADERS|PdfWriter.ALLOW_PRINTING,PdfWriter.STANDARD_ENCRYPTION_128); //加密

        tDoc.open();  //打开文档

        tDoc.newPage();

        //背景图片

        Image tImgCover = Image.getInstance(DEST+"/"+"星空.png");

        /* 设置图片的位置 */

        tImgCover.setAbsolutePosition(0, 0);

        /* 设置图片的大小 */

        tImgCover.scaleAbsolute(595, 842);

 

 

        tDoc.add(tImgCover);

        Paragraph paragraph1 = new Paragraph(SPACE+"家庭地址:"+SPACE3, fontTital);

        Paragraph paragraph2 = new Paragraph(SPACE+"工号:"+SPACE3, fontTital);

        Paragraph paragraph3 = new Paragraph(SPACE+"移动电话:"+SPACE3, fontTital);

        Paragraph paragraph4 = new Paragraph(SPACE+"籍贯:"+SPACE3, fontTital);

        Paragraph paragraph5 = new Paragraph(SPACE+"邮箱地址:"+SPACE3, fontTital);

        paragraph1.setSpacingBefore(470f);

        tDoc.add(paragraph1);

        tDoc.add(paragraph2);

        tDoc.add(paragraph3);

        tDoc.add(paragraph4);

        tDoc.add(paragraph5);

 

        tDoc.close();

 

 

 

 

        //正文pdf

        String mainPath = DEST+"/"+System.currentTimeMillis()+"mainBody.pdf";

        File mainFile  = new File(mainPath);

        Document mainDoc = new Document(PageSize.A4, 50, 50, 50, 50); //创建文档

        PdfWriter writer = PdfWriter.getInstance(mainDoc, new FileOutputStream(mainFile)); //创建输出流

 

        //页眉图片

        String hander = DEST+"/"+"页眉.jpg";

 

        //页脚图片

        String fotter = DEST+"/"+"页脚.jpg";

        mainDoc.open();

        mainDoc.newPage();

        creatHeader(mainDoc,writer,hander,fotter);

        PdfPTable table = new PdfPTable(7);

        table.setTotalWidth(540);

        table.setLockedWidth(true);

        table.setSpacingBefore(100);

 

        table.addCell(addCell("事件", font, null, 3));

        table.addCell(addCell("地点", font, null, 1));

        table.addCell(addCell("时间", font, null, 1));

        table.addCell(addCell("处理", font, null, 1));

        table.addCell(addCell("后续", font, null, 1));

        table.addCell(addCell("起床", font, null, 3));

        table.addCell(addCell("家里", font, null, 1));

        table.addCell(addCell("早上8点", font, null, 1));

        table.addCell(addCell("懒床10分钟", font, null, 1));

        table.addCell(addCell("晚到公司", font, null, 1));

 

        Paragraph paragraph6 = new Paragraph(SPACE+"文本1:"+SPACE3, fontTital);

        Paragraph paragraph7 = new Paragraph(SPACE+"文本2:"+SPACE3, fontTital);

        Paragraph paragraph8 = new Paragraph(SPACE+"文本3:"+SPACE3, fontTital);

        Paragraph paragraph9 = new Paragraph(SPACE+"文本4:"+SPACE3, fontTital);

        Paragraph paragraph10 = new Paragraph(SPACE+"文本5:"+SPACE3, fontTital);

        paragraph1.setSpacingBefore(470f);

 

 

        try {

 

            mainDoc.add(table);

            mainDoc.add(paragraph6);

            mainDoc.add(paragraph7);

            mainDoc.add(paragraph8);

            mainDoc.add(paragraph9);

            mainDoc.add(paragraph10);

 

        } catch (DocumentException e) {

            log.error(e.getMessage());

 

        }

        mainDoc.newPage();

        PdfPTable table2 = new PdfPTable(7);

        table2.setTotalWidth(540);

        table2.setLockedWidth(true);

        table2.setSpacingBefore(100);

 

        table2.addCell(addCell("事件", font, null, 3));

        table2.addCell(addCell("地点", font, null, 1));

        table2.addCell(addCell("时间", font, null, 1));

        table2.addCell(addCell("注意事项", font, null, 1));

        table2.addCell(addCell("结果", font, null, 1));

        table2.addCell(addCell("郊游", font, null, 3));

        table2.addCell(addCell("野外", font, null, 1));

        table2.addCell(addCell("周日早上8点", font, null, 1));

        table2.addCell(addCell("带上工具", font, null, 1));

        table2.addCell(addCell("玩的很开心", font, null, 1));

 

        mainDoc.add(table2);

        mainDoc.close();

        String pdfArrays[] = {cover,mainPath};

        //合并pdf

        String mergePdf = "mergePdf.pdf";

        String finalPdf = "finalPdf.pdf";

        mergePDF(pdfArrays,DEST,mergePdf);

        //添加页码

        addPageNum(DEST+"/"+mergePdf,DEST+"/"+finalPdf,font);

    }

 

    /**

     * 添加页码

     * @param pdfPath

     * @param outFilePath

     */

    public static void addPageNum(String pdfPath, String outFilePath,Font font) {

        PdfReader reader = null;

        PdfStamper stamper = null;

        try {

            // 创建一个pdf读入流

            reader = new PdfReader(pdfPath);

            // 根据一个pdfreader创建一个pdfStamper.用来生成新的pdf.

            stamper = new PdfStamper(reader, new FileOutputStream(outFilePath));

            // 这个字体是itext-asian.jar中自带的 所以不用考虑操作系统环境问题.

//            BaseFont bf = BaseFont.createFont(PATH_FONT_COUR, BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);

            // baseFont不支持字体样式设定.但是font字体要求操作系统支持此字体会带来移植问题.

//            Font font = new Font(bf, 10);

//            font.setStyle(Font.BOLD);

            font.setColor(146,146,146);

            font.getBaseFont();

            // 获得宽

            Rectangle pageSize = reader.getPageSize(1);

            float width = pageSize.getWidth();

            // 获取页码

            int num = reader.getNumberOfPages();

            for (int i = 1; i <= num; i++) {

                if (i >1){

 

 

                    PdfContentByte over = stamper.getOverContent(i);

 

                    over.beginText();

                    over.setFontAndSize(font.getBaseFont(), 10);

//                over.setColorFill(BaseColor.BLACK);

                    // 设置页码在页面中的坐标

                    over.setTextMatrix((int) width - 100, 30);

//                          over.setTextRenderingMode(1); // 设置字体加粗

                    over.showText("第 " + (i-1) + " 页"+" / "+"共 " +(num-1)+" 页");

                    over.endText();

                    over.stroke();

                }

            }

            stamper.close();

        } catch (Exception e) {

            e.printStackTrace();

        } finally {

            if (reader != null) {

                reader.close();

            }

        }

    }

    /**

     * @param files          源PDF路径

     * @param outputPath     合并后输出的PDF路径

     * @param outputFileName 合并后输出的PDF文件名

     * @author Reverse_XML

     * 将多个PDF合并成一个PDF

     */

    public static void mergePDF(String[] files, String outputPath, String outputFileName) {

        Document document = null;

        PdfCopy copy = null;

        PdfReader reader = null;

        try {

            document = new Document(new PdfReader(files[0]).getPageSize(1));

            copy = new PdfCopy(document, new FileOutputStream(outputPath + "/" + outputFileName));

            document.open();

            for (int i = 0; i < files.length; i++) {

                reader = new PdfReader(files[i]);

                int numberOfPages = reader.getNumberOfPages();

                for (int j = 1; j <= numberOfPages; j++) {

                    document.newPage();

                    PdfImportedPage page = copy.getImportedPage(reader, j);

                    copy.addPage(page);

                }

            }

        } catch (IOException e) {

            log.error(e.getMessage(), e);

        } catch (DocumentException e) {

            log.error(e.getMessage(), e);

        } finally {

            document.close();

            reader.close();

            copy.close();

        }

    }

 

    /**

     * 添加单元格

     *

     * @param val            单元格内容

     * @param font           单元格字体

     * @param backgroudColor 单元格背景颜色

     * @param colSpan        单元格跨列列数

     */

    private static PdfPCell addCell(String val, Font font, BaseColor backgroudColor, int colSpan) {

        String val1 = null == val ? " " : val;

        //新建单元格

        PdfPCell cell = new PdfPCell(new Phrase(val1, font));

        //水平居中

        cell.setHorizontalAlignment(Element.ALIGN_CENTER);

        //单元格高度

        cell.setFixedHeight(20);

        if (backgroudColor != null) {

            //设置背景颜色

            cell.setBackgroundColor(backgroudColor);

        }

 

        if (colSpan != 0) {

            //设置跨列列数

            cell.setColspan(colSpan);

        }

 

//        if (rowSpan != 0) {

//            cell.setRowspan(rowSpan);//设置跨行行数

//        }

 

        return cell;

    }

 

    /**

     * 正文页眉页脚水印

     *

     * @param document

     * @param writer

     * @throws BadElementException

     * @throws IOException

     */

    private  static void creatHeader( Document document, PdfWriter writer ,String signImage ,String footerImage) throws DocumentException, IOException {

//        PdfPTable table = new PdfPTable(new float[]{25, 425});

        PdfPTable table = new PdfPTable(1);

        table.setTotalWidth(475);

 

 

        //页眉图片

        Image image = Image.getInstance(signImage);

        image.setAlignment(Image.ALIGN_CENTER);

        image.scalePercent(30); //依照比例缩放

        //image.setWidthPercentage();

        PdfPCell cell1Imag = new PdfPCell(image);

 

        cell1Imag.setBorder(0);

 

        //页脚图片

        Image imageFotter = Image.getInstance(footerImage);

        imageFotter.setAlignment(Image.ALIGN_CENTER);

        imageFotter.scalePercent(35); //依照比例缩放

        PdfPCell cellImag = new PdfPCell(imageFotter);

//        cellImag.setPaddingTop(-15f);

//        cellImag.setPaddingLeft(-12f);

//        cell1Imag.setPaddingRight(25f);

        cellImag.setBorder(0);

 

//        table.getDefaultCell().setBorder(0);

        table.addCell(cell1Imag);

        table.addCell(cellImag);

//        table.addCell(cell1);

//        table.addCell(cell);

        PdfHeaderFooter event = new PdfHeaderFooter(table);

        writer.setPageEvent(event);

        //页眉页脚

        event.onEndPage(writer, document);

//        event.onEndPage(writer, document,20,20);

 

        Watermark waterEvent = new Watermark("NEW PAGE");

        writer.setPageEvent(waterEvent);

        //水印

        waterEvent.onEndPage(writer, document);

//        event.onEndPage(writer, document,20,20);

 

 

    }

/**

 * PdfHeaderFooter

 *

 * @author

 * @Description pdf生成页眉页脚工具类

 * @Date 2020/12/8 13:51

 */

public class PdfHeaderFooter extends PdfPageEventHelper {

    public static PdfPTable header;

 

    public PdfHeaderFooter(PdfPTable header) {

        PdfHeaderFooter.header = header;

    }

 

 

    /**

     * 页眉

     * @param writer

     * @param document

     */

    @Override

    public void onEndPage(PdfWriter writer, Document document) {

 

//        //页眉

        header.writeSelectedRows( 0, 1, 20, PageSize.A4.getHeight() - 20, writer.getDirectContent());

//        //页脚

        header.writeSelectedRows(1, 2, 20, 80, writer.getDirectContent());

    }

 

}

/**

 * Watermark

 *

 * @author

 * @Description 水印工具类

 * @Date 2021/1/6 10:23

 */

public class Watermark extends PdfPageEventHelper {

 

    BaseFont chinese =BaseFont.createFont("C:/WINDOWS/Fonts/FZSTK.TTF", BaseFont.IDENTITY_H,BaseFont.NOT_EMBEDDED);//本地系统字体

    Font fontTital = new Font(chinese, 10, Font.NORMAL);

 

    private String waterCont;//水印内容

    public Watermark() throws IOException, DocumentException {

 

    }

    public Watermark(String waterCont) throws IOException, DocumentException {

        this.waterCont = waterCont;

    }

 

    @Override

    public void onEndPage(PdfWriter writer, Document document) {

        for(int i=0 ; i<5; i++) {

            for(int j=0; j<5; j++) {

                ColumnText.showTextAligned(writer.getDirectContentUnder(),

                        Element.ALIGN_CENTER,

                        new Phrase(this.waterCont == null ? "HELLO WORLD" : this.waterCont, fontTital),

                        (50.5f+i*350),

                        (40.0f+j*150),

                        writer.getPageNumber() % 2 == 1 ? 45 : -45);

            }

        }

    }

 

}

八、    生成pdf成品展示

本文只做一个粗略的展示,pdf做的略显粗糙,更好的优化还需要读者去自己探索

 

 

 

 

 

 

 

 

 

 

九、    后记

本文采用的是后端代码撰写的方式生成pdf文档,这种方法的主要问题是模板有修改的话后端需要维护代码较多,这种做法比较繁琐

posted @ 2021-01-07 11:22  蔚岚  阅读(3579)  评论(0)    收藏  举报