1. 程式人生 > 實用技巧 >itextpdf FormField 生成pdf 檔案(包含中文以及圖片處理)

itextpdf FormField 生成pdf 檔案(包含中文以及圖片處理)

很常見的一個功能,基於pdf 的AcroFields 提供的模版的能力,通過資料填充生成新的pdf 文件,對於圖片的處理基於
PdfContentByte (一個強大的內容處理物件)

模版製作

一般大家的做法可以直接基於word 然後匯出為pdf,然後通過pdf pro 工具,製作AcroFields ,但是pdf pro 比較貴,而且
很多時候我們不需要複雜的功能,pdfescape 是一個不錯的選擇,https://www.pdfescape.com 具體操作可以搜相關資料
比較簡單

程式碼使用

使用了itextpdf 5 沒有使用7 版本

  • 模版說明

我已經制作好了一個測試模版,AcroFields 自己的資訊為 year,makrs,d,logo (圖片的)

模版內容

  • 專案結構

  • pom.xml
    說明引入itext-asian 主要是解決字型語言的問題
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.dalongdemoapp</groupId>
  <artifactId>pdf</artifactId>
  <version>1.0-SNAPSHOT</version>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <encoding>UTF-8</encoding>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>
  <dependencies>
    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itextpdf</artifactId>
      <version>5.5.3</version>
    </dependency>
    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itext-asian</artifactId>
      <version>5.2.0</version>
    </dependency>
  </dependencies>
</project>
  • 程式碼說明
    Application.java
package com.dalong;
import com.itextpdf.text.Image;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.*;
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
 @author dalong
*/
public class Application {
  public static void export(){
    try {
      String fileName = "src/main/resources/授課時間-image.pdf";
      String imageFile = "src/main/resources/zeebe.png";
      PdfReader reader = new PdfReader(fileName);
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      PdfStamper ps = new PdfStamper(reader, bos);
      // 中文字型問題
      BaseFont bf = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H", BaseFont.NOT_EMBEDDED);
      ArrayList<BaseFont> fontList = new ArrayList<BaseFont>();
      fontList.add(bf);
      AcroFields fields = ps.getAcroFields();
      fields.setSubstitutionFonts(fontList);
      fillData(fields, data());
      // 新增圖片
      addImage(ps,fields,"logo",imageFile);
      ps.setFormFlattening(true);
      ps.close();
      //生成pdf路徑存放的路徑
      OutputStream fos = new FileOutputStream("result.pdf");
      fos.write(bos.toByteArray());
      fos.flush();
      fos.close();
      bos.close();
     }catch (Exception e){
      e.printStackTrace();
     }
   }
  /**
   * 填充模板中的資料
   */
  public static void fillData(AcroFields fields, Map<String, String> data) {
    try {
      for (String key : data.keySet()) {
        String value = data.get(key);
        fields.setField(key, value);
       }
     } catch (Exception e) {
      e.printStackTrace();
     }
   }
  public static void addImage(PdfStamper stamper,AcroFields form,String field,String fieldValue){
    try{
      java.util.List<AcroFields.FieldPosition> photograph = form.getFieldPositions(field);
      if(photograph!=null && photograph.size()>0){
        Rectangle rect= photograph.get(0).position;
        Image img = Image.getInstance(fieldValue);
        img.scaleToFit(rect.getWidth(), rect.getHeight());
        img.setBorder(2);
        img.setAbsolutePosition(
            photograph.get(0).position.getLeft() + (rect.getWidth() - img.getScaledWidth() )
             , photograph.get(0).position.getTop() - (rect.getHeight()));
        PdfContentByte cb = stamper.getOverContent((int)photograph.get(0).page);
        cb.addImage(img);
       }
     }catch(Exception e){
      e.printStackTrace();
     }
   }
  /**
   * 填充資料來源
   * 其中data存放的key值與pdf模板中的文字域值相對應
   */
  public static Map<String, String> data() {
    Map<String, String> data = new HashMap<String, String>();
    data.put("year", "2020");
    data.put("marks","摘要:這個本來屬於s3 的特性,但是我們在實際使用的過程中肯定不想別人直接可以通過瀏覽器或者http就可以可以我們的檔案內容 這個屬於安全的控制,以下是一個實踐以及一些安全控制 一些原則 不能直接暴露minio 訪問到公網環境(可以基於nginx,以及反向代理工具解決) 配置合理的bucket 策略,可以 閱讀全文\n");
    data.put("d","因為當前大家主流的還是基於前後端分離的模式開發軟體,元件+api 實現功能,但是很多時候好多租戶對於功能有個性化需求,但是\n" +
        "系統在設計的時候因為時間問題+早期設計問題造成業務擴充套件能力有點差,還需要支援個性化需求開發,所以我們可以拆分標準版本\n" +
        "以及自定型版本,同時基於minio 提供的s3 管理模式,對於不同的租戶建立不同的bucket,標準的使用標準版,這樣客戶化開發就很簡單\n" +
        "了(特殊場景需要個性化),此方案的缺點也比較明確:空間的佔用,但是還好因為前後端分離的模式。每個租戶佔用的靜態資源也不是\n" +
        "很大,核心問題是在系統更新的時候,我們可能需要引導客戶自己升級或者基於強大的ci/cd 系統進行所有租戶的系統升級(構建包的處理)\n" +
        "個人感覺好處也是很明顯的,如果我們的api 以及website 已經做了比較好的版本管理,使用者切換版本也就是靜態資源的替換(直接基於s3 bucket)。\n" +
        "saas 應用的功能主要基於單頁面的模式開發的應用,在s3 中的儲存就是css,js 以及靜態html 頁面。這樣管理起來也是比較靈活的");
    return data;
   }
  public static void main(String[] args) {
    export();
   }
}
  • 生成的效果

模版欄位替換


圖片替換

說明

程式碼很簡單,但是此功能還是一個比較常見的需求,還有有點用的

參考資料

https://www.pdfescape.com/
https://github.com/rongfengliang/itextpdf-image-learning