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: