java創建包含復雜樣式的PDF文件


簡介

在項目中我們有時候會有創建復雜PDF的需求,如訂單合同,今天我們使用itext工具來實現此功能。

實現

maven依賴

<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itextpdf</artifactId>
  <version>5.5.13</version>
</dependency>
<dependency>
  <groupId>com.itextpdf</groupId>
  <artifactId>itext-asian</artifactId>
  <version>5.2.0</version>
</dependency>

itext是一個可以操作PDF的開源工具包,類似pdfbox,但功能更加強大,官方文檔

實現效果

代碼實現

點擊查看代碼
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import java.io.IOException;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PdfUtil {

  public static BaseColor blackColor = new BaseColor(0, 0, 0);
  public static BaseColor redColor = new BaseColor(255, 0, 0);
  public static BaseColor lightYellowColor = new BaseColor(255, 255, 153);
  public static BaseColor lightBlueColor = new BaseColor(133, 188, 224);
  public static BaseColor hardBlueColor = new BaseColor(31, 86, 112);

  /**
   * 創建cell
   *
   * @param context 內容
   * @param aligin 對齊方式
   * @param colspan 跨列數
   */
  public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
      PdfPTable headerTable, Font font) {
    return createCell(context, aligin, colspan, rowspan, headerTable, font, 0f, 0);
  }

  /**
   * @param paddingLeft 左邊距
   * @param border Rectangle.TOP,Rectangle.BOTTOM,Rectangle.LEFT,Rectangle.RIGHT,Rectangle.BOX,Rectangle.NO_BORDER
   */
  public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
      PdfPTable headerTable, Font font, Float paddingLeft, int border) {
    return createCell(context, aligin, colspan, rowspan, headerTable, font, paddingLeft, border,
        true, BaseColor.WHITE);
  }

  /**
   * @param paddingLeft 左邊距
   * @param border Rectangle.TOP,Rectangle.BOTTOM,Rectangle.LEFT,Rectangle.RIGHT,Rectangle.BOX,Rectangle.NO_BORDER
   */
  public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
      PdfPTable headerTable, Font font, Float paddingLeft, int border, BaseColor bgColor) {
    return createCell(context, aligin, colspan, rowspan, headerTable, font, paddingLeft, border,
        true, bgColor);
  }

  /**
   * @param paddingLeft 左邊距
   * @param border Rectangle.TOP,Rectangle.BOTTOM,Rectangle.LEFT,Rectangle.RIGHT,Rectangle.BOX,Rectangle.NO_BORDER
   */
  public static PdfPCell createCell(String context, int aligin, int colspan, int rowspan,
      PdfPTable headerTable, Font font, Float paddingLeft, int border, boolean isSet,
      BaseColor bgColor) {
    if (Objects.isNull(context)) {
      context = "";
    }
    Pattern p = Pattern.compile("[\u4e00-\u9fa5]");
    Matcher m = p.matcher(context);
    //包含中文,則使用中文字體
    if (m.find()) {
      font = ChineseFont(font.getSize(), font.getStyle(), font.getColor());
    }
    Paragraph elements = new Paragraph(context, font);
    elements.setAlignment(aligin);

    PdfPCell cell = newPdfPCell(bgColor, border, aligin, colspan, rowspan, elements);
    if (paddingLeft != null) {
      cell.setPaddingLeft(paddingLeft);
    }
    if (isSet) {
      headerTable.addCell(cell);
    }
    return cell;
  }

  public static PdfPCell newPdfPCell(BaseColor bgColor, int border, int align, int colspan,
      int rowspan, Paragraph paragraph) {
    PdfPCell cell = new PdfPCell();
    cell.setBorderColor(BaseColor.BLACK);
    cell.setBorderColorLeft(BaseColor.BLACK);
    cell.setBorderColorRight(BaseColor.BLACK);
    cell.setBackgroundColor(bgColor);
    cell.setBorder(border);
    cell.setHorizontalAlignment(align);  //水平居中
    cell.setVerticalAlignment(Element.ALIGN_MIDDLE); //垂直居中
    cell.setColspan(colspan);
    cell.setRowspan(rowspan);
    cell.setMinimumHeight(10);//單元格最小高度
    cell.addElement(paragraph);
    // 設置單元格的邊框寬度和顏色
    cell.setBorderWidth(0.5f); //邊框的寬度
    BaseColor blackColor = new BaseColor(139, 134, 130);
    cell.setBorderColor(blackColor);
    return cell;
  }

  /**
   * 設置空白線
   */
  public static void setBlankLine(PdfPTable headerTable, BaseColor bgColor, int columnNumber,
      float fixedHeight) {
    Paragraph paragraph = new Paragraph("");
    PdfPCell pdfPCell = PdfUtil
        .newPdfPCell(bgColor, 0, 1, columnNumber, 1, paragraph);
    pdfPCell.setFixedHeight(fixedHeight);//單元格固定高度
    headerTable.addCell(pdfPCell);
  }

  /**
   * 創建單元格(內容為圖片)
   *
   * @param bgColor 背景
   * @param border 邊框
   * @param align 對齊方式
   * @param colspan 所占列數
   * @param image 內容(文字或圖片對象)
   */
  public static PdfPCell newPdfPCellOfImage(BaseColor bgColor, int border, int align, int colspan,
      int rowspan, Image image) {
    PdfPCell cell = new PdfPCell();
    cell.setBorderColor(BaseColor.BLACK);
    cell.setBorderColorLeft(BaseColor.BLACK);
    cell.setBorderColorRight(BaseColor.BLACK);
    cell.setBackgroundColor(bgColor);
    cell.setBorder(border);
    cell.setUseAscender(Boolean.TRUE);
    cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
    cell.setHorizontalAlignment(align);
    cell.setColspan(colspan);
    cell.setRowspan(rowspan);
    cell.addElement(image);
    return cell;
  }

  /**
   * 創建pdf
   */
  public static Document createPDF() {
    return new Document(PageSize.A4);
  }

  /**
   * 中文字體
   */
  public static Font ChineseFont(float fontSize, int style, BaseColor clolr) {
    BaseFont baseFont = null;
    try {
      String fontDir = "/font";
      //包含中文則使用微軟雅黑
      String fontPath = fontDir + "/msyh.ttc,0";
      baseFont = BaseFont.createFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
    } catch (DocumentException | IOException e) {
    }
    return new Font(baseFont, fontSize, style, clolr);
  }

}

一個工具類,方便的創建單元格及字體,字體顏色等對象。

點擊查看代碼
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.Image;
import com.itextpdf.text.Paragraph;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfDiv;
import com.itextpdf.text.pdf.PdfDiv.PositionType;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.URL;

public class PdfService {

  private static final int COLUMN_NUMBER = 24;

  /**
   * 下載pdf
   */
  public void downloadPdf(OutputStream outputStream) throws Exception {
    //創建字體
    BaseFont baseFont = BaseFont.createFont("/font/calibri.ttf", BaseFont.IDENTITY_H,
        BaseFont.EMBEDDED);  //沒有加粗calibrib字體
    BaseFont boldFont = BaseFont.createFont("/font/calibrib.ttf", BaseFont.IDENTITY_H,
        BaseFont.EMBEDDED);//加粗calibrib字體

    Font bold_black_font7 = new Font(boldFont, 7.5f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-7號字體
    Font normal_hard_blue_font7 = new Font(boldFont, 7.5f, Font.NORMAL,
        PdfUtil.hardBlueColor);//加粗-黑色-7號字體
    Font bold_red_font7 = new Font(boldFont, 7.5f, Font.NORMAL, PdfUtil.redColor);//加粗-黑色-7號字體
    Font normal_black_font5 = new Font(baseFont, 7f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-7號字體
    Font normal_red_font5 = new Font(baseFont, 7f, Font.NORMAL, PdfUtil.redColor);//加粗-黑色-7號字體
    Font bold_black_font5 = new Font(baseFont, 8f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-7號字體
    Font bold_black_font16 = new Font(boldFont, 16f, Font.NORMAL, PdfUtil.blackColor);//加粗-黑色-17號字體

    //創建輸出流
    Document document = PdfUtil.createPDF();
    PdfWriter writer = PdfWriter.getInstance(document, outputStream);
    writer.setPdfVersion(PdfWriter.PDF_VERSION_1_7);
    // 打開文檔對象
    document.open();
    //頭部主要信息表
    PdfPTable headerTable = new PdfPTable(COLUMN_NUMBER);
    headerTable.setWidthPercentage(100);

    Paragraph paragraph = new Paragraph("");

    //第1行 公司logo
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL logoRes = classLoader.getResource("image/test_logo.jpeg");
    URL sealRes = classLoader.getResource("image/test_seal.jpeg");
    Image xieldLogoImage = Image.getInstance(logoRes);
    Image sealImage = Image.getInstance(sealRes);
    PdfPCell leftLogoImageCell = PdfUtil
        .newPdfPCellOfImage(BaseColor.WHITE, 0, Element.ALIGN_LEFT, 4, 4, xieldLogoImage);
    PdfPCell rightLogoImageCell = PdfUtil
        .newPdfPCellOfImage(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 4, 4, xieldLogoImage);
    //添加左邊圖片logo單元格
    headerTable.addCell(leftLogoImageCell);
    //添加空白單元格
    headerTable
        .addCell(PdfUtil.newPdfPCell(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 16, 4, paragraph));
    //添加右邊圖片logo單元格
    headerTable.addCell(rightLogoImageCell);
    //添加空白單元格
    headerTable
        .addCell(PdfUtil.newPdfPCell(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 6, 2, paragraph));
    //添加標題單元格
    PdfUtil.createCell("合同", Element.ALIGN_CENTER, 10, 2, headerTable,
        bold_black_font16);
    //添加空白單元格
    headerTable
        .addCell(PdfUtil.newPdfPCell(BaseColor.WHITE, 0, Element.ALIGN_RIGHT, 8, 2, paragraph));
    //甲方公司和乙方公司信息單元格
    PdfUtil.createCell("", Element.ALIGN_LEFT, 16, 1, headerTable,
        bold_black_font7);
    PdfUtil
        .createCell("合同編號:xxxx", Element.ALIGN_LEFT, 8, 1, headerTable, bold_black_font7,
            20f, 0);
    PdfUtil.createCell("甲方:xxxx有限公司", Element.ALIGN_LEFT, 16, 1, headerTable,
        bold_red_font7);
    PdfUtil
        .createCell("乙方:xxxx有限公司", Element.ALIGN_LEFT, 8, 1, headerTable,
            normal_hard_blue_font7, 20f,
            0);
    PdfUtil.createCell("聯絡人:xxx", Element.ALIGN_LEFT, 16, 1, headerTable, bold_red_font7);
    PdfUtil
        .createCell("聯系人: xxx", Element.ALIGN_LEFT, 8, 1, headerTable,
            normal_hard_blue_font7, 20f,
            0);
    PdfUtil.createCell("電話:xxxx", Element.ALIGN_LEFT, 16, 1, headerTable, bold_red_font7);
    PdfUtil
        .createCell("電話:xxxx", Element.ALIGN_LEFT, 8, 1, headerTable,
            normal_hard_blue_font7, 20f,
            0);
    PdfUtil.createCell("E-MAIL: xxxx", Element.ALIGN_LEFT, 16, 1, headerTable,
        bold_red_font7);
    PdfUtil.createCell("賬號:xxxx", Element.ALIGN_LEFT, 8, 1, headerTable,
        normal_hard_blue_font7, 20f, 0);
    PdfUtil.createCell("地址:xxxx", Element.ALIGN_LEFT, 16, 1, headerTable,
        bold_red_font7);
    PdfUtil.createCell("E-MAIL: xxxx", Element.ALIGN_LEFT, 8, 1, headerTable,
        normal_hard_blue_font7, 20f, 0);

    //設備塊 上邊 空白區域
    PdfUtil.setBlankLine(headerTable, BaseColor.WHITE, COLUMN_NUMBER, 10);
    //設備塊中的明細列表 這部分應該是一個循環體
    PdfUtil
        .createCell("項次", Element.ALIGN_CENTER, 1, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil.createCell("產品名稱", Element.ALIGN_CENTER, 5, 1, headerTable,
        bold_black_font5, 1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("產品編碼", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("種類", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("原價", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("折扣", Element.ALIGN_CENTER, 1, 1,
            headerTable, bold_black_font5, 1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("優惠價", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("數量", Element.ALIGN_CENTER, 1, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("小計", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("SN", Element.ALIGN_CENTER, 2, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    PdfUtil
        .createCell("最終用戶", Element.ALIGN_CENTER, 4, 1, headerTable, bold_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    for (int i = 0; i < 2; i++) {
      PdfUtil
          .createCell(String.valueOf(i + 1), Element.ALIGN_CENTER, 1, 1, headerTable,
              normal_black_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("測試商品",
              Element.ALIGN_CENTER,
              5, 1, headerTable,
              normal_red_font5, 1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("測試產品編碼", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("測試種類", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("100%", Element.ALIGN_CENTER, 1, 1,
              headerTable, normal_red_font5, 1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("1", Element.ALIGN_CENTER, 1, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("xxxx", Element.ALIGN_CENTER, 2, 1, headerTable, normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
      PdfUtil
          .createCell("XXXX有限公司", Element.ALIGN_CENTER, 4, 1, headerTable,
              normal_red_font5,
              1f, Rectangle.BOX, PdfUtil.lightYellowColor);
    }
    PdfUtil
        .createCell("產品合計(含13%增值稅):", Element.ALIGN_RIGHT, 16, 1, headerTable, normal_black_font5,
            1f, Rectangle.BOX);
    PdfUtil
        .createCell("¥10000.00", Element.ALIGN_CENTER, 2, 1, headerTable, normal_black_font5,
            1f, Rectangle.BOX, PdfUtil.lightBlueColor);
    //空白區域
    PdfUtil.setBlankLine(headerTable, BaseColor.WHITE, COLUMN_NUMBER, 10);
    PdfUtil.createCell("備注", Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, normal_hard_blue_font7);
    PdfUtil.createCell("1.運費由乙方承擔,默認方式為順豐速運。也可甲方提供運輸賬號或甲方上門自提。", Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, normal_hard_blue_font7);
    PdfUtil.createCell("一、甲方向乙方采購 XXXX。",
        Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, bold_black_font7);
    PdfUtil.createCell("二、有效期為30天,本合同必須在2021年11月9日起30天內簽署完成,過期作廢。", Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, bold_black_font7);
    PdfUtil.createCell("三、本合同總金額(大寫)貳萬元整 人民幣:¥10000.00", Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, bold_black_font7);
    PdfUtil.createCell("甲方:XXXX有限公司", Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, bold_red_font7);
    PdfUtil.createCell("乙方:XXXX有限公司", Element.ALIGN_LEFT,
        COLUMN_NUMBER, 24, headerTable, bold_black_font7);
    // 2-PDF文檔屬性
    document.addTitle("合同");// 標題(登錄id)
    document.addAuthor("作者");// 作者(登錄人名稱)
    document.addSubject("合同");// 主題
    document.addKeywords("合同");// 關鍵字
    document.addCreator("XXXX有限公司");// 誰創建的
    document.add(headerTable);
    PdfDiv sealImageDiv = new PdfDiv();
    sealImageDiv.setPosition(PositionType.ABSOLUTE);
    sealImageDiv.setPaddingLeft(300);
    sealImageDiv.setPaddingBottom(700);
    sealImageDiv.addElement(sealImage);
    sealImage.scaleAbsolute(100f, 100f);
    document.add(sealImageDiv);
    //關閉
    document.close();
  }

  public static void main(String[] args) throws Exception {
    new PdfService().downloadPdf(new FileOutputStream("D:/testjar/test.pdf"));
  }
}

用來創建PDF的核心邏輯,主要原理為將整個PDF視為一個大表格,每個元素都是一個單元格,類似於excel的單元格,
有普通文本的單元格,也有包含圖片的單元格,每個單元格都可以設置樣式,如設置寬,高,背景顏色,字體等。
例子代碼中設置的總列寬為24,所以下邊每一行的單元格總寬度加起來也要為24。PDF中的公司公章圖片是要浮在文字上的,
不能使用單元格這種方式,要使用絕對定位。

文件資源

代碼中使用到的字體和圖片要放到classpath下,因為我的項目為SpringBoot項目,所以放在了resources目錄下

字體下載 提取碼(gonn)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM