itextpdf generates pdf based on pdf form template filling

itextpdf generates pdf based on pdf form template filling

Template creation

Example: Create a template using adobe acrobat tools

Take excel to generate a form template as an example

1. Create a form excel

2. Create a new PDF form template

Open the pdf tool: File -> Create -> Create Form

Select the created excel import

Edit imported form

After importing, the left side is the field name, and the right side is the content of the form field, that is, the form placeholder, which can be edited by double-clicking
The name is the key obtained when itext is read

Depend on jar package

<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.13</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.5</version>
</dependency>

Implementation code

Form pdf field configuration information entity class

import com.itextpdf.text.pdf.BaseFont;
import com.test.utils.pdf.PdfCreateUtil;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.Optional;

@Data
@ApiModel("Form pdf field configuration information")
public class FormPdfFieldConfig {<!-- -->

    @ApiModelProperty("font size, default 14")
    private float fontSize = 14f;

    @ApiModelProperty("font style")
    private BaseFont baseFont;

    @ApiModelProperty("Paragraph line spacing in the cell, the default is 1.5 times")
    private float cellLineLeading = 1.5f;

    @ApiModelProperty("Image location x-axis coordinates")
    private float xPos = -1f;

    @ApiModelProperty("Y-axis coordinates of image position")
    private float yPos = -1f;

    @ApiModelProperty("image width")
    private float width = -1f;

    @ApiModelProperty("picture height")
    private float height = -1f;

    public BaseFont getBaseFont() {<!-- -->
        baseFont = Optional.ofNullable(baseFont).orElse(PdfCreateUtil.getDefaultBaseFont());
        return baseFont;
    }
}

Form pdf parameter information entity class

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.util.Optional;

@Data
@ApiModel("Form pdf parameter information")
public class FormPdfParamInfo {<!-- -->

    @ApiModelProperty("Field filling type - adaptive text filling")
    public static final String FIELD_FILL_TYPE_ADAPTIVE = "adaptive";

    @ApiModelProperty("Field filling type - QR code filling")
    public static final String FIELD_FILL_TYPE_QR_IMAGE = "qrImage";

    @ApiModelProperty("field key")
    private String fieldKey;

    @ApiModelProperty("String field value")
    private String stringValue;

    @ApiModelProperty("picture content")
    private String imageContent;

    @ApiModelProperty("field filling type")
    private String fieldFillType = FIELD_FILL_TYPE_ADAPTIVE;

    @ApiModelProperty("field configuration information")
    private FormPdfFieldConfig fieldConfig;

    @ApiModelProperty("The pdf page number where the field is located, starting from 1")
    private int pageNum = 1;

    public FormPdfFieldConfig getFieldConfig() {<!-- -->
        fieldConfig = Optional.ofNullable(fieldConfig).orElse(new FormPdfFieldConfig());
        return fieldConfig;
    }
}

Form type pdf field processing tool class

import cn.hutool.extra.qrcode.QrCodeUtil;
import com.itextpdf.text.*;
import com.itextpdf.text.pdf.AcroFields;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.test.constants.NumberConst;
import com.test.vo.pdf.FormPdfFieldConfig;
import com.test.vo.pdf.FormPdfParamInfo;

import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * Form type pdf field processing tool class
 */
public class PdfFormFieldUtil {<!-- -->

    /**
     * Adaptive plug value
     * @param acroFields
     * @param paramInfo
     */
    public static void setAdaptiveField(AcroFields acroFields, PdfContentByte pdfContentByte, FormPdfParamInfo paramInfo) {<!-- -->
        PdfPTable pdfPtable = new PdfPTable(NumberConst.NUM_1);

        Rectangle rectangle = acroFields.getFieldPositions(paramInfo.getFieldKey()).get(NumberConst.NUM_0).position;

        float totalWidth = rectangle.getRight() - rectangle.getLeft() - 1;
        pdfPtable.setTotalWidth(totalWidth);

        getTable(pdfPtable, rectangle, paramInfo);

        pdfPtable.writeSelectedRows(0, -1, rectangle.getLeft(), rectangle.getTop(), pdfContentByte);
    }

/**
     * Add QR code/picture (to be verified and optimized)
     * @param acroFields
     * @param pdfContentByte
     * @param paramInfo
     * @throws DocumentException
     * @throws IOException
     */
    public static void setQrImage(AcroFields acroFields, PdfContentByte pdfContentByte, FormPdfParamInfo paramInfo) throws DocumentException, IOException {<!-- -->

        Rectangle rectangle = acroFields.getFieldPositions(paramInfo.getFieldKey()).get(NumberConst.NUM_0).position;
        FormPdfFieldConfig fieldConfig = paramInfo.getFieldConfig();

        BufferedImage bufImg = QrCodeUtil.generate(paramInfo.getImageContent(), 400, 400);
        Image image = Image.getInstance(bufImg, null);

        float x = fieldConfig.getXPos()>0 ? fieldConfig.getXPos() : rectangle.getLeft();
        float y = fieldConfig.getYPos()>0 ? fieldConfig.getYPos() : rectangle.getBottom();
        image.setAbsolutePosition(x, y);

        float width = fieldConfig.getWidth()>0 ? fieldConfig.getWidth() : rectangle.getWidth();
        float height = fieldConfig.getHeight()>0 ? fieldConfig.getHeight() : rectangle.getHeight();
        image.scaleToFit(width, height);

        pdfContentByte.addImage(image);
    }

    /**
     * About the automatic scaling of PDF cell spacing and font size
     * 2019-02-13
     * @param table
     * @param rectangle
     * @param paramInfo
     * @return
     */
    private static void getTable(PdfPTable table, Rectangle rectangle, FormPdfParamInfo paramInfo) {<!-- -->
        FormPdfFieldConfig fieldConfig = paramInfo.getFieldConfig();

        Font cellTitleFont = new Font(fieldConfig.getBaseFont(), fieldConfig.getFontSize(), Font.NORMAL);
        Paragraph titleTitle = new Paragraph(paramInfo. getStringValue(), cellTitleFont);
        PdfPCell cell = new PdfPCell(titleTitle);
        //Cell setting Border
        cell.setBorderWidth(0);
        // cell content top margin distance
        cell.setPaddingTop(-5f);
        //Cell content multi-line spacing
        cell.setLeading(0f, fieldConfig.getCellLineLeading());
        table. addCell(cell);
        float i = 0.5f;
        float j = 0.1f;
        if(table.getTotalHeight()>rectangle.getHeight()){<!-- -->
            table.deleteBodyRows();
            fieldConfig.setFontSize(fieldConfig.getFontSize()-i);
            fieldConfig.setCellLineLeading(fieldConfig.getCellLineLeading()-j);
            getTable(table, rectangle, paramInfo);
        }
    }

}

pdf generation tool class

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;

@Slf4j
public class PdfCreateUtil {<!-- -->

    private static BaseFont DEFAULT_BASE_FONT = null;

    /**
     * Use form type pdf template fill to generate pdf
     * @param paramMap
     * @param fileConfig
     * @return
     */
    public static byte[] createPdfByFormTemplate(Map<String, FormPdfParamInfo> paramMap, PdfFileConfig fileConfig) {<!-- -->
        try {<!-- -->
            String basePath = PdfCreateUtil.class.getClassLoader().getResource("").getFile();
            //Get the default font
            BaseFont baseFont = BaseFont.createFont(basePath + Optional.ofNullable(fileConfig.getFontFilePath()).orElse("fontfiles/song.ttf"), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);
            DEFAULT_BASE_FONT = baseFont;

            ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
            fontList. add(baseFont);
            // get the template
            PdfReader pdfReader = new PdfReader(basePath + fileConfig.getPdfTempPath());
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            PdfStamper pdfStamper = new PdfStamper(pdfReader, outputStream);
            AcroFields acroFields = pdfStamper. getAcroFields();
            //Set template font
            acroFields.setSubstitutionFonts(fontList);

            Map<String, AcroFields.Item> fieldMap = acroFields.getFields();
            for (String fieldKey : fieldMap. keySet()) {<!-- -->
                FormPdfParamInfo paramInfo = paramMap.get(fieldKey);

                if (paramInfo != null){<!-- -->
                    switch (paramInfo.getFieldFillType()){<!-- -->
                        case FormPdfParamInfo.FIELD_FILL_TYPE_QR_IMAGE:
                            // Fill in the QR code/picture
                            PdfFormFieldUtil.setQrImage(acroFields, pdfStamper.getOverContent(paramInfo.getPageNum()), paramInfo);
                            break;
                        default:
                            // default adaptive padding text
                            PdfFormFieldUtil.setAdaptiveField(acroFields, pdfStamper.getOverContent(paramInfo.getPageNum()), paramInfo);
                    }
                }
            }

            pdfStamper.setFormFlattening(true);
            pdfStamper. close();
            pdfReader. close();
            outputStream. close();

            return outputStream.toByteArray();

        } catch (Exception e) {<!-- -->
            log.error("pdf generation exception", e);
        }
        return null;
    }

    public static BaseFont getDefaultBaseFont() {<!-- -->
        return DEFAULT_BASE_FONT;
    }
}

Usage example (data assembly)

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.test.constant.BusiConstant;
import com.test.constants.NumberConst;
import com.test.utils.pdf.PdfCreateUtil;
import com.test.vo.pdf.FormPdfParamInfo;
import com.test.vo.pdf.PdfFileConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

public byte[] createPdfByTemplate(String type, JSONObject paramJson) {<!-- -->
        Map<String, FormPdfParamInfo> paramMap = new HashMap<>(NumberConst.NUM_4);
        PdfFileConfig fileConfig = new PdfFileConfig();

        if (StringUtils.isBlank(type)){<!-- -->
            log.error("The type parameter is empty");
            return null;
        }

        if (OtherbusiConstant.ENTERPRISE_TYPE_ENTERPRISE.equals(type)){<!-- -->
            // type 1
            fileConfig.setPdfTempPath("pdftemplate/template1.pdf");
        } else if (OtherbusiConstant.ENTERPRISE_TYPE_INDIVIDUAL.equals(type)){<!-- -->
            // type 2
            fileConfig.setPdfTempPath("pdftemplate/template2.pdf");
        }

        if (paramJson == null || paramJson.isEmpty()){<!-- -->
            log.error("pdf generated parameter data is empty, type=", type);
            return null;
        }

        paramJson.entrySet().forEach( entry -> {<!-- -->
            FormPdfParamInfo paramInfo = new FormPdfParamInfo();
            paramInfo.setFieldKey(entry.getKey());
            paramInfo.setStringValue((String) Optional.ofNullable(entry.getValue()).orElse(""));

            if (BusiConstant.TYPE_FIRST.equals(type)){<!-- -->
                if (BusiConstant.TYPE_SECOND.contains(entry.getKey())){<!-- -->
                    paramInfo.setPageNum(NumberConst.NUM_2);
                }
            }

            paramMap.put(entry.getKey(), paramInfo);
        });

        return PdfCreateUtil.createPdfByFormTemplate(paramMap, fileConfig);
    }