图片分页

This commit is contained in:
lijiazhuo
2026-01-19 14:36:25 +08:00
parent e750e92a77
commit 777f26027e
9 changed files with 1973 additions and 142 deletions

View File

@ -2,87 +2,182 @@ package com.example.sso.dao;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.font.PdfFont;
import com.itextpdf.kernel.font.PdfFontFactory;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.pdf.canvas.PdfCanvas;
import com.itextpdf.layout.Canvas;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.properties.TextAlignment;
import com.itextpdf.layout.properties.VerticalAlignment;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.Event;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
public class Photo {
// 使用 ThreadLocal 存储每个线程的独立变量
private static final ThreadLocal<PdfDocument> pdfDocThreadLocal = new ThreadLocal<>();
private static final ThreadLocal<Document> documentThreadLocal = new ThreadLocal<>();
private static final ThreadLocal<String> currentPdfPathThreadLocal = new ThreadLocal<>();
private static final ThreadLocal<PdfFont> chineseFontThreadLocal = new ThreadLocal<>();
private static PdfDocument pdfDoc = null;
private static Document document = null;
private static String currentPdfPath = null;
// 存储每页的页脚文本使用ThreadLocal确保线程安全
private static final ThreadLocal<List<FooterText>> footerTextsThreadLocal = new ThreadLocal<>();
// 页脚文本类
private static class FooterText {
String text;
float x;
float y;
DeviceRgb color;
FooterText(String text, float x, float y, DeviceRgb color) {
this.text = text;
this.x = x;
this.y = y;
this.color = color;
}
}
// 页脚事件处理器
private static class FooterEventHandler implements IEventHandler {
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfCanvas canvas = new PdfCanvas(docEvent.getPage());
Rectangle pageSize = docEvent.getPage().getPageSize();
// 获取当前线程的字体和页脚文本
PdfFont chineseFont = chineseFontThreadLocal.get();
List<FooterText> footerTexts = footerTextsThreadLocal.get();
if (chineseFont == null || footerTexts == null || footerTexts.isEmpty()) {
canvas.release();
return;
}
// 创建Canvas用于绘制
Canvas pageCanvas = new Canvas(canvas, pageSize);
// 绘制所有页脚文本
for (FooterText footerText : footerTexts) {
Paragraph paragraph = new Paragraph(footerText.text)
.setFont(chineseFont)
.setFontSize(12);
if (footerText.color != null) {
paragraph.setFontColor(footerText.color);
}
// 在指定位置绘制文本
pageCanvas.showTextAligned(
paragraph,
footerText.x,
footerText.y,
TextAlignment.LEFT,
VerticalAlignment.BOTTOM
);
}
canvas.release();
}
}
/**
* 开始创建PDF
*/
public static void startPDF(String pdfPath) throws IOException {
if (pdfDoc != null) {
// 获取当前线程的变量
PdfDocument pdfDoc = pdfDocThreadLocal.get();
Document document = documentThreadLocal.get();
// 如果已经存在,先关闭
if (pdfDoc != null || document != null) {
closePDF();
}
/* currentPdfPath = pdfPath;
// 创建新的PDF文档
currentPdfPathThreadLocal.set(pdfPath);
PdfWriter writer = new PdfWriter(pdfPath);
pdfDoc = new PdfDocument(writer);
// document = new Document(pdfDoc);
// 创建文档时设置0边距
// 注册页脚事件处理器
FooterEventHandler footerHandler = new FooterEventHandler();
pdfDoc.addEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
// 使用标准A4纸
document = new Document(pdfDoc, PageSize.A4, false);
// 设置页边距为0
document.setMargins(0, 0, 0, 0);
// 或者使用具体的值
// document.setMargins(0f, 0f, 0f, 0f);
// 移除默认的页眉页脚
pdfDoc.getDefaultPageSize();*/
currentPdfPath = pdfPath;
PdfWriter writer = new PdfWriter(pdfPath);
pdfDoc = new PdfDocument(writer);
// A4纸的标准宽度是595点约210mm高度自定义
// PageSize.A4.getWidth() 获取A4宽度
PageSize customPageSize = new PageSize(PageSize.A4.getWidth(), 842*20);
document = new Document(pdfDoc, customPageSize, false);
document.setMargins(0, 0, 0, 0);
// 初始化中文字体
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
// 初始化页脚文本列表
footerTextsThreadLocal.set(new ArrayList<>());
System.out.println("开始创建PDF: " + pdfPath);
// 保存到ThreadLocal
pdfDocThreadLocal.set(pdfDoc);
documentThreadLocal.set(document);
chineseFontThreadLocal.set(chineseFont);
System.out.println(Thread.currentThread().getName() + " 开始创建PDF: " + pdfPath);
}
/**
* 设置页脚文本(会在每一页底部显示)
*/
public static void setFooterText(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
// 获取页脚文本列表
List<FooterText> footerTexts = footerTextsThreadLocal.get();
if (footerTexts == null) {
footerTexts = new ArrayList<>();
footerTextsThreadLocal.set(footerTexts);
}
// 清空原有页脚,添加新的页脚文本
footerTexts.clear();
footerTexts.add(new FooterText(text, 50F, 30F, (DeviceRgb) DeviceRgb.BLACK));
System.out.println(Thread.currentThread().getName() + " 已设置页脚文本: " + text);
}
/**
* 添加图片章节
*/
public static void appendImageSection(String imagePath) throws IOException {
Document document = documentThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
// 添加章节标题
// document.add(new Paragraph("图片部分")
// .setFontSize(20)
// .setBold()
// .setMarginTop(20));
// 添加图片
if (imagePath != null && new File(imagePath).exists()) {
ImageData imageData = ImageDataFactory.create(imagePath);
Image image = new Image(imageData);
// image.setAutoScale(true);
image.setHorizontalAlignment(com.itextpdf.layout.properties.HorizontalAlignment.CENTER);
image.setMarginTop(5);
image.setMarginBottom(5);
@ -90,55 +185,48 @@ public class Photo {
image.setWidth(200);
document.add(image);
// document.add(new Paragraph("图片说明")
// .setFontSize(10)
// .setItalic()
// .setTextAlignment(com.itextpdf.layout.properties.TextAlignment.CENTER));
}
System.out.println("已添加图片章节");
System.out.println(Thread.currentThread().getName() + " 已添加图片章节");
}
/**
* 添加文本章节
*/
public static void appendTextSection(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
// 添加章节标题
document.add(new Paragraph(text)
.setFont(chineseFont)
.setFontSize(20)
.setBold()
.setMarginTop(20));
/* // 添加文本
if (text != null && !text.trim().isEmpty()) {
String[] paragraphs = text.split("\n");
for (String para : paragraphs) {
document.add(new Paragraph(para)
.setFontSize(12)
.setMarginBottom(10));
}
}*/
System.out.println("已添加文本章节");
System.out.println(Thread.currentThread().getName() + " 已添加文本章节");
}
public static void TextAlignment(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
// 创建段落并设置居中对齐
Paragraph paragraph = new Paragraph(text)
@ -146,52 +234,120 @@ public class Photo {
.setFontSize(20)
.setBold()
.setMarginTop(20)
.setTextAlignment(TextAlignment.CENTER); // 添加这行实现居中
.setTextAlignment(TextAlignment.CENTER);
document.add(paragraph);
System.out.println("已添加文本章节");
System.out.println(Thread.currentThread().getName() + " 已添加文本章节");
}
public static void appendTextSections(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
PdfFont chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
// 添加章节标题
// 设置绝对位置在页面底部
Paragraph paragraph = new Paragraph(text);
float pageHeight = PageSize.A4.getHeight(); // 842点
float bottomMargin = 50; // 距离底部50点
float leftMargin = 400; // 左边距
List<FooterText> footerTexts = footerTextsThreadLocal.get();
if (footerTexts == null) {
footerTexts = new ArrayList<>();
footerTextsThreadLocal.set(footerTexts);
}
paragraph.setFixedPosition(leftMargin, bottomMargin, PageSize.A4.getWidth() - 40).setFont(chineseFont);
document.add(paragraph);
// 添加白色文本
footerTexts.add(new FooterText(text, 475F, 25f, (DeviceRgb) DeviceRgb.WHITE));
/* // 添加文本
if (text != null && !text.trim().isEmpty()) {
String[] paragraphs = text.split("\n");
for (String para : paragraphs) {
document.add(new Paragraph(para)
.setFontSize(12)
.setMarginBottom(10));
}
}*/
System.out.println(Thread.currentThread().getName() + " 已添加白色页脚文本: " + text);
}
System.out.println("已添加文本章节");
public static void appendTextSections1(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
List<FooterText> footerTexts = footerTextsThreadLocal.get();
if (footerTexts == null) {
footerTexts = new ArrayList<>();
footerTextsThreadLocal.set(footerTexts);
}
// 添加黑色文本
footerTexts.add(new FooterText(text, 310f, 25f, null));
System.out.println(Thread.currentThread().getName() + " 已添加黑色页脚文本: " + text);
}
public static void appendTextSectionss(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
List<FooterText> footerTexts = footerTextsThreadLocal.get();
if (footerTexts == null) {
footerTexts = new ArrayList<>();
footerTextsThreadLocal.set(footerTexts);
}
// 添加白色文本
footerTexts.add(new FooterText(text, 400F, 50F, (DeviceRgb) DeviceRgb.WHITE));
System.out.println(Thread.currentThread().getName() + " 已添加白色页脚文本: " + text);
}
public static void appendTextSectionss1(String text) throws IOException {
Document document = documentThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document == null) {
throw new IllegalStateException("请先调用 startPDF() 方法");
}
if (chineseFont == null) {
chineseFont = PdfFontFactory.createFont("STSong-Light", "UniGB-UCS2-H");
chineseFontThreadLocal.set(chineseFont);
}
List<FooterText> footerTexts = footerTextsThreadLocal.get();
if (footerTexts == null) {
footerTexts = new ArrayList<>();
footerTextsThreadLocal.set(footerTexts);
}
// 添加黑色文本
footerTexts.add(new FooterText(text, 310, 50, null));
System.out.println(Thread.currentThread().getName() + " 已添加黑色页脚文本: " + text);
}
/**
* 添加分页符
*/
public static void addPageBreak() {
Document document = documentThreadLocal.get();
if (document != null) {
document.add(new com.itextpdf.layout.element.AreaBreak());
System.out.println("已添加分页符");
System.out.println(Thread.currentThread().getName() + " 已添加分页符");
}
}
@ -199,17 +355,30 @@ public class Photo {
* 关闭并保存PDF
*/
public static void closePDF() {
// 获取当前线程的变量
Document document = documentThreadLocal.get();
PdfDocument pdfDoc = pdfDocThreadLocal.get();
String currentPdfPath = currentPdfPathThreadLocal.get();
PdfFont chineseFont = chineseFontThreadLocal.get();
if (document != null) {
document.close();
document = null;
}
if (pdfDoc != null) {
pdfDoc.close();
pdfDoc = null;
documentThreadLocal.remove();
}
System.out.println("PDF已保存: " + currentPdfPath);
currentPdfPath = null;
if (pdfDoc != null) {
pdfDoc.close();
pdfDocThreadLocal.remove();
}
if (chineseFont != null) {
chineseFontThreadLocal.remove();
}
if (currentPdfPath != null) {
System.out.println(Thread.currentThread().getName() + " PDF已保存: " + currentPdfPath);
currentPdfPathThreadLocal.remove();
}
}
/**
@ -218,24 +387,21 @@ public class Photo {
public static void createPDFWithMultipleSections(String pdfPath) throws IOException {
startPDF(pdfPath);
// 添加各种内容
appendTextSection("这是文档的第一部分\n包含一些介绍性的文字");
appendImageSection("image1.jpg");
appendTextSection("这是图片1的描述文字");
appendImageSection("image2.png");
appendTextSection("这是图片2的描述文字");
// 关闭并保存
closePDF();
try {
// 添加各种内容
appendTextSection("这是文档的第一部分\n包含一些介绍性的文字");
appendImageSection("image1.jpg");
appendTextSection("这是图片1的描述文字");
appendImageSection("image2.png");
appendTextSection("这是图片2的描述文字");
} finally {
// 确保资源被释放
closePDF();
}
}
public static void createSimpleChinesePDF() throws IOException {
// 这个方法不使用ThreadLocal变量因为它自己创建PDF
// 1. 创建PDF
PdfWriter writer = new PdfWriter("D:\\下载模板\\a.pdf");
PdfDocument pdfDoc = new PdfDocument(writer);
@ -258,17 +424,13 @@ public class Photo {
// 4. 关闭文档
document.close();
pdfDoc.close();
System.out.println("PDF创建完成");
}
public static void main(String[] args) {
try {
createSimpleChinesePDF();
} catch (IOException e) {
System.out.println("错误: " + e.getMessage());
System.out.println("请确保添加了 font-asian 依赖");
}
}
/**
* 测试多线程并发
*/
}
}