基于IText7 PDF模板填充?

引入依赖

XML 复制代码
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>itext7-core</artifactId>
    <version>8.0.1</version>
    <type>pom</type>
</dependency>
<dependency>
    <groupId>com.itextpdf</groupId>
    <artifactId>bouncy-castle-adapter</artifactId>
    <version>8.0.1</version>
</dependency>

模板填充工具

java 复制代码
import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormCreator;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.*;
import com.itextpdf.kernel.pdf.annot.PdfAnnotation;
import com.itextpdf.kernel.pdf.annot.PdfWidgetAnnotation;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.muchenx.util.ImageCompressUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * PDF 模板填充工具类(itext7)
 * <p>
 * 支持文本 及 图片 填充
 */
@Slf4j
public final class PDFTemplateFillHandler {

    /**
     * 待填充的PDF模板文档
     */
    private final transient PdfDocument templateDocument;

    /**
     * 填充后PDF文档数据
     */
    private final transient ByteArrayOutputStream destByteOutStream;

    /**
     * 字体
     */
    private transient PdfFont font;

    private PDFTemplateFillHandler(PdfDocument templateDocument, ByteArrayOutputStream destByteOutStream) {
        this.templateDocument = templateDocument;
        this.destByteOutStream = destByteOutStream;
        try {
            this.font = loadFont();
        } catch (IOException e) {
            log.warn("加载指定字体异常:{}", e.getMessage());
        }
    }

    public static PDFTemplateFillHandler loadTemplate(InputStream templateFileStream) {
        try {
            ByteArrayOutputStream dest = new ByteArrayOutputStream();
            PdfDocument pdfDocument = new PdfDocument(new PdfReader(templateFileStream), new PdfWriter(dest));
            return new PDFTemplateFillHandler(pdfDocument, dest);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 填充及返回填充后的数据
     *
     * @param fillData - 待填充数据
     * @return - 填充后bytes数据
     */
    public byte[] fill(Map<String, Object> fillData) {
        try {
            PdfAcroForm form = PdfAcroForm.getAcroForm(templateDocument, true);
            fillData.forEach((keyword, value) -> {
                Optional.ofNullable(form.getField(keyword)).ifPresent(templateFormField -> {
                    if (value instanceof byte[]) {
                        PdfArray pos = templateFormField.getWidgets().get(0).getRectangle();
                        float x = pos.getAsNumber(0).floatValue();
                        float y = pos.getAsNumber(1).floatValue();
                        float width = pos.getAsNumber(2).floatValue() - x;
                        float height = pos.getAsNumber(3).floatValue() - y;
                        Rectangle rectangle = new Rectangle(x, y, width, height);
                        PdfWidgetAnnotation widget = new PdfWidgetAnnotation(rectangle);
                        PdfFormField formField = PdfFormCreator.createFormField(widget, templateDocument);
                        PdfPage annotationPage = findAnnotationPage(keyword);
                        if (annotationPage != null) {
                            doFillFieldImage(annotationPage, formField, (byte[]) value);
                        }
                    } else {
                        templateFormField.setValue(String.valueOf(value));
                        if (font != null) {
                            templateFormField.setFont(templateFormField.getFont());
                        }
                    }
                    form.partialFormFlattening(keyword);
                });
            });
            form.flattenFields();
        } finally {
            if (templateDocument != null) {
                templateDocument.close();
            }
        }
        return destByteOutStream.toByteArray();
    }

    /**
     * 图片填充
     *
     * @param newPage   - 当前页
     * @param formField - 表单文本域
     * @param imgBytes  - 图片文件字节数组
     */
    private void doFillFieldImage(PdfPage newPage, PdfFormField formField, byte[] imgBytes) {
        Rectangle rtl = formField.getWidgets().get(0).getRectangle().toRectangle(); // 获取表单域的xy坐标
        PdfCanvas canvas = new PdfCanvas(newPage);
        ImageData img = ImageDataFactory.create(imgBytes);
        if (Float.compare(img.getWidth(), rtl.getWidth()) <= 0 && Float.compare(img.getHeight(), rtl.getHeight()) <= 0) {// 不处理
            canvas.addImageAt(img, rtl.getX(), rtl.getY(), true);
        } else {
            // 压缩图片。计算得到图片放缩的最大比例
            float scale = Math.max(img.getWidth() / rtl.getWidth(), img.getHeight() / rtl.getHeight());
            int imgWidth = Math.round(img.getWidth() / scale);
            int imgHeight = Math.round(img.getHeight() / scale);
            // 压缩图片
            byte[] compressImgBytes;
            try {
                compressImgBytes = ImageCompressUtils.resizeByThumbnails(imgBytes, imgWidth, imgHeight);
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            img = ImageDataFactory.create(compressImgBytes);
            canvas.addImageAt(img, rtl.getX(), rtl.getY(), true);
        }
    }

    /**
     * 根据表单域关键字查找当前关键字所在页对象(PdfPage)
     *
     * @param keyword - 关键字
     * @return - page object
     */
    private PdfPage findAnnotationPage(String keyword) {
        int pages = templateDocument.getNumberOfPages();
        for (int index = 1; index <= pages; index++) {
            PdfPage page = templateDocument.getPage(index);
            for (PdfAnnotation annotation : page.getAnnotations()) {
                PdfString title = annotation.getPdfObject().getAsString(PdfName.T);
                if (title != null && keyword.equals(String.valueOf(title))) {
                    return page;
                }
            }
        }
        return null;
    }

    /**
     * 获取模板文件表单域关键字位置信息
     */
    private Map<Integer, Map<String, float[]>> getFormKeywordsPos() {
        int pages = templateDocument.getNumberOfPages();
        Map<Integer, Map<String, float[]>> maps = new HashMap<>(pages);
        for (int index = 1; index <= pages; index++) {
            maps.putIfAbsent(index, new HashMap<>());
            PdfPage page = templateDocument.getPage(index);
            // 获取当前页的表单域
            int finalIndex = index;
            page.getAnnotations().forEach(anno -> {
                PdfString title = anno.getTitle();
                PdfArray rectangle = anno.getRectangle();
                float x = rectangle.getAsNumber(0).floatValue();
                float y = rectangle.getAsNumber(1).floatValue();
                float width = rectangle.getAsNumber(2).floatValue() - x;
                float height = rectangle.getAsNumber(3).floatValue() - y;
                maps.get(finalIndex).put(title.getValue(), new float[]{x, y, width, height});
            });
        }
        return maps;
    }

    /**
     * 加载字体
     */
    private PdfFont loadFont() throws IOException {
        Resource[] resources = new PathMatchingResourcePatternResolver()
                .getResources("classpath*:/font/*.ttc");
        if (resources.length == 0) {
            return null;
        }
        PdfFontFactory.register(resources[0].getURL().getPath(), "SimSun");
        return PdfFontFactory.createRegisteredFont("SimSun", PdfEncodings.IDENTITY_H,
                PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);
    }
}

填充实例

java 复制代码
LocalDate now = LocalDate.now();
// 图片文件
ByteArrayOutputStream catOutStream = new ByteArrayOutputStream();
File img = ResourceUtils.getFile("classpath:cat.png");
InputStream catInStream = Files.newInputStream(img.toPath());
IOUtils.copy(catInStream, catOutStream);
Map<String, Object> data = new HashMap<>();
data.put("username", "喵星人");
data.put("gender", "女");
data.put("nation", "狗");
data.put("school", "狗族大学");
data.put("describe", "千百年来,地球上一直住着一种外星生物,名叫喵星人。它们从遥远的喵星来到地球,化身为猫科动物,分散在世界每个角落。萌萌的外表,加上机智聪明的头脑,轻易就得到人类的宠爱。");
data.put("describe1", "千百年来,地球上一直住着一种外星生物,名叫喵星人。它们从遥远的喵星来到地球,化身为猫科动物,分散在世界每个角落。萌萌的外表,加上机智聪明的头脑,轻易就得到人类的宠爱。");
data.put("content", "千百年来,地球上一直住着一种外星生物,名叫喵星人。它们从遥远的喵星来到地球,化身为猫科动物,分散在世界每个角落。萌萌的外表,加上机智聪明的头脑,轻易就得到人类的宠爱。");
data.put("sign", catOutStream.toByteArray());
data.put("avatar", catOutStream.toByteArray());
data.put("year", String.valueOf(now.getYear()));
data.put("month", String.valueOf(now.getMonthValue()));
data.put("day", String.valueOf(now.getDayOfMonth()));

String homePath = FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath();
File template = ResourceUtils.getFile("classpath:fill_template.pdf");
byte[] filledDataBytes = PDFTemplateFillHandler.loadTemplate(Files.newInputStream(template.toPath()))
        .fill(data);
IOUtils.write(filledDataBytes, Files.newOutputStream(Paths.get(homePath + "/" + System.nanoTime() + ".pdf")));

基于IText7 的 PDF表单域模板填充

相关推荐
duration~38 分钟前
Maven随笔
java·maven
zmgst41 分钟前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
其乐无涯1 小时前
服务器技术(一)--Linux基础入门
linux·运维·服务器
跃ZHD1 小时前
前后端分离,Jackson,Long精度丢失
java
写bug的小屁孩1 小时前
前后端交互接口(三)
运维·服务器·数据库·windows·用户界面·qt6.3
斑布斑布1 小时前
【linux学习2】linux基本命令行操作总结
linux·运维·服务器·学习
紅色彼岸花1 小时前
第六章:DNS域名解析服务器
运维·服务器
Spring_java_gg1 小时前
如何抵御 Linux 服务器黑客威胁和攻击
linux·服务器·网络·安全·web安全
✿ ༺ ོIT技术༻1 小时前
Linux:认识文件系统
linux·运维·服务器
恒辉信达1 小时前
hhdb数据库介绍(8-4)
服务器·数据库·mysql