Files
campus-activity-system/server/src/main/java/com/campus/activity/util/PdfUtil.java

273 lines
11 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package com.campus.activity.util;
import com.campus.activity.entity.Activity;
import com.campus.activity.entity.Registration;
import com.campus.activity.entity.User;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.EncodeHintType;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.itextpdf.io.font.PdfEncodings;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.ColorConstants;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.draw.SolidLine;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.*;
import com.itextpdf.layout.properties.HorizontalAlignment;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.UnitValue;
import com.itextpdf.layout.properties.VerticalAlignment;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
public class PdfUtil {
private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm");
public static byte[] generateTicketPdf(User user, Activity activity, Registration registration) {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdf = new PdfDocument(writer);
Document document = new Document(pdf);
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
PdfFont boldChineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H", PdfFontFactory.EmbeddingStrategy.PREFER_EMBEDDED);
createHeader(document, chineseFont, boldChineseFont);
createDivider(document);
createActivityInfo(document, activity, chineseFont, boldChineseFont);
createParticipantAndQrCode(document, user, registration, chineseFont, boldChineseFont);
createFooter(document, chineseFont);
document.close();
return outputStream.toByteArray();
} catch (Exception e) {
throw new RuntimeException("生成电子票失败", e);
}
}
private static void createHeader(Document document, PdfFont font, PdfFont boldFont) {
Paragraph title = new Paragraph("校园活动电子票")
.setFont(boldFont)
.setFontSize(20)
.setBold()
.setTextAlignment(TextAlignment.CENTER)
.setMarginTop(15)
.setMarginBottom(5);
Paragraph subtitle = new Paragraph("Campus Activity E-Ticket")
.setFont(font)
.setFontSize(9)
.setTextAlignment(TextAlignment.CENTER)
.setFontColor(ColorConstants.GRAY)
.setMarginBottom(10);
document.add(title);
document.add(subtitle);
}
private static void createDivider(Document document) {
SolidLine line = new SolidLine(1f);
line.setColor(ColorConstants.LIGHT_GRAY);
LineSeparator separator = new LineSeparator(line)
.setMarginBottom(15)
.setWidth(UnitValue.createPercentValue(100));
document.add(separator);
}
private static void createActivityInfo(Document document, Activity activity, PdfFont font, PdfFont boldFont) {
Paragraph sectionTitle = new Paragraph("活动信息")
.setFont(boldFont)
.setFontSize(12)
.setBold()
.setMarginBottom(8);
document.add(sectionTitle);
Table infoTable = new Table(UnitValue.createPercentArray(new float[]{25, 75}))
.setMarginBottom(15)
.setWidth(UnitValue.createPercentValue(90))
.setHorizontalAlignment(HorizontalAlignment.CENTER);
addTableRow(infoTable, "活动名称", activity.getTitle(), font, boldFont, true);
addTableRow(infoTable, "活动时间", formatDateTime(activity.getStartTime()) + " ~ " + formatDateTime(activity.getEndTime()), font, boldFont, false);
addTableRow(infoTable, "活动地点", activity.getLocation(), font, boldFont, false);
document.add(infoTable);
}
private static void createParticipantAndQrCode(Document document, User user, Registration registration, PdfFont font, PdfFont boldFont) {
Table mainTable = new Table(UnitValue.createPercentArray(new float[]{60, 40}))
.setWidth(UnitValue.createPercentValue(90))
.setHorizontalAlignment(HorizontalAlignment.CENTER)
.setMarginBottom(15);
Cell leftCell = new Cell()
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER)
.setPadding(0);
Cell rightCell = new Cell()
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER)
.setPadding(0)
.setVerticalAlignment(VerticalAlignment.MIDDLE);
Paragraph participantTitle = new Paragraph("参与者信息")
.setFont(boldFont)
.setFontSize(12)
.setBold()
.setMarginBottom(8);
Table participantTable = new Table(UnitValue.createPercentArray(new float[]{25, 75}))
.setMarginBottom(0);
addTableRow(participantTable, "学生姓名", user.getName(), font, boldFont, true);
addTableRow(participantTable, "学号", user.getStudentId(), font, boldFont, false);
addTableRow(participantTable, "电子票号", registration.getTicketCode(), font, boldFont, false);
addTableRow(participantTable, "报名时间", formatDateTime(registration.getCreatedAt()), font, boldFont, false);
leftCell.add(participantTitle);
leftCell.add(participantTable);
try {
byte[] qrCodeBytes = generateQrCode(registration.getTicketCode());
Image qrCodeImage = new Image(ImageDataFactory.create(qrCodeBytes))
.setWidth(120)
.setHeight(120)
.setHorizontalAlignment(HorizontalAlignment.CENTER)
.setMarginBottom(5);
Paragraph qrTitle = new Paragraph("签到二维码")
.setFont(font)
.setFontSize(12)
.setBold()
.setTextAlignment(TextAlignment.CENTER)
.setMarginBottom(8);
Paragraph ticketCode = new Paragraph("票号:" + registration.getTicketCode())
.setFont(font)
.setFontSize(9)
.setTextAlignment(TextAlignment.CENTER)
.setFontColor(ColorConstants.GRAY)
.setMarginBottom(0);
rightCell.add(qrTitle);
rightCell.add(qrCodeImage);
rightCell.add(ticketCode);
} catch (Exception e) {
throw new RuntimeException("生成二维码失败", e);
}
mainTable.addCell(leftCell);
mainTable.addCell(rightCell);
document.add(mainTable);
}
private static void createFooter(Document document, PdfFont font) {
SolidLine line = new SolidLine(1f);
line.setColor(ColorConstants.LIGHT_GRAY);
LineSeparator separator = new LineSeparator(line)
.setMarginTop(15)
.setMarginBottom(10)
.setWidth(UnitValue.createPercentValue(100));
document.add(separator);
Paragraph notice = new Paragraph("温馨提示请妥善保管此电子票活动当天凭此票签到入场。请提前15分钟到达活动现场出示电子票二维码签到。")
.setFont(font)
.setFontSize(9)
.setTextAlignment(TextAlignment.CENTER)
.setMarginBottom(10);
document.add(notice);
Paragraph footer = new Paragraph("本电子票由校园活动系统自动生成 | 生成时间:" + LocalDateTime.now().format(DATE_TIME_FORMATTER))
.setFont(font)
.setFontSize(7)
.setTextAlignment(TextAlignment.CENTER)
.setFontColor(ColorConstants.GRAY)
.setMarginBottom(15);
document.add(footer);
}
private static void addTableRow(Table table, String label, String value, PdfFont font, PdfFont boldFont, boolean isFirstRow) {
Cell labelCell = new Cell()
.add(new Paragraph(label).setFont(boldFont).setFontSize(10))
.setPadding(4)
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER);
Cell valueCell = new Cell()
.add(new Paragraph(value != null ? value : "").setFont(font).setFontSize(10))
.setPadding(4)
.setBorder(com.itextpdf.layout.borders.Border.NO_BORDER);
if (isFirstRow) {
labelCell.setBackgroundColor(new com.itextpdf.kernel.colors.DeviceRgb(240, 240, 240));
valueCell.setBackgroundColor(new com.itextpdf.kernel.colors.DeviceRgb(240, 240, 240));
}
table.addCell(labelCell);
table.addCell(valueCell);
}
private static String formatDateTime(LocalDateTime dateTime) {
if (dateTime == null) {
return "待定";
}
return dateTime.format(DATE_TIME_FORMATTER);
}
private static String formatDescription(String description) {
if (description == null || description.isEmpty()) {
return "暂无描述";
}
if (description.length() > 100) {
return description.substring(0, 100) + "...";
}
return description;
}
private static String formatRegistrationStatus(Integer status) {
if (status == null) {
return "未知";
}
switch (status) {
case 0:
return "已取消";
case 1:
return "已报名";
case 2:
return "已签到";
default:
return "未知";
}
}
private static byte[] generateQrCode(String content) throws IOException {
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
QRCodeWriter qrCodeWriter = new QRCodeWriter();
Map<EncodeHintType, Object> hints = new HashMap<>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
hints.put(EncodeHintType.MARGIN, 2);
BitMatrix bitMatrix = qrCodeWriter.encode(content, BarcodeFormat.QR_CODE, 300, 300, hints);
MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
return outputStream.toByteArray();
} catch (com.google.zxing.WriterException e) {
throw new RuntimeException("生成二维码失败", e);
}
}
}