java 文字生成pdf,并创建自定义表单域pdf模板

  • Post author:
  • Post category:java



目录


本文总共知识点:


pom


所有的import


生成带表格的pdf


另一种方式是指定坐标生成文本域


main方法:


创建表单域做为pdf模板:


创建签名域:


根据文字获取坐标位置


完整代码:



本文总共知识点


1.生成带表格的pdf文件

2.创建文本域

3.创建签名域

4.获取指定文字坐标位置并在对应位置添加签名域。

本文比较长,可直接搜索自己需要的功能位置。

可以将font 拿出来设置成static的,可以节省很多速度。

本文缺点:

1. 同一个线程中连续创建两个及以上pdf会报错


生成带表格的pdf参考




java处理pdf文件——iText的使用_xiaomifeng1010的博客-CSDN博客_java处理pdf文件


PDF(Portable Document Format的简称,意为“便携式文档格式”),是由Adobe Systems用于与应用程序、操作系统、硬件无关的方式进行文件交换所发展出的文件格式。PDF文件以PostScript语言图象模型为基础,无论在哪种打印机上都可保证精确的颜色和准确的打印效果,即PDF会忠实地再现原稿的每一个字符、颜色以及图象。(来自百度百科的介绍)。可以用来…



https://blog.csdn.net/u011174699/article/details/105903929


pom

<dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.13.1</version>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.1.8</version>
        </dependency>
        <dependency>
            <groupId>org.apache.pdfbox</groupId>
            <artifactId>pdfbox</artifactId>
            <version>2.0.19</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.commons</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext-asian</artifactId>
            <version>5.2.0</version>
        </dependency>

所有的import

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.colors.DeviceGray;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.HorizontalAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.layout.property.VerticalAlignment;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontFactory;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

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

import java.io.File;
import java.io.IOException;
import java.util.Map;
import java.io.*;
import java.util.*;

生成带表格的pdf



public static void createTablePdfFile(String savePath, String fontPath, List<String[]> tableData, String title, int width){
        try{
            File pdfFile=new File(savePath);
            if(!pdfFile.exists()){
                pdfFile.getParentFile().mkdir();
            }
            //            PdfFont pdfFont= PdfFontFactory.createFont(StandardFonts.HELVETICA);
//            设置中文字体为黑体常规(传入一个字体路径或者itext中内置支持的字体,如上)
            PdfFont documentFont=PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H,true);
//            使用font-asian中的字体,设置字体华文宋体(STSong-Light)
            PdfFont tableFont=PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H",true);
            com.itextpdf.kernel.pdf.PdfDocument pdfDocument=new com.itextpdf.kernel.pdf.PdfDocument(new com.itextpdf.kernel.pdf.PdfWriter(pdfFile));
            com.itextpdf.layout.Document document=new com.itextpdf.layout.Document(pdfDocument);
//            设置文本居中对齐
            document.setTextAlignment(TextAlignment.CENTER);
            document.setHorizontalAlignment(HorizontalAlignment.CENTER);
//            设置文档字体
            document.setFont(documentFont);
//            表格标题
            Paragraph tableTitle=new Paragraph(title);
//            设置表格标题字体和颜色(段落)
            tableTitle.setFont(documentFont).setFontColor(DeviceGray.BLACK);
            document.add(tableTitle);
            Table table=new Table(UnitValue.createPercentArray(tableData.get(0).length)).useAllAvailableWidth();
//            设置表格字体
            table.setFont(tableFont);
//            设置表格列宽
            table.setWidth(width);
//            设置表格背景色
//            table.setBackgroundColor(ColorConstants.LIGHT_GRAY);
//            设置表格文本居中对齐
            table.setTextAlignment(TextAlignment.CENTER);
//            设置表格垂直对齐方式
            table.setVerticalAlignment(VerticalAlignment.MIDDLE);
//            设置表格水平对齐方式
            table.setHorizontalAlignment(HorizontalAlignment.CENTER);
//            Cell cell=new Cell();
//            设置单元格背景色为深灰色
//            cell.setBackgroundColor(ColorConstants.DARK_GRAY);


            for (String[] row :
                    tableData) {
//                更精确的颜色可以使用new DeviceRgb()传入三原色的数值[0-255]来设置
//                new DeviceRgb(13,23,5);
                for (String cell : row) {
                    table.addCell(new Paragraph(cell).setFontColor(DeviceRgb.BLACK));
                }
            }
            UnitValue height = table.getHeight();
            document.add(table);
            pdfDocument.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

另一种方式是指定坐标生成文本域

    public static void createPdfFileTest(String savePath, String fontPath){

//        创建文档页面(设置为矩形A4大小)
        PDPage page = new PDPage(PDRectangle.A4);
//        创建pdf文档对象,以及页面内容流对象
        try {
            File pdfFile=new File(savePath);
            if(!pdfFile.exists()){
                pdfFile.getParentFile().mkdir();
            }
            PdfFont pdfFont = PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H, true);
            PdfFont tableFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H",true);

            PDDocument pdfDocument=new PDDocument();
            PDPageContentStream pageContentStream=new PDPageContentStream(pdfDocument,page);
            pdfDocument.addPage(page);
//        设置字体为Helvetica-Bold或TIMES_ROMAN;,类库中没有中文字体,程序会去查找本地是否安装了该字体,查找非常慢
            PDFont font= PDType1Font.TIMES_ROMAN;
//            开始页面的文本操作
            pageContentStream.beginText();
            pageContentStream.setFont(font,12);
            pageContentStream.moveTextPositionByAmount(100,700);
//            因为jar包中自带的没有中文字体,所以写入中文会出异常
            pageContentStream.showText("hello");

//            结束页面文本操作
            pageContentStream.endText();
            pageContentStream.close();

//            pdf文档保存在本项目所在磁盘下data目录下
            pdfDocument.save(pdfFile);
            System.out.println("文件写入成功");

        } catch (Exception e) {
            e.printStackTrace();
        }finally{
//            流虽然都关闭了,但是还是无法删除文件,所以手动调用一下gc
            System.gc();
            System.out.println("执行垃圾回收");
        }
    }

main方法:

public static void main(String[] args) {

        FileOutputStream out= null;
        PdfReader reader=null;
        PdfStamper pdfStamper=null;
        try {
            String createFile = "K:\\create.pdf";
            String fontPath = "c:\\Windows\\fonts\\simhei.ttf";
            long start = System.currentTimeMillis();
            String title = "人员信息表";

            List<String[]> tableData = new ArrayList<>();
            tableData.add(new String[]{"交易类型", "时间", "金额"});
            tableData.add(new String[]{"放款", "2022/10/11 18:23:22", "28432.00"});
            tableData.add(new String[]{"还款", "2022/10/13 18:23:22", "11.00"});
            tableData.add(new String[]{"还款", "2022/10/13 18:23:22", "12.00"});
            tableData.add(new String[]{"还款", "2022/10/13 18:23:22", "52.00"});
            tableData.add(new String[]{"罚息", "2022/10/15 18:23:22", "11111.00"});
            createTablePdfFile(createFile, fontPath, tableData, title, 500);
//            createPdfFileTest(createFile, fontPath);
            long end = System.currentTimeMillis() - start;
            System.out.println(end);
            System.out.println(tableData.size());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

效果:

创建表单域做为pdf模板:

带有表单域填充的pdf,使用edge 打开会显示不出来填充的值,这里要特别注意下。

    /**
     * 创建表单文本域
     * @author dongdong
     * @date 2022/4/27
     * @param float llx, float lly, float urx, float ury 前面两个参数代表第一个点的 xy 坐标,后面两个参数代表第二个点的 xy 坐标值
     * @return
     */
    public static void createTextFieldByDefine(String readFile, String saveFile, float llx, float lly, float urx, float ury) throws IOException, DocumentException{
        FileOutputStream out = new FileOutputStream(new File(saveFile));
        PdfReader reader=new PdfReader(new FileInputStream(new File(readFile)));
        PdfStamper pdfStamper=new PdfStamper(reader,out);

        PdfWriter writer = pdfStamper.getWriter();

        float height = reader.getPageSize(reader.getNumberOfPages()).getHeight();
        float width = reader.getPageSize(reader.getNumberOfPages()).getWidth();

        PdfFormField form = PdfFormField.createEmpty(writer);
        //普通文本框
        TextField field = new TextField(writer, new Rectangle(llx, lly, urx, ury), "text");
        field.setOptions(TextField.MULTILINE);
        //防止读取pdf文档时,就是有旋转角度的
        field.setRotation(reader.getPageRotation(1));
        //有些状况下,页数问题能够在这里设置,优先级最高
        field.getTextField().setPlaceInPage(1);
        form.addKid(field.getTextField());
        // file.setOptions(TextField.VISIBLE);//文本域可见(相对于文本域是否高亮)
        // file.setOptions(TextField.HIDDEN);//文本域不可见
        // file.setOptions(TextField.VISIBLE_BUT_DOES_NOT_PRINT);//该字段可见,但不打印。
        // file.setOptions(TextField.HIDDEN_BUT_PRINTABLE);//该字段不可见,但不打印。
        // file.setOptions(TextField.HIDDEN_BUT_PRINTABLE);//该字段不可见,但不打印。
        // file.setOptions(TextField.READ_ONLY);//只读
        // file.setOptions(TextField.REQUIRED);//该字段在经过提交表单操做导出时必须具备值。
        // file.setOptions(TextField.MULTILINE);//规定区域内能够换行显示
        // file.setOptions(TextField.DO_NOT_SCROLL);//文本域不会有滚动条,对于单行字段为水平,对于多行字段为垂直,一旦区域满了,将不会再接受任何文字。
        // file.setOptions(TextField.PASSWORD);//该字段用于输入安全密码,该密码不该该被可视地显示在屏幕上。
        // file.setOptions(TextField.FILE_SELECTION);//我的理解好像是上传文件,不是很理解
        // file.setOptions(TextField.DO_NOT_SPELL_CHECK);//无拼写检查
        // file.setOptions(TextField.EDIT);//若是设置组合框包括一个可编辑的文本框以及一个下拉列表;若是清楚,它只包括一个下拉列表。这个标志只对组合字段有意义。
        // file.setOptions(TextField.MULTISELECT);//无论列表是否能够有多个选择。仅适用于/ ch列表字段,而不适用于组合框。
        // file.setOptions(TextField.COMB);//组合框标志。

        pdfStamper.addAnnotation(form, reader.getNumberOfPages());
        // 设置表单域可被编辑,用来做为后面可以填充的模板
        pdfStamper.setFormFlattening(false);

        pdfStamper.close();
        out.close();
    }

测试方法:

    public static void main(String[] args) {

        FileOutputStream out= null;
        PdfReader reader=null;
        PdfStamper pdfStamper=null;
        try {
            String createFile = "K:\\resources\\META-INF\\create.pdf";
            String fontPath = "K:\\resources\\META-INF\\simhei.ttf";
            long start = System.currentTimeMillis();
            String title = "人员信息表";

            List<String[]> tableData = new ArrayList<>();
            tableData.add(new String[]{"交易类型", "时间", "金额"});
            tableData.add(new String[]{"放款", "2022/10/11 18:23:22", "28432.00"});
            tableData.add(new String[]{"还款", "2022/10/13 18:23:22", "11.00"});

            tableData.add(new String[]{"罚息", "2022/10/15 18:23:22", "11111.00"});
            createTablePdfFile(createFile, fontPath, tableData, title, 500);
//            createPdfFileTest(createFile, fontPath);
            long end = System.currentTimeMillis() - start;
            System.out.println(end);
            System.out.println(tableData.size());

            String saveFile = "K:\\resources\\META-INF\\createFile2.pdf";
            createTextFieldByDefine(createFile, saveFile, 400, 300, 500, 400);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

坐标的话是从页面的左下角往右下角画的,这里有点不是特别的好。

效果:

创建签名域:

    public static void fillTemplate(String readFile, String savePdfFile) {
        PdfReader reader;
        FileOutputStream out;
        PdfStamper ps;

        try {
            out = new FileOutputStream(savePdfFile);
            reader = new PdfReader(new FileInputStream(new File(readFile)));//源文件
            ps  = new PdfStamper(reader, out);
            // 创建数组签名域  (因为添加两个,所以搞了两个坐标系)
            int x = 210, y = 437, width = 200, height = 100; // 坐标系远点位于页面左下角,左下角到右下角为  x 轴,左下角到左上角为 y 轴
            Rectangle areaSignatureRect = new Rectangle(// 签名域区域,由两个对角点构成的矩形区域
                    x, // 点1 x坐标 左边距
                    y, // 点1 y坐标 上边距
                    x+width,// 点2 x坐标, 这个最好是左边距+宽,好调点。
                    y+height // 点2 y坐标, 同样这个最好是上边距+高,好调点。(其实我也懵懵懂.)
            );
            int pageNo = reader.getNumberOfPages(); // PDF 文件的页码从 1 开始,而不是 0。(这个就是你要在哪个页面添加签名域,我的是第五页)
            PdfFormField pdfFormField = PdfFormField.createSignature(ps.getWriter());
            pdfFormField.setFieldName("AREA_SIGNATURE"); // 签名域标识
            pdfFormField.setPage(pageNo);
            pdfFormField.setWidget(areaSignatureRect, PdfAnnotation.HIGHLIGHT_OUTLINE); // 高亮显示


            ps.setFormFlattening(true);// 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑
            ps.addAnnotation(pdfFormField, pageNo);
            System.err.println("生成pdf文件完成~~~~~~~~~~");
            ps.close();//必须要关闭,要不然生成的PDF会是0KB(此代码大部分都是网上摘抄的,以为能用来着。没写这个,让我找大半天)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

效果:

根据文字获取坐标位置

public static float[] getPdfPositionByText(String readPdfFile, String outPdfFile, String keyWords){
        PdfReader reader = null;
        FileOutputStream fileOutputStream = null;
        PdfStamper stamper = null;
        try {
            reader = new PdfReader(readPdfFile);
            fileOutputStream = new FileOutputStream(outPdfFile);
            //新建一个PDF解析对象
            PdfReaderContentParser parser = new PdfReaderContentParser(reader);

            //包含了PDF页面的信息,作为处理的对象
            stamper = new PdfStamper(reader, fileOutputStream);

            float[] coordinate = null;
            int page = 0;
            try{
                int pageNum = reader.getNumberOfPages();
                PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(reader);
                CustomRenderListener renderListener = new CustomRenderListener();
                renderListener.setKeyWord(keyWords);
                for (page = 1; page <= pageNum; page++) {
                    renderListener.setPage(page);
                    pdfReaderContentParser.processContent(page, renderListener);
                    coordinate = renderListener.getPcoordinate();
                    if (coordinate != null) break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return coordinate;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

需要用到一个监听类

import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;

/**
 * @date 2022/4/27 18:18
 * 说明
 */
public class CustomRenderListener implements RenderListener {
    private float[] pcoordinate = null;

    private String keyWord;

    private int page;

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public float[] getPcoordinate(){
        return pcoordinate;
    }

    public String getKeyWord() {
        return keyWord;
    }

    public void setKeyWord(String keyWord) {
        this.keyWord = keyWord;
    }

    @Override
    public void beginTextBlock() {

    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) {

    }

    @Override
    public void endTextBlock() {

    }

    @Override
    public void renderText(TextRenderInfo textRenderInfo) {
        String text = textRenderInfo.getText();
        if (null != text && text.contains(keyWord)) {
            Rectangle2D.Float boundingRectange = textRenderInfo.getBaseline().getBoundingRectange();
            pcoordinate = new float[3];
            pcoordinate[0] = boundingRectange.x;
            pcoordinate[1] = boundingRectange.y;
            pcoordinate[2] = page;
        }
    }
}

最后优化了一下根据文字坐标添加签名域的方法,大体代码同上面添加签名域的,就不重新贴了

方法增加参数上面搜索到的文字坐标

int x = (int)keyWordPosition[0],
                    y = (int)keyWordPosition[1] - 100,
                    width = 200, height = 100;

然后这个坐标是从左下角开始查找的,这里注意下。

效果:

完整代码:



import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.colors.DeviceGray;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.element.Table;
import com.itextpdf.layout.property.HorizontalAlignment;
import com.itextpdf.layout.property.TextAlignment;
import com.itextpdf.layout.property.UnitValue;
import com.itextpdf.layout.property.VerticalAlignment;
import com.itextpdf.text.Document;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.PdfReaderContentParser;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.font.PDFontFactory;
import org.apache.pdfbox.pdmodel.font.PDType1Font;

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

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Map;
import java.io.*;
import java.util.*;

/**
 * @date 2022/4/26 15:10
 * 说明
 */
public class PdfCreatedByText {

    /**
     * 创建表单文本域
     * @date 2022/4/27
     * @param float llx, float lly, float urx, float ury 前面两个参数代表第一个点的 xy 坐标,后面两个参数代表第二个点的 xy 坐标值
     * @return
     */
    public static void createTextFieldByDefine(PdfReader reader, PdfStamper pdfStamper, float llx, float lly, float urx, float ury) throws IOException, DocumentException{

        PdfWriter writer = pdfStamper.getWriter();

        PdfFormField form = PdfFormField.createEmpty(writer);
        //普通文本框
        TextField field = new TextField(writer, new Rectangle(llx, lly, urx, ury), "text");
        field.setOptions(TextField.MULTILINE);
        //防止读取pdf文档时,就是有旋转角度的
        field.setRotation(reader.getPageRotation(1));
        //有些状况下,页数问题能够在这里设置,优先级最高
        field.getTextField().setPlaceInPage(1);
        form.addKid(field.getTextField());
        // file.setOptions(TextField.VISIBLE);//文本域可见(相对于文本域是否高亮)
        // file.setOptions(TextField.HIDDEN);//文本域不可见
        // file.setOptions(TextField.VISIBLE_BUT_DOES_NOT_PRINT);//该字段可见,但不打印。
        // file.setOptions(TextField.HIDDEN_BUT_PRINTABLE);//该字段不可见,但不打印。
        // file.setOptions(TextField.HIDDEN_BUT_PRINTABLE);//该字段不可见,但不打印。
        // file.setOptions(TextField.READ_ONLY);//只读
        // file.setOptions(TextField.REQUIRED);//该字段在经过提交表单操做导出时必须具备值。
        // file.setOptions(TextField.MULTILINE);//规定区域内能够换行显示
        // file.setOptions(TextField.DO_NOT_SCROLL);//文本域不会有滚动条,对于单行字段为水平,对于多行字段为垂直,一旦区域满了,将不会再接受任何文字。
        // file.setOptions(TextField.PASSWORD);//该字段用于输入安全密码,该密码不该该被可视地显示在屏幕上。
        // file.setOptions(TextField.FILE_SELECTION);//我的理解好像是上传文件,不是很理解
        // file.setOptions(TextField.DO_NOT_SPELL_CHECK);//无拼写检查
        // file.setOptions(TextField.EDIT);//若是设置组合框包括一个可编辑的文本框以及一个下拉列表;若是清楚,它只包括一个下拉列表。这个标志只对组合字段有意义。
        // file.setOptions(TextField.MULTISELECT);//无论列表是否能够有多个选择。仅适用于/ ch列表字段,而不适用于组合框。
        // file.setOptions(TextField.COMB);//组合框标志。

        pdfStamper.addAnnotation(form, reader.getNumberOfPages());
        // 设置表单域可被编辑,用来做为后面可以填充的模板
        pdfStamper.setFormFlattening(false);

        pdfStamper.close();
    }

    /**
     * @param fields
     * @param data
     * @throws IOException
     * @throws DocumentException
     */
    private static void fillData(AcroFields fields, Map<String, String> data) throws IOException, DocumentException {
        Map<String, AcroFields.Item> formFields = fields.getFields();
        for (String key : data.keySet()) {
            if(formFields.containsKey(key)){
                String value = data.get(key);
                fields.setField(key, value); // 为字段赋值,注意字段名称是区分大小写的
            }
        }
    }

    // 生成表格
    public static void createPdfTextFile(String savePath){

//        创建文档页面(设置为矩形A4大小)
        PDPage page = new PDPage(PDRectangle.A4);
//        创建pdf文档对象,以及页面内容流对象
        try {
            File pdfFile=new File(savePath);
            if(!pdfFile.exists()){
                pdfFile.getParentFile().mkdir();
            }
            PDDocument pdfDocument=new PDDocument();
            PDPageContentStream pageContentStream=new PDPageContentStream(pdfDocument,page);
            pdfDocument.addPage(page);
//        设置字体为Helvetica-Bold或TIMES_ROMAN;,类库中没有中文字体,程序会去查找本地是否安装了该字体,查找非常慢
            PDFont font= PDType1Font.TIMES_ROMAN;
//            开始页面的文本操作
            pageContentStream.beginText();
            pageContentStream.setFont(font,12);
            pageContentStream.moveTextPositionByAmount(100,700);
//            因为jar包中自带的没有中文字体,所以写入中文会出异常
            pageContentStream.showText("hello");

//            结束页面文本操作
            pageContentStream.endText();
            pageContentStream.close();

//            pdf文档保存在本项目所在磁盘下data目录下
            pdfDocument.save(pdfFile);
            System.out.println("文件写入成功");
            pdfDocument.close();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
//            流虽然都关闭了,但是还是无法删除文件,所以手动调用一下gc
            System.gc();
            System.out.println("执行垃圾回收");
        }
    }

    public static float[] getPdfPositionByText(PdfReader reader, String keyWords){
        try {

            float[] coordinate = null;
            int page = 0;
            try{
                int pageNum = reader.getNumberOfPages();
                //新建一个PDF解析对象
                PdfReaderContentParser pdfReaderContentParser = new PdfReaderContentParser(reader);
                CustomRenderListener renderListener = new CustomRenderListener();
                renderListener.setKeyWord(keyWords);
                for (page = 1; page <= pageNum; page++) {
                    renderListener.setPage(page);
                    //包含了PDF页面的信息,作为处理的对象
                    pdfReaderContentParser.processContent(page, renderListener);
                    coordinate = renderListener.getPcoordinate();
                    if (coordinate != null) break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            return coordinate;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new float[3];
    }
    /**
     * 生成带表格的pdf文件
     * @date 2022/4/27
     * @param  tableWidth 表格宽度
     * @param  tableData 填充的数据,二维数据
     * @return
     */
    public static void createTablePdfFile(File pdfFile, String fontPath, List<String[]> tableData, String title, int tableWidth){
        try{

            //            PdfFont pdfFont= PdfFontFactory.createFont(StandardFonts.HELVETICA);
//            设置中文字体为黑体常规(传入一个字体路径或者itext中内置支持的字体,如上)
            PdfFont documentFont=PdfFontFactory.createFont(fontPath, PdfEncodings.IDENTITY_H,true);
//            使用font-asian中的字体,设置字体华文宋体(STSong-Light)
            PdfFont tableFont=PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H",true);
            com.itextpdf.kernel.pdf.PdfWriter pdfWriter = new com.itextpdf.kernel.pdf.PdfWriter(pdfFile);
            com.itextpdf.kernel.pdf.PdfDocument pdfDocument=new com.itextpdf.kernel.pdf.PdfDocument(pdfWriter);
            com.itextpdf.layout.Document document=new com.itextpdf.layout.Document(pdfDocument);
//            设置文本居中对齐
            document.setTextAlignment(TextAlignment.CENTER);
            document.setHorizontalAlignment(HorizontalAlignment.CENTER);
//            设置文档字体
            document.setFont(documentFont);
//            表格标题
            Paragraph tableTitle=new Paragraph(title);
//            设置表格标题字体和颜色(段落)
            tableTitle.setFont(documentFont).setFontColor(DeviceGray.BLACK);
            document.add(tableTitle);
            Table table=new Table(UnitValue.createPercentArray(tableData.get(0).length)).useAllAvailableWidth();
//            设置表格字体
            table.setFont(tableFont);
//            设置表格列宽
            table.setWidth(tableWidth);
//            设置表格背景色
//            table.setBackgroundColor(ColorConstants.LIGHT_GRAY);
//            设置表格文本居中对齐
            table.setTextAlignment(TextAlignment.CENTER);
//            设置表格垂直对齐方式
            table.setVerticalAlignment(VerticalAlignment.MIDDLE);
//            设置表格水平对齐方式
            table.setHorizontalAlignment(HorizontalAlignment.CENTER);
//            Cell cell=new Cell();
//            设置单元格背景色为深灰色
//            cell.setBackgroundColor(ColorConstants.DARK_GRAY);

            for (String[] row : tableData) {
//                更精确的颜色可以使用new DeviceRgb()传入三原色的数值[0-255]来设置
//                new DeviceRgb(13,23,5);
                for (String cell : row) {
                    table.addCell(new Paragraph(cell).setFontColor(DeviceRgb.BLACK));
                }
            }
            document.add(table);
            document.close();
            pdfDocument.close();
            pdfWriter.flush();
            pdfWriter.close();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void fillTemplate(PdfReader reader, PdfStamper ps, float[] keyWordPosition) {
        try {
            // 创建数组签名域  (因为添加两个,所以搞了两个坐标系)
            int x = (int)keyWordPosition[0],
                    y = (int)keyWordPosition[1] - 100,
                    width = 200, height = 100; // 坐标系远点位于页面左下角,左下角到右下角为  x 轴,左下角到左上角为 y 轴
//            int x = 210, y = 437, width = 200, height = 100; // 坐标系远点位于页面左下角,左下角到右下角为  x 轴,左下角到左上角为 y 轴
            Rectangle areaSignatureRect = new Rectangle(// 签名域区域,由两个对角点构成的矩形区域
                    x, // 点1 x坐标 左边距
                    y, // 点1 y坐标 上边距
                    x+width,// 点2 x坐标, 这个最好是左边距+宽,好调点。
                    y+height // 点2 y坐标, 同样这个最好是上边距+高,好调点。(其实我也懵懵懂.)
            );
            int pageNo = reader.getNumberOfPages(); // PDF 文件的页码从 1 开始,而不是 0。(这个就是你要在哪个页面添加签名域,我的是第五页)
            PdfFormField pdfFormField = PdfFormField.createSignature(ps.getWriter());
            pdfFormField.setFieldName("AREA_SIGNATURE"); // 签名域标识
            pdfFormField.setPage(pageNo);
            pdfFormField.setWidget(areaSignatureRect, PdfAnnotation.HIGHLIGHT_OUTLINE); // 高亮显示


            ps.setFormFlattening(true);// 如果为false,生成的PDF文件可以编辑,如果为true,生成的PDF文件不可以编辑
            ps.addAnnotation(pdfFormField, pageNo);
            System.err.println("生成pdf文件完成~~~~~~~~~~");
            ps.close();//必须要关闭,要不然生成的PDF会是0KB(此代码大部分都是网上摘抄的,以为能用来着。没写这个,让我找大半天)
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        try {
            // 必须是两个不同的文件
            String createFilePath = "K:\\resources\\META-INF\\create.pdf";
            String saveFile = "K:\\resources\\META-INF\\create2.pdf";
            String fontPath = "K:\\resources\\META-INF\\simhei.ttf";

            long start = System.currentTimeMillis();
            String title = "人员信息表";

            List<String[]> tableData = new ArrayList<>();
            tableData.add(new String[]{"交易类型", "时间", "金额"});
            tableData.add(new String[]{"放款", "2022/10/11 18:23:22", "28432.00"});
            tableData.add(new String[]{"还款", "2022/10/13 18:23:22", "11.00"});

            tableData.add(new String[]{"还款", "2022/10/13 18:23:22", "52.00"});
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:dd");
            tableData.add(new String[]{"罚息", simpleDateFormat.format(new Date()), "11111.00"});

            File pdfFile=new File(saveFile);
            if(!pdfFile.exists()){
                pdfFile.getParentFile().mkdir();
                pdfFile.createNewFile();
            }
            File createFile = new File(createFilePath);
            if(!createFile.exists()){
                createFile.getParentFile().mkdir();
                createFile.createNewFile();
            }
            // 先生成 create2 文件
            createTablePdfFile(pdfFile, fontPath, tableData, title, 500);

            // 读取  create2 文件,并写入 create 文件
             FileOutputStream out = new FileOutputStream(createFilePath);
             PdfReader reader =new PdfReader(new FileInputStream(pdfFile));
             PdfStamper pdfStamper =new PdfStamper(reader,out);

 // 生成文件测试
 //            createPdftextFile(createFile);
 //            createTextFieldByDefine(createFile, saveFile, 400, 300, 500, 400);

             float[] keyWordPosition = getPdfPositionByText(reader, "罚息");
             fillTemplate(reader, pdfStamper, keyWordPosition);
            long end = System.currentTimeMillis() - start;
            System.out.println(end);
            System.out.println(tableData.size());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
CustomRenderListener


import com.itextpdf.awt.geom.Rectangle2D;
import com.itextpdf.text.pdf.parser.ImageRenderInfo;
import com.itextpdf.text.pdf.parser.RenderListener;
import com.itextpdf.text.pdf.parser.TextRenderInfo;

/**
 * @date 2022/4/27 18:18
 * 说明
 */
public class CustomRenderListener implements RenderListener {
    private float[] pcoordinate = null;

    private String keyWord;

    private int page;

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public float[] getPcoordinate(){
        return pcoordinate;
    }

    public String getKeyWord() {
        return keyWord;
    }

    public void setKeyWord(String keyWord) {
        this.keyWord = keyWord;
    }

    @Override
    public void beginTextBlock() {

    }

    @Override
    public void renderImage(ImageRenderInfo renderInfo) {

    }

    @Override
    public void endTextBlock() {

    }

    @Override
    public void renderText(TextRenderInfo textRenderInfo) {
        String text = textRenderInfo.getText();
        if (null != text && text.contains(keyWord)) {
            Rectangle2D.Float boundingRectange = textRenderInfo.getBaseline().getBoundingRectange();
            pcoordinate = new float[3];
            pcoordinate[0] = boundingRectange.x;
            pcoordinate[1] = boundingRectange.y;
            pcoordinate[2] = page;
        }
    }
}

最终效果:



版权声明:本文为dandanforgetlove原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。