Use itextpdf to fill form fields and generate pdf

Article directory

  • foreword
  • 1. Preparation
    • 1.1 Install the software
    • 1.2 Prepare PDFs
    • 1.3 Setting up form fields
  • 2. Create a project
  • 3. Write code
    • 3.1 Writing tool classes
    • 3.2 Testing
  • 4. Test results

Foreword

Recently, I have a task in hand, that is, I need to do a pdf export function.

There are many technical points to choose from. After comprehensive consideration, I use itext.
There are roughly two implementation ideas:
1: Use the software [Adobe Acrobat DC] to make a pdf template, specify the form fields, and then use the code to fill in the parameters, and finally get a pdf or byte array.

2: Use [Freemarker] to render the html page, and finally use the code to convert the page to pdf.

The current needs on my side are more suitable for the first way.

1. Preparations

1.1 Installing software

The first is to install the software (if it fails, please leave a message in the comment area)
Link: https://pan.baidu.com/s/1O8JtVuK87VYbzx0DGQyJ1g
Extraction code: a0hy

1.2 Prepare pdf

Create a new word document and insert a table, the effect is as follows:

Then export the word as a pdf file.
Then use the software we just installed to open.

1.3 Setting form fields

Find the “Prepare Form” function in the toolbar and click to open it.

Then modify the text field name, font size and other configurations you want:

You can also right-click to add a new text field (the title here is new):

Then we can save the pdf. In the end we will get a pdf like this:

2. Create a project

Create an ordinary springboot project and introduce dependencies as follows:

 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itext7-core</artifactId>
            <version>7.2.5</version>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

Create a templates folder in the resources directory, and put the pdf we prepared earlier into it. The effect is as follows:

The file I prepared here is personal_info.pdf .

3. Write code

3.1 Writing tool classes

package org.feng.pdf;

import com.itextpdf.forms.PdfAcroForm;
import com.itextpdf.forms.fields.PdfFormField;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.ResourceUtils;

import java.io.*;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
 * Fill pdf form fields
 *
 * @author fengjinsong
 * @date 2023-06-28 16:55:38
 * @version: 1.0
 */
@Slf4j
@Data
public class FillFormFieldsToPdfTemplateUtil {<!-- -->

    /**
     * Default template path
     */
    private static final String DEFAULT_TEMPLATE_DIRECTORY;

    /**
     * Default font (pdf display Chinese)
     */
    private static final PdfFont DEFAULT_FONT;

    /**
     * template path
     */
    private String templateDirectory;

    /**
     * font
     */
    private PdfFont pdfFont;

    public FillFormFieldsToPdfTemplateUtil() {<!-- -->
    }

    static {<!-- -->
        try {<!-- -->
            DEFAULT_TEMPLATE_DIRECTORY = ResourceUtils.getURL("classpath:").getPath() + "/templates/";
            DEFAULT_FONT = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_NOT_EMBEDDED);
        } catch (IOException e) {<!-- -->
            throw new RuntimeException(e);
        }
    }

    /**
     * Based on the pdf template, fill in the form fields and generate a pdf file
     *
     * @param templateFileName Template file name, cannot be empty
     * @param formFieldsParams Parameters required by the form field, cannot be empty
     * @param destPdfPath destination location
     * @throws IOException related to file operations
     */
    public void generatePdfFile(String templateFileName, String destPdfPath, Map<String, String> formFieldsParams) throws IOException {<!-- -->
        // Construct a pdf reader, specify the input stream
        PdfReader pdfReader = new PdfReader(new FileInputStream(getTemplatePath(templateFileName)));
        // Construct a pdf writer and specify the output stream
        OutputStream fos = new FileOutputStream(destPdfPath);

        // construct pdfDocument instance
        PdfDocument pdf = new PdfDocument(pdfReader, new PdfWriter(fos));

        // set to a4 paper size
        pdf.setDefaultPageSize(PageSize.A4);

        // replace parameters (if any)
        PdfAcroForm form = PdfAcroForm. getAcroForm(pdf, true);
        fillFormFields(form, formFieldsParams);

        // Lock the form and prevent modification
        form. flattenFields();
        pdf. close();
    }

    /**
     * Get the byte array corresponding to pdf according to pdf template and form field parameters
     *
     * @param templateFileName Template file name, cannot be empty
     * @param formFieldsParams Parameters required by the form field, cannot be empty
     * @return pdf byte array filled with form field parameters
     * @throws IOException involving file operations
     */
    public byte[] generatePdfByteArray(String templateFileName, Map<String, String> formFieldsParams) throws IOException, RuntimeException {<!-- -->

        // Construct a pdf reader, specify the input stream
        PdfReader pdfReader = new PdfReader(new FileInputStream(getTemplatePath(templateFileName)));
        // Construct a pdf writer and specify the output stream
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        PdfWriter pdfWriter = new PdfWriter(outputStream);

        // construct pdfDocument instance
        PdfDocument pdf = new PdfDocument(pdfReader, pdfWriter);
        // set to a4 paper size
        pdf.setDefaultPageSize(PageSize.A4);

        // replace parameters (if any)
        PdfAcroForm form = PdfAcroForm. getAcroForm(pdf, true);
        fillFormFields(form, formFieldsParams);

        // Lock the form and prevent modification
        form. flattenFields();
        // close resource
        pdf. close();
        // return the final result
        return outputStream.toByteArray();
    }

    /**
     * Get the template file path
     *
     * @param templateFileName template file name, in the specified template directory
     * @return template file path
     */
    private String getTemplatePath(String templateFileName) {<!-- -->
        // Concatenate the complete template path
        return Objects. nonNull(templateDirectory) ?
                templateDirectory + templateFileName : DEFAULT_TEMPLATE_DIRECTORY + templateFileName;
    }

    /**
     * Populate form fields
     *
     * @param form form
     * @param formFieldsParams The dynamic parameters required by the form
     */
    private void fillFormFields(PdfAcroForm form, Map<String, String> formFieldsParams) {<!-- -->
        // get all form fields
        Map<String, PdfFormField> fields = form. getFormFields();
        // Get the current font
        PdfFont currentFont = pdfFont == null ? DEFAULT_FONT : pdfFont;
        formFieldsParams.forEach((key, value) -> {<!-- -->
            // get a form field
            Optional<PdfFormField> formFieldOptional = Optional.ofNullable(fields.get(key));
            formFieldOptional.ifPresent(formField -> {<!-- -->
                // Set the font and replacement value
                formField.setFont(currentFont).setValue(value);
            });
        });
    }
}

3.2 Testing

Prepare a Controller class as follows:

package org.feng.controller;

import lombok.extern.slf4j.Slf4j;
import org.feng.pdf.FillFormFieldsToPdfTemplateUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * itextpdf7 controller
 *
 * @author fengjinsong
 * @date 2023-06-29 09:00:04
 * @version: 1.0
 */
@Controller
@Slf4j
public class ItextPdf7Controller {<!-- -->

    /**
     * download file
     *
     * @param response response object; used to set response parameters
     */
    @GetMapping("/download")
    public ResponseEntity<byte[]> download(HttpServletResponse response) throws IOException {<!-- -->

        HttpHeaders headers = new HttpHeaders();
        // application/octet-stream binary stream data (text download).
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

        // Download the displayed file name to solve the Chinese name garbled problem
        String downloadFileName = System.currentTimeMillis() + ".pdf";
        // The pop-up save box is the resource selector
        response.setHeader("Content-Disposition", "attachment;filename=" + downloadFileName);

        // assembly parameters
        Map<String, String> data = new HashMap<>();
        data.put("title", "Introduction");
        data.put("name", "a friend of mine");
        data.put("age", "30");
        data.put("likes", "female");
        data.put("birthday", "2022-12-22");
        // fill the pdf template with data and convert to byte array
        FillFormFieldsToPdfTemplateUtil pdfTemplateUtil = new FillFormFieldsToPdfTemplateUtil();
        byte[] bytes = pdfTemplateUtil.generatePdfByteArray("personal_info.pdf", data);

        // return result
        return new ResponseEntity<>(bytes, headers, HttpStatus.CREATED);
    }


    @ResponseBody
    @GetMapping("/generate")
    public String generatePdfFile() throws IOException {<!-- -->

        // Specify the output directory
        String path = ResourceUtils.getURL("classpath:").getPath() + "/templates/";

        // assembly parameters
        Map<String, String> data = new HashMap<>();
        data.put("title", "Introduction");
        data.put("name", "a friend of mine");
        data.put("age", "30");
        data.put("birthday", "2022-12-22");
        data.put("likes", "female");

        // generate pdf in project
        FillFormFieldsToPdfTemplateUtil pdfTemplateUtil = new FillFormFieldsToPdfTemplateUtil();
        pdfTemplateUtil.generatePdfFile("personal_info.pdf", path + "dsadsad.pdf", data);

        // return result
        return "success";
    }
}

4. Test results

Generate a pdf file in the project, send a request GET http://localhost:8080/generate
Download the generated pdf file and send a request GET http://localhost:8080/download

The generated pdf effect is as follows: