|
|
@@ -0,0 +1,271 @@
|
|
|
+package com.cloud.sa.base.module.support.pdf;
|
|
|
+
|
|
|
+import com.itextpdf.text.*;
|
|
|
+import com.itextpdf.text.Font;
|
|
|
+import com.itextpdf.text.Image;
|
|
|
+import com.itextpdf.text.Rectangle;
|
|
|
+import com.itextpdf.text.pdf.*;
|
|
|
+
|
|
|
+import javax.swing.*;
|
|
|
+import java.awt.*;
|
|
|
+import java.io.ByteArrayOutputStream;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.InputStream;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author gufj
|
|
|
+ * @date 2025-05-18
|
|
|
+ */
|
|
|
+public class PDFBuilder extends PdfPageEventHelper {
|
|
|
+ /**
|
|
|
+ * 页眉
|
|
|
+ */
|
|
|
+ public String header = "";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文档字体大小,页腳页眉最好和文本大小一致
|
|
|
+ */
|
|
|
+ public int presentFontSize = 10;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文档页面大小,最好前面传入,否則默认为A4
|
|
|
+ */
|
|
|
+ public Rectangle pageSize = PageSize.A4;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 模板
|
|
|
+ */
|
|
|
+ public PdfTemplate total;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 基础字体对象
|
|
|
+ */
|
|
|
+ public BaseFont bf = null;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 利用基础字体生成的字体对象,一般用于生成中文文字
|
|
|
+ */
|
|
|
+ public Font fontDetail = null;
|
|
|
+ /**
|
|
|
+ * 水印文件
|
|
|
+ */
|
|
|
+ private boolean watermark = true;
|
|
|
+ /**
|
|
|
+ * 是否展示页脚页码信息
|
|
|
+ */
|
|
|
+ private boolean isHeaderFooter = false;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 左上角logo
|
|
|
+ */
|
|
|
+ private String plate;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 字体
|
|
|
+ */
|
|
|
+ private String fontName = "STSong-Light";
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 编码
|
|
|
+ */
|
|
|
+ private String encoding = "UniGB-UCS2-H";
|
|
|
+ /**
|
|
|
+ * @param header
|
|
|
+ * @param presentFontSize
|
|
|
+ * @param pageSize
|
|
|
+ * @param watermark
|
|
|
+ */
|
|
|
+ public PDFBuilder(String header, int presentFontSize, Rectangle pageSize, boolean watermark,
|
|
|
+ boolean isHeaderFooter, String plate) {
|
|
|
+ this.header = header;
|
|
|
+ this.presentFontSize = presentFontSize;
|
|
|
+ this.pageSize = pageSize;
|
|
|
+ this.watermark = watermark;
|
|
|
+ this.isHeaderFooter = isHeaderFooter;
|
|
|
+ this.plate = plate;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 文档打开时创建模板
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void onOpenDocument(PdfWriter writer, Document document) {
|
|
|
+ /**
|
|
|
+ * 共 页 的矩形的长宽高
|
|
|
+ */
|
|
|
+ total = writer.getDirectContent().createTemplate(50, 50);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 关闭每页的时候添加页眉页腳和水印
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void onEndPage(PdfWriter writer, Document document) {
|
|
|
+ /**
|
|
|
+ * 添加分页
|
|
|
+ */
|
|
|
+ if (isHeaderFooter) {
|
|
|
+ this.addPage(writer, document);
|
|
|
+ }
|
|
|
+ /**
|
|
|
+ * 添加水印
|
|
|
+ */
|
|
|
+ if (watermark) {
|
|
|
+ this.addWatermark(writer, document);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分页
|
|
|
+ *
|
|
|
+ * @param writer
|
|
|
+ * @param document
|
|
|
+ */
|
|
|
+ public void addPage(PdfWriter writer, Document document) {
|
|
|
+ try {
|
|
|
+ if (bf == null) {
|
|
|
+ bf = BaseFont.createFont(fontName, encoding, BaseFont.NOT_EMBEDDED);
|
|
|
+ }
|
|
|
+ if (fontDetail == null) {
|
|
|
+ /**
|
|
|
+ * 数据字体好
|
|
|
+ */
|
|
|
+ fontDetail = new Font(bf, presentFontSize, Font.NORMAL);
|
|
|
+ fontDetail.setColor(BaseColor.BLACK);
|
|
|
+ }
|
|
|
+ } catch (Exception e) {
|
|
|
+ // log.error("", e);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 写入页眉
|
|
|
+ */
|
|
|
+ ColumnText.showTextAligned(writer.getDirectContent(), Element.ALIGN_LEFT,
|
|
|
+ new Phrase(header, fontDetail), document.left(), document.top() + 10, 0);
|
|
|
+
|
|
|
+ Image img;
|
|
|
+ try {
|
|
|
+ String logoPath = "templates/" + plate + "Logo.png";
|
|
|
+ ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
|
|
|
+ InputStream inputStream = classLoader.getResourceAsStream(logoPath);
|
|
|
+
|
|
|
+ int n;
|
|
|
+ byte[] buffer = new byte[4096];
|
|
|
+ ByteArrayOutputStream output = new ByteArrayOutputStream();
|
|
|
+ while (-1 != (n = inputStream.read(buffer))) {
|
|
|
+ output.write(buffer, 0, n);
|
|
|
+ }
|
|
|
+ img = Image.getInstance(output.toByteArray());
|
|
|
+
|
|
|
+ } catch (BadElementException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ } catch (IOException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ img.setAlignment(Image.ALIGN_RIGHT);
|
|
|
+ img.setWidthPercentage(80);
|
|
|
+ img.scaleToFit(50, 50);
|
|
|
+ img.setAbsolutePosition(document.right() - 50, document.top() + 10);
|
|
|
+ try {
|
|
|
+ writer.getDirectContent().addImage(img);
|
|
|
+ } catch (DocumentException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ // 页眉加下划线
|
|
|
+ PdfPTable tableHeader = new PdfPTable(1);
|
|
|
+ tableHeader.setTotalWidth(PageSize.A4.getWidth() - 60);
|
|
|
+ PdfPCell pCell = new PdfPCell();
|
|
|
+ pCell.setBorderWidthBottom(0.3f);
|
|
|
+ tableHeader.addCell(pCell);
|
|
|
+ tableHeader.writeSelectedRows(0, -1, 30, 810, writer.getDirectContent());
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 写入页脚(分页信息)
|
|
|
+ */
|
|
|
+ int pageS = writer.getPageNumber();
|
|
|
+ String foot1 = "第 " + pageS + " 页 / 共";
|
|
|
+ Phrase footer = new Phrase(foot1, fontDetail);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算前半部分的foot1的長度,后面好定位最后一部分的'Y页'这两字的x轴坐标,字体长度也要计算进去 = len
|
|
|
+ */
|
|
|
+ float len = bf.getWidthPoint(foot1, presentFontSize);
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 拿到当前的PdfContentByte
|
|
|
+ */
|
|
|
+ PdfContentByte cb = writer.getDirectContent();
|
|
|
+
|
|
|
+ ColumnText.showTextAligned(cb, Element.ALIGN_CENTER, footer,
|
|
|
+ (document.rightMargin() + document.right() + document.leftMargin() - document.left() - len)
|
|
|
+ / 2.0F + 20F,
|
|
|
+ document.bottom() - 25, 0);
|
|
|
+
|
|
|
+ cb.addTemplate(total,
|
|
|
+ (document.rightMargin() + document.right() + document.leftMargin() - document.left()) / 2.0F
|
|
|
+ + 20F,
|
|
|
+ document.bottom() - 25);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 添加水印
|
|
|
+ *
|
|
|
+ * @param writer
|
|
|
+ */
|
|
|
+ public void addWatermark(PdfWriter writer, Document document) {
|
|
|
+ // 加入水印
|
|
|
+ PdfContentByte waterMark = writer.getDirectContent();
|
|
|
+ Rectangle pageSize = writer.getPageSize();
|
|
|
+ // 开始设置水印
|
|
|
+ waterMark.beginText();
|
|
|
+ // 设置水印透明度
|
|
|
+ PdfGState gs = new PdfGState();
|
|
|
+ // 设置填充字体不透明度为0.4f
|
|
|
+ gs.setFillOpacity(0.2f);
|
|
|
+
|
|
|
+ JLabel label = new JLabel();
|
|
|
+ FontMetrics metrics;
|
|
|
+ int textH;
|
|
|
+ int textW;
|
|
|
+ label.setText("无限畅联");
|
|
|
+ metrics = label.getFontMetrics(label.getFont());
|
|
|
+ textH = metrics.getHeight();
|
|
|
+ textW = metrics.stringWidth(label.getText());
|
|
|
+ try {
|
|
|
+ // 设置水印字体参数及大小 (字体参数,字体编码格式,是否将字体信息嵌入到pdf中(一般不需要嵌入),字体大小)
|
|
|
+ waterMark.setFontAndSize(BaseFont.createFont(fontName, encoding, BaseFont.NOT_EMBEDDED), 20);
|
|
|
+ // 设置透明度
|
|
|
+ waterMark.setGState(gs);
|
|
|
+ for (int height = textH + textH * 5; height < pageSize.getHeight(); height = height + textH * 5) {
|
|
|
+ if (height + textH * 5 < pageSize.getHeight()) {
|
|
|
+ for (int width = textW; width < pageSize.getWidth() * 1.5 + textW; width = width + textW * 3) {
|
|
|
+ waterMark.showTextAligned(Element.ALIGN_LEFT, "无限畅联", width - textW, height - textH, 30);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 设置水印颜色
|
|
|
+ waterMark.setColorFill(BaseColor.LIGHT_GRAY);
|
|
|
+ //结束设置
|
|
|
+ waterMark.endText();
|
|
|
+ waterMark.stroke();
|
|
|
+ } catch (IOException e) {
|
|
|
+ e.printStackTrace();
|
|
|
+ } catch (DocumentException e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void onCloseDocument(PdfWriter writer, Document document) {
|
|
|
+ if (isHeaderFooter) {
|
|
|
+ total.beginText();
|
|
|
+ total.setFontAndSize(bf, presentFontSize);
|
|
|
+ total.setColorFill(BaseColor.GRAY);
|
|
|
+ String foot2 = " " + (writer.getPageNumber()) + " 页";
|
|
|
+ total.showText(foot2);
|
|
|
+ total.endText();
|
|
|
+ total.closePath();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|