PDF模板填充,基于IText5

发布时间:2023年12月29日

前言

对于PDF模板填充,有很多现有的Java库,付费版本略过。

较出名的有Apache的PDFBox,以及ITextPdf。

而后者具有两个很大的版本ITextPdf-5和ITextPdf-7,ITextPdf-7功能更强大,但可能存在商业版权问题。之前也用过一阵,没驾驭住。

今天使用?ITextPdf-5,支持文本填充、图片填充 、添加页码 及 PDF文档合并。

依赖引入

<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itextpdf</artifactId>
    <version>5.5.13.3</version>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext-asian</artifactId>
    <version>5.2.0</version>
</dependency>

工具代码

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

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;

/**
 * PDF模板填充,基于itext-pdf-v5
 *
 */
public class PDFTemplateFiller {

    /**
     * 模板
     */
    private final transient PdfReader reader;

    /**
     * 字体
     */
    private BaseFont baseFont;

    /**
     * 添加页码
     */
    private boolean addPageNumber;

    /**
     * 页码字体大小
     * {@link #addPageNumber}为true时有效
     */
    private float pageNumberFontSize = 14F;

    /**
     * 页码左边样式字符
     * {@link #addPageNumber}为true时有效
     */
    private String pageNumberLeft = "";

    /**
     * 页码邮编样式字符
     * {@link #addPageNumber}为true时有效
     */
    private String pageNumberRight = "";

    /**
     * 填充模板,返回填充后文件
     *
     * @param fillData - 待填充数据
     * @return - 填充后文件字节数据
     */
    public byte[] fill(Map<String, Object> fillData) throws DocumentException, IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        PdfStamper stamper = new PdfStamper(reader, out);
        AcroFields form = stamper.getAcroFields();
        // 设置字体
        form.addSubstitutionFont(getDefaultFont());

        // 执行填充
        doFill(stamper, form, fillData);

        // 添加页码
        if (addPageNumber) {
            int pages = reader.getNumberOfPages();
            for (int i = 1; i < pages; i++) {
                addPageNumber(stamper, i);
            }
        }

        // 清除表单域可编辑状态
        stamper.setFormFlattening(true);
        stamper.close();
        return out.toByteArray();
    }

    /**
     * 是否添加页码(默认:否)
     *
     * @param isAddPageNumber - true-添加/false-不添加
     */
    public PDFTemplateFiller pageNumber(boolean isAddPageNumber) {
        this.addPageNumber = isAddPageNumber;
        return this;
    }

    /**
     * <pre>
     * 设置字体(默认:STSong-Light)
     * 通过 {@link com.itextpdf.text.pdf.BaseFont#createFont(java.lang.String, java.lang.String, boolean)}创建
     * </pre>
     *
     * @param baseFont - 待设值字体
     */
    public PDFTemplateFiller font(BaseFont baseFont) {
        this.baseFont = baseFont;
        return this;
    }

    /**
     * 页码样式设置
     * <p>
     * {@link #addPageNumber}为true时有效
     *
     * @param pageNumberLeft  - 页码左边样式字符
     * @param pageNumberRight - 页码邮编样式字符
     */
    public PDFTemplateFiller pageNumberStyle(String pageNumberLeft, String pageNumberRight) {
        this.pageNumberLeft = pageNumberLeft;
        this.pageNumberRight = pageNumberRight;
        return this;
    }

    /**
     * 根据表单域字段名查找其对应所在页码
     *
     * @param form      - 表单对象
     * @param fieldName - 表单域属性名
     * @return - 找到返回具体页码(起始页为1);否则返回-1
     */
    private int findPageNumber(AcroFields form, String fieldName) {
        List<AcroFields.FieldPosition> positions = form.getFieldPositions(fieldName);
        if (positions == null || positions.isEmpty()) {
            return -1;
        }
        return positions.get(0).page;
    }

    /**
     * 填充文本
     */
    private void doFill(PdfStamper stamper, AcroFields form, Map<String, Object> fillData) throws DocumentException, IOException {
        for (Map.Entry<String, Object> entry : fillData.entrySet()) {
            if (entry.getValue() instanceof byte[]) {
                doFillImage(stamper, entry.getKey(), (byte[]) entry.getValue());
            } else {
                form.setField(entry.getKey(), String.valueOf(entry.getValue()));
            }
        }
    }

    /**
     * 填充图片
     */
    private void doFillImage(PdfStamper stamper, String fieldName, byte[] image) throws DocumentException, IOException {
        AcroFields form = stamper.getAcroFields();
        List<AcroFields.FieldPosition> positions = form.getFieldPositions(fieldName);
        if (positions != null && !positions.isEmpty()) {
            AcroFields.FieldPosition position = positions.get(0);
            Rectangle rectangle = position.position;
            com.itextpdf.text.Image img = com.itextpdf.text.Image.getInstance(image);
            // 根据域的大小缩放图片
            img.scaleToFit(rectangle.getWidth(), rectangle.getHeight());
            img.setAbsolutePosition(rectangle.getLeft(), rectangle.getBottom());
            stamper.getOverContent(position.page).addImage(img);
        }
    }

    /**
     * 添加页码
     */
    private void addPageNumber(PdfStamper stamper, int pageNumber) {
        PdfContentByte contentByte = stamper.getOverContent(pageNumber);
        contentByte.beginText();
        contentByte.setFontAndSize(baseFont, pageNumberFontSize);
        Rectangle rectangle = stamper.getReader().getPageSize(pageNumber);
        // 页码的 横轴 坐标 居中
        float x = (rectangle.getLeft() + rectangle.getRight()) / 2;
        contentByte.showTextAligned(Element.ALIGN_CENTER, String.format("%s%d%s", pageNumberLeft, pageNumber, pageNumberRight), x, 20, 0);
        contentByte.endText();
    }

    /**
     * 合并多个PDF文件到一个PDF中
     *
     * @param pdfs - 待合并的PDF 文件
     * @return byte - 合并后的PDF流
     */
    public static byte[] merge(List<PdfReader> pdfs) throws Exception {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Document document = new Document();
        PdfCopy copy = new PdfCopy(document, out);
        document.open();
        for (PdfReader reader : pdfs) {
            // 获取PDF文件总页数
            int n = reader.getNumberOfPages();
            for (int i = 1; i <= n; i++) {
                document.newPage();// 创建新页面
                PdfImportedPage page = copy.getImportedPage(reader, i);
                copy.addPage(page);
            }
        }
        document.close();
        return out.toByteArray();
    }

    public static PDFTemplateFiller load(InputStream pdfTemplate) throws IOException, DocumentException {
        return new PDFTemplateFiller(new PdfReader(pdfTemplate));
    }

    public static PDFTemplateFiller load(File pdfTemplate) throws IOException, DocumentException {
        return new PDFTemplateFiller(new PdfReader(pdfTemplate.getAbsolutePath()));
    }

    public static PDFTemplateFiller load(byte[] pdfTemplate) throws IOException, DocumentException {
        return new PDFTemplateFiller(new PdfReader(pdfTemplate));
    }

    public static PDFTemplateFiller load(ByteArrayOutputStream pdfTemplate) throws IOException, DocumentException {
        return load(pdfTemplate.toByteArray());
    }

    private PDFTemplateFiller(PdfReader pdfReader) throws DocumentException, IOException {
        this.reader = pdfReader;
        this.baseFont = getDefaultFont();
        this.addPageNumber = false;
    }

    /**
     * 获取默认的字体(宋体)
     */
    private BaseFont getDefaultFont() throws DocumentException, IOException {
        return BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
    }
}

后续

动态表格填充实现较为困难,知道的大佬欢迎骚扰(⊙o⊙)…

文章来源:https://blog.csdn.net/gengzhy/article/details/135295311
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。