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)

posted @ 2022-03-13 17:20  strongmore  阅读(1242)  评论(0编辑  收藏  举报