XML
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>5.0.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>8.0.0</version>
</dependency>
word模版
java的ftl文件 也就是先写好html然后后缀改成 ftl 用freeMarker渲染
模版文件 在资源里边
java用代码熏染
java
package com.mxbc.mos.utils;
import cn.hutool.core.io.resource.ResourceUtil;
import com.itextpdf.html2pdf.ConverterProperties;
import com.itextpdf.html2pdf.HtmlConverter;
import com.itextpdf.kernel.colors.DeviceRgb;
import com.itextpdf.kernel.events.Event;
import com.itextpdf.kernel.events.IEventHandler;
import com.itextpdf.kernel.events.PdfDocumentEvent;
import com.itextpdf.kernel.geom.PageSize;
import com.itextpdf.kernel.geom.Rectangle;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfPage;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.font.FontProvider;
import com.itextpdf.layout.properties.TextAlignment;
import com.mxbc.mos.vo.CreateTaxFreeVo;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.Version;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
@Slf4j
public class PDFTemplateUtil {
public static void main(String[] args) throws Exception {
ByteArrayOutputStream baos = null;
try {
CreateTaxFreeVo createTaxFreeVo = new CreateTaxFreeVo();
createTaxFreeVo.setShopCode("门店编码");
createTaxFreeVo.setShopName("门店名称");
createTaxFreeVo.setCustomer("签约名称");
createTaxFreeVo.setNaxNo("税号");
createTaxFreeVo.setOrderDate("订单日期");
createTaxFreeVo.setOrderNumber("订单号");
createTaxFreeVo.setWarehouse("发货仓");
createTaxFreeVo.setCurrency("币种");
createTaxFreeVo.setTotalPrice("总价");
createTaxFreeVo.setDiscountPrice("折扣额");
createTaxFreeVo.setNoTaxTotalPrice("不含税总额");
createTaxFreeVo.setTaxPrice("税额");
createTaxFreeVo.setTaxTotalPrice("含税总价");
List<CreateTaxFreeVo.MaterialDetail> materialDetails = new ArrayList<>();
for (int i = 0; i < 10; i++) {
CreateTaxFreeVo.MaterialDetail materialDetail = new CreateTaxFreeVo.MaterialDetail();
materialDetail.setMaterialName("物料名称");
materialDetail.setSpecification("规格");
materialDetail.setMaterialCount("数量");
materialDetail.setMaterialUnit("单位");
materialDetail.setMaterialPrice("价格");
materialDetail.setMaterialTotalPrice("总价");
materialDetails.add(materialDetail);
}
createTaxFreeVo.setMaterialVos(materialDetails);
PDFTemplateUtil.createPDF(createTaxFreeVo, "id_tax_free_order.ftl", Files.newOutputStream(Paths.get("C:\\Users\\yuanhui\\Desktop\\1.pdf")));
} catch (Exception e) {
e.printStackTrace();
throw new Exception("导出失败:" + e.getMessage());
} finally {
if (baos != null) {
baos.close();
}
}
}
public static final String ENCODING = "UTF-8";
public static void createPDF(Object data, String templateFileName, OutputStream outputStream) throws Exception {
// 创建一个FreeMarker实例, 负责管理FreeMarker模板的Configuration实例
Configuration cfg = new Configuration(new Version(2, 3, 32));
// 设置模板的编码格式
cfg.setEncoding(Locale.CHINA, ENCODING);
cfg.setClassForTemplateLoading(PDFTemplateUtil.class, "/template");
// 获取模板文件
Template template = cfg.getTemplate(templateFileName, ENCODING);
StringWriter writer = new StringWriter();
// 将数据输出到html中
template.process(data, writer);
writer.flush();
String html = writer.toString();
ConverterProperties converterProperties = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.addStandardPdfFonts();
//中文
fp.addFont(IOUtils.toByteArray(ResourceUtil.getStream("template/font/STSong-Light.ttf")));
//自定义字体
fp.addFont(IOUtils.toByteArray(ResourceUtil.getStream("template/font/AlibabaSansThai-Rg.ttf")));
// // .ttf 字体所在目录
// fp.addDirectory(ResourceUtil.getResource("template/font").getPath());
converterProperties.setFontProvider(fp);
// html中使用的图片等资源目录(图片也可以直接用url或者base64格式而不放到资源里)
//converterProperties.setBaseUri(TEMPLATE_PATH + File.separator + "img");
PdfWriter pdfWriter = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(pdfWriter);
pdfDocument.setDefaultPageSize(PageSize.A4);
pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, new PageMarker());
HtmlConverter.convertToPdf(html, pdfDocument, converterProperties);
//HtmlConverter.convertToPdf(html, pdfWriter);
}
@AllArgsConstructor
public static class PageMarker implements IEventHandler {
@Override
public void handleEvent(Event event) {
final PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
final PdfDocument pdfDoc = docEvent.getDocument();
final PdfPage page = docEvent.getPage();
final Rectangle pageSize = page.getPageSize();
final int pageNumber = pdfDoc.getPageNumber(page);
final Document doc = new Document(pdfDoc);
Paragraph p = new Paragraph(pageNumber + "/" + pdfDoc.getNumberOfPages());
p.setFontSize(7f);
p.setBold();
p.setFontColor(new DeviceRgb(22, 28, 39));
p.setFixedPosition(pageSize.getWidth() / 2 - 25, 20, 50);
p.setTextAlignment(TextAlignment.CENTER);
doc.add(p);
}
}
}
pdf合并
java
package com.mxbc.mos.utils;
import cn.hutool.core.date.DateUtil;
import com.itextpdf.io.image.ImageData;
import com.itextpdf.io.image.ImageDataFactory;
import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfReader;
import com.itextpdf.kernel.pdf.PdfWriter;
import com.itextpdf.kernel.utils.PdfMerger;
import com.itextpdf.layout.Document;
import com.itextpdf.layout.element.AreaBreak;
import com.itextpdf.layout.element.Image;
import com.itextpdf.layout.properties.AreaBreakType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
public class PDFUtil {
private static final Logger logger = LoggerFactory.getLogger(PDFUtil.class);
/**
* 本地测试用
* 将给定List集合中的pdf文档,按照顺序依次合并,生成最终的目标PDF文档
*
* @param pdfPathLists 待合并的PDF文档路径集合,可以是本地PDF文档,也可以是网络上的PDF文档
* @param destPath 目标合并生成的PDF文档路径
*/
public static boolean mergeMultiplePdfs(List<String> pdfPathLists, String destPath) {
try {
int size = pdfPathLists.size();
byte[] pdfData = getPdfBytes(pdfPathLists.get(0));
for (int i = 1; i < size; i++) {
pdfData = mergePdfBytes(pdfData, getPdfBytes(pdfPathLists.get(i)));
}
if (pdfData != null) {
FileOutputStream fis = new FileOutputStream(destPath);
fis.write(pdfData);
fis.close();
}
return true;
} catch (Exception e) {
logger.error("合并PDF异常:", e);
}
return false;
}
/**
* 多个pdf合并 可以传url
*
* @param pdfPathOrFileUrlLists 待合并的PDF文档路径集合,可以是本地PDF文档,也可以是网络上的PDF文档
* @return 字节数组 方便上传阿里云等云资源
*/
public static byte[] mergeMultiplePdfs(List<String> pdfPathOrFileUrlLists) {
if (pdfPathOrFileUrlLists == null || pdfPathOrFileUrlLists.isEmpty()) {
return null;
}
byte[] pdfData;
try {
int size = pdfPathOrFileUrlLists.size();
pdfData = getPdfBytes(pdfPathOrFileUrlLists.get(0));
for (int i = 1; i < size; i++) {
pdfData = mergePdfBytes(pdfData, getPdfBytes(pdfPathOrFileUrlLists.get(i)));
}
} catch (Exception e) {
logger.error("合并PDF异常:", e);
return null; // 返回 null 以指示异常
}
return pdfData; // 返回合并后的PDF字节数组
}
/**
* 将给定集合中的图片合并到一个pdf文档里面
*
* @param imagePathList 图片路径集合
* @param destPath 合并之后的PDF文档
*/
public static boolean mergeImagesToPdf(List<String> imagePathList, String destPath) {
try {
PdfDocument pdfDocument = new PdfDocument(new PdfWriter(destPath));
Document document = new Document(pdfDocument);
if (imagePathList != null && imagePathList.size() > 0) {
int size = imagePathList.size();
for (int i = 0; i < size; i++) {
String imgPath = imagePathList.get(i);
ImageData imageData;
if (imgPath.startsWith("http://") || imgPath.startsWith("https://")) {
imageData = ImageDataFactory.create(new URL(imgPath));
} else {
imageData = ImageDataFactory.create(imgPath);
}
Image image = new Image(imageData);
/*
设置旋转的弧度值,默认是逆时针旋转的。
弧度、角度换算公式:
1° = PI / 180°
1 rad = 180° / PI
*/
image.setRotationAngle(-Math.PI / 2); // 顺时针旋转90°
// 设置图片自动缩放,即:图片宽高自适应
image.setAutoScale(true);
document.add(image);
if (i != size - 1) {
// 最后一页不需要新增空白页
document.add(new AreaBreak(AreaBreakType.NEXT_PAGE));
}
}
}
pdfDocument.close();
return true;
} catch (Exception e) {
logger.error("合并图片到PDF异常:", e);
}
return false;
}
/**
* 基于内存中的字节数组进行PDF文档的合并
*
* @param firstPdf 第一个PDF文档
* @param secondPdf 第二个PDF文档
*/
private static byte[] mergePdfBytes(byte[] firstPdf, byte[] secondPdf) throws IOException {
if (firstPdf == null || secondPdf == null) {
return null;
}
// 创建字节数组,基于内存进行合并
// 合并的pdf文件对象
// 合并对象
try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
PdfDocument destDoc = new PdfDocument(new PdfWriter(baos));
PdfDocument firstDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(firstPdf)));
PdfDocument secondDoc = new PdfDocument(new PdfReader(new ByteArrayInputStream(secondPdf)))) {
PdfMerger merger = new PdfMerger(destDoc);
merger.merge(firstDoc, 1, firstDoc.getNumberOfPages());
merger.merge(secondDoc, 1, secondDoc.getNumberOfPages());
// 关闭文档流
merger.close();
return baos.toByteArray();
} catch (IOException e) {
throw new IOException("Error merging PDF documents", e);
}
}
/**
* 将pdf文档转换成字节数组
*
* @param pdf PDF文档路径
* @return 返回对应PDF文档的字节数组
*/
private static byte[] getPdfBytes(String pdf) throws Exception {
if (StringUtils.isBlank(pdf)) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
InputStream is;
if (pdf.startsWith("http://") || pdf.startsWith("https://")) {
is = new URL(pdf).openStream();
} else {
is = new FileInputStream(pdf);
}
byte[] data = new byte[2048];
int len;
while ((len = is.read(data)) != -1) {
out.write(data, 0, len);
}
return out.toByteArray();
}
public static void main(String[] args) throws FileNotFoundException {
// 图片合并之后生成的PDF路径
//String imagePath = "C:\\Users\\yuanhui\\Desktop\\marge.pdf";
//List<String> imageList = new ArrayList<>();
//imageList.add("https://mxpos-sg.oss-ap-southeast-1.aliyuncs.com/app/mos/order/invoice/booking/test/INV-230700003.pdf");
//imageList.add("https://mxpos-sg.oss-ap-southeast-1.aliyuncs.com/app/mos/order/invoice/booking/test/INV-230700003.pdf");
// 先合并图片
//PDFUtil.mergeImagesToPdf(imageList, imagePath);
long startTime = System.currentTimeMillis();
System.out.println("pdf合并程序开始时间" + DateUtil.now());
// 在合并PDF
String destPath = "C:/Users/yuanhui/Desktop/marge.pdf";
List<String> pdfPath = new ArrayList<>();
pdfPath.add("https://mxpos-sg.oss-ap-southeast-1.aliyuncs.com/app/mos/order/invoice/booking/test/INV-230700003.pdf");
PDFUtil.mergeMultiplePdfs(pdfPath, destPath);
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:" + (endTime - startTime) + "ms");
System.out.println("pdf程序合并结束时间:" + DateUtil.now());
}
}
成品