java
复制代码
package org.jeecg.dts.common.util;
import lombok.Data;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.PDPageContentStream;
import org.apache.pdfbox.pdmodel.common.PDRectangle;
import org.apache.pdfbox.pdmodel.font.PDType0Font;
import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.jeecg.common.util.ZosUtil;
import org.jeecg.dts.common.constant.BussinessEnum;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
public class ProductCertificateGenerator {
/**
* 生成产品登记证书并上传图片到存储系统
* @param productInfo 产品信息
* @param fontPath 字体文件路径
* @param backgroundImagePath 背景图片路径
* @return 上传后的图片URL
* @throws Exception 处理过程中可能出现的异常
*/
public static String generateCertificateAndUpload(ProductInfo productInfo,
String fontPath,
String backgroundImagePath) throws Exception {
// 临时文件路径
String tempDir = System.getProperty("java.io.tmpdir");
String pdfPath = tempDir + File.separator + "product_cert_" + System.currentTimeMillis() + ".pdf";
String imagePath = tempDir + File.separator + "product_cert_" + System.currentTimeMillis() + ".png";
try {
// 1. 生成PDF证书
generateCertificate(pdfPath, backgroundImagePath, fontPath, productInfo);
// 2. 将PDF转换为图片
pdfToImage(pdfPath, imagePath);
// 3. 上传图片到存储
File imageFile = new File(imagePath);
return uploadToStorage(imageFile);
} finally {
// 清理临时文件
new File(pdfPath).delete();
new File(imagePath).delete();
}
}
/**
* 生成产品登记证书PDF
*/
private static void generateCertificate(String outputPath,
String backgroundImagePath,
String fontPath,
ProductInfo productInfo) throws Exception {
try (PDDocument document = new PDDocument()) {
PDPage page = new PDPage(PDRectangle.A4);
document.addPage(page);
// 加载字体
PDType0Font font = PDType0Font.load(document, new File(fontPath));
// 加载背景图片
PDImageXObject background = PDImageXObject.createFromFile(backgroundImagePath, document);
try (PDPageContentStream contentStream = new PDPageContentStream(document, page)) {
// 1. 添加背景图片
contentStream.drawImage(background, 0, 0,
page.getMediaBox().getWidth(),
page.getMediaBox().getHeight());
// 2. 添加产品信息(标题和名称居中)
addCenteredProductInfo(contentStream, page, productInfo, font);
// 3. 添加其他产品信息
addProductDetails(contentStream, page, productInfo, font);
// 4. 添加底部信息
addFooterInfo(contentStream, page, productInfo, font);
}
document.save(outputPath);
}
}
/**
* 添加居中显示的标题和产品名称
*/
private static void addCenteredProductInfo(PDPageContentStream contentStream,
PDPage page,
ProductInfo productInfo,
PDType0Font font) throws IOException {
float pageWidth = page.getMediaBox().getWidth();
float centerX = pageWidth / 2;
// 垂直起始位置(距顶部250px)
float startY = page.getMediaBox().getHeight() - 250;
// 1. 添加标题(36号字,居中)
String title = productInfo.getTitle();
float titleWidth = calculateTextWidth(font, title, 36);
contentStream.beginText();
contentStream.setFont(font, 36);
contentStream.newLineAtOffset(centerX - titleWidth/2, startY);
contentStream.showText(title);
contentStream.endText();
// 2. 添加产品名称(28号字,居中,距标题60px)
String name = productInfo.getName();
float nameWidth = calculateTextWidth(font, name, 28);
contentStream.beginText();
contentStream.setFont(font, 28);
contentStream.newLineAtOffset(centerX - nameWidth/2, startY - 60);
contentStream.showText(name);
contentStream.endText();
}
/**
* 添加产品详细信息
*/
private static void addProductDetails(PDPageContentStream contentStream,
PDPage page,
ProductInfo productInfo,
PDType0Font font) throws IOException {
float startY = page.getMediaBox().getHeight() - 400; // 从标题下移150px开始
float lineHeight = 40;
// 左侧对齐,距左边150px
addFormattedLine(contentStream, "登记编号:", productInfo.getCode(), 150, startY, font, 20);
addFormattedLine(contentStream, "登记日期:", productInfo.getCreateDate(), 150, startY - lineHeight, font, 20);
addFormattedLine(contentStream, "登记机构:", productInfo.getOrgName(), 150, startY - lineHeight * 2, font, 20);
addFormattedLine(contentStream, "数据产品持有者:", productInfo.getSupplierName(), 150, startY - lineHeight * 3, font, 20);
addFormattedLine(contentStream, "统一社会信用代码:", productInfo.getShxydm(), 150, startY - lineHeight * 4, font, 20);
}
/**
* 添加底部信息(右侧对齐)
*/
private static void addFooterInfo(PDPageContentStream contentStream,
PDPage page,
ProductInfo productInfo,
PDType0Font font) throws IOException {
float pageWidth = page.getMediaBox().getWidth();
float rightMargin = 100; // 右侧边距100px
// 1. 大写登记日期(底部右侧,距底部120px)
String createDateUpper = productInfo.getCreateDateUppercase();
float dateUpperWidth = calculateTextWidth(font, createDateUpper, 22);
contentStream.beginText();
contentStream.setFont(font, 22);
contentStream.newLineAtOffset(pageWidth - dateUpperWidth - rightMargin, 120);
contentStream.showText(createDateUpper);
contentStream.endText();
// 2. 登记日期(底部右侧,距大写日期上方30px)
String createDate = productInfo.getCreateDate();
float dateWidth = calculateTextWidth(font, createDate, 22);
contentStream.beginText();
contentStream.setFont(font, 22);
contentStream.newLineAtOffset(pageWidth - dateWidth - rightMargin, 150);
contentStream.showText(createDate);
contentStream.endText();
}
/**
* 添加格式化文本行(标签+值)
*/
private static void addFormattedLine(PDPageContentStream contentStream,
String label,
String value,
float x,
float y,
PDType0Font font,
int fontSize) throws IOException {
// 添加标签
contentStream.beginText();
contentStream.setFont(font, fontSize);
contentStream.newLineAtOffset(x, y);
contentStream.showText(label);
contentStream.endText();
// 添加值
float labelWidth = calculateTextWidth(font, label, fontSize);
contentStream.beginText();
contentStream.setFont(font, fontSize);
contentStream.newLineAtOffset(x + labelWidth, y);
contentStream.showText(value);
contentStream.endText();
}
/**
* 将PDF转换为图片
*/
private static void pdfToImage(String pdfPath, String imagePath) throws IOException {
try (PDDocument document = PDDocument.load(new File(pdfPath))) {
PDFRenderer renderer = new PDFRenderer(document);
// 高质量渲染,300 DPI,RGB格式
BufferedImage image = renderer.renderImageWithDPI(0, 300, ImageType.RGB);
ImageIO.write(image, "PNG", new File(imagePath));
}
}
/**
* 上传图片到存储系统
*/
private static String uploadToStorage(File imageFile) throws IOException {
try (InputStream is = new FileInputStream(imageFile)) {
Map<String, Object> result = ZosUtil.streamUpload(
is,
BussinessEnum.CERT.name(),
imageFile.getName(),
null
);
return result.get("fileUrl").toString();
}
}
/**
* 精确计算文本显示宽度
*/
private static float calculateTextWidth(PDType0Font font, String text, float fontSize) throws IOException {
return font.getStringWidth(text) / 1000 * fontSize;
}
/**
* 产品信息实体类
*/
@Data
public static class ProductInfo {
private String title; // 证书标题
private String name; // 产品名称
private String code; // 登记编号
private String createDate; // 登记日期
private String createDateUppercase; // 大写登记日期
private String orgName; // 登记机构
private String supplierName; // 数据产品持有者
private String shxydm; // 统一社会信用代码
}
/**
* 测试方法
*/
public static void main(String[] args) {
try {
// 产品信息
ProductInfo productInfo = new ProductInfo();
productInfo.setTitle("数据产品登记凭证");
productInfo.setName("燃气输送SCADA监测数据");
productInfo.setCode("2025320100SJA0308");
productInfo.setCreateDate("2025年05月16日");
productInfo.setCreateDateUppercase("二〇二五年五月十六日");
productInfo.setOrgName("南京市公共资源交易中心");
productInfo.setSupplierName("江苏东能天然气管网有限公司");
productInfo.setShxydm("91320623067634411F");
// 文件路径配置
String fontPath = "C:/Windows/Fonts/simhei.ttf"; // 黑体字体路径
String backgroundPath = "C:/path/to/certificate_bg.png"; // 背景图片路径
// 生成并上传证书
String imageUrl = generateCertificateAndUpload(productInfo, fontPath, backgroundPath);
System.out.println("证书图片生成成功,URL: " + imageUrl);
} catch (Exception e) {
e.printStackTrace();
}
}
}