工作中经常遇到pdf与word转换场景,我们找到一个好用的工具。
pom:
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.4.2</version>
</dependency>
<dependency>
<groupId>com.itextpdf.tool</groupId>
<artifactId>xmlworker</artifactId>
<version>5.4.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.xhtmlrenderer</groupId>
<artifactId>flying-saucer-pdf</artifactId>
<version>9.0.3</version>
</dependency>
工具类:PdfUtil
public class PdfUtil {
private static final Logger logger = LoggerFactory.getLogger(PdfUtil.class);
/**
* 字体文件目录
*/
public static final String FONTS_PATH = "/fonts";
private PdfUtil() {}
public static String createPdf(String filePath, String fileName, String content) {
return createPdf(filePath, fileName, content, null);
}
/**
* 生成pdf文件
* @param filePath
* @param content
* @param data
*/
public static String createPdf(String filePath, String fileName, String content, Object data) {
if (!FileUtil.exist(filePath)) {
FileUtil.mkdir(filePath);
}
FileOutputStream outputStream=null;
try{
File file = FileUtil.file(filePath, fileName);
//设置输出路径
outputStream=new FileOutputStream(file);
//设置文档大小
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
//设置页眉页脚
PDFHeaderFooter headerFooter=new PDFHeaderFooter();
PDFBuilder builder = new PDFBuilder(headerFooter, data);
builder.setPresentFontSize(10);
writer.setPageEvent(builder);
//输出为PDF文件
convertToPDF(writer,document,content);
return file.getPath();
}catch(Exception ex){
throw new RuntimeException("PDF export to File fail",ex);
}finally{
IOUtils.closeQuietly(outputStream);
}
}
private static void convertToPDF(PdfWriter writer,Document document,String htmlString){
//获取字体路径
String fontPath = getFontsPath();
document.open();
try {
XMLWorkerHelper.getInstance().parseXHtml(writer,document,
new ByteArrayInputStream(htmlString.getBytes("utf-8")),
XMLWorkerHelper.class.getResourceAsStream("/default.css"),
Charset.forName("UTF-8"),new XMLWorkerFontProvider(fontPath));
} catch (IOException e) {
logger.error("", e);
throw new RuntimeException("PDF文件生成异常",e);
}finally {
if (null != document) {
document.close();
}
}
}
/**
* @description 获取字体设置路径
*/
public static String getFontsPath() {
String fontPath = System.getProperty("user.home") + FONTS_PATH;
return fontPath;
}
}
构建实体:PDFBuilder
public class PDFBuilder extends PdfPageEventHelper {
private static Logger log= LoggerFactory.getLogger(PDFBuilder.class);
//字体文件名
private String fontFileName;
// 基础字体对象
private BaseFont bf;
// 利用基础字体生成的字体对象,一般用于生成中文文字
private Font fontDetail;
//文档字体大小
private int fontSize = 12;
//模板
private PdfTemplate template;
//数据实体
private Object data;
//页眉页脚定制接口
private HeaderFooterBuilder headerFooterBuilder;
private static final String FONT_FILE = "ping_fang_light.ttf";
private static final String[] FONTS = new String[]{"ping_fang_bold.ttf","ping_fang_light.ttf","ping_fang_regular.ttf","SIMLI.TTF",
"FZXBS_GBK.TTF","FS_GB2312.ttf","FZXBSJW.TTF"};
//不允许空的构造方法
private PDFBuilder() {
}
public PDFBuilder(HeaderFooterBuilder headerFooterBuilder) {
this(headerFooterBuilder,null);
}
public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data) {
this(headerFooterBuilder,data,FONT_FILE);
}
public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data, String fontFileName) {
this(headerFooterBuilder,data,fontFileName,12);
}
public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data, String fontFileName, int fontSize) {
this.headerFooterBuilder = headerFooterBuilder;
this.data=data;
this.fontFileName=fontFileName;
this.fontSize=fontSize;
initFontResource();
}
public void onOpenDocument(PdfWriter writer, Document document) {
template = writer.getDirectContent().createTemplate(50, 50);
}
/**
*
* 关闭每页的时候,写入页眉,页脚等
*
*/
public void onEndPage(PdfWriter writer, Document document) {
this.addPage(writer, document);
}
//加分页
private void addPage(PdfWriter writer, Document document){
if(headerFooterBuilder !=null){
//1.初始化字体
initFront();
//2.写入页眉
headerFooterBuilder.writeHeader(writer,document,data,fontDetail,template);
//3.写入前半部分页脚
headerFooterBuilder.writeFooter(writer,document,data,fontDetail,template);
}
}
/**
*
* 关闭文档时,替换模板,完成整个页眉页脚组件
*
*/
public void onCloseDocument(PdfWriter writer, Document document) {
if(headerFooterBuilder !=null){
template.beginText();
template.setFontAndSize(bf,fontSize);
String replace= headerFooterBuilder.getReplaceOfTemplate(writer,document,data);
template.showText(replace);
template.endText();
template.closePath();
}
}
/**
* @description 初始化字体
*/
private void initFront(){
if(StringUtils.isEmpty(fontFileName)){
throw new RuntimeException("PDF文档字体未设置!");
}
try {
if (bf == null) {
//添加字体,以支持中文
String fontPath =PdfUtil.getFontsPath() + "/" + fontFileName;
//创建基础字体
bf = BaseFont.createFont(fontPath,BaseFont.IDENTITY_H,BaseFont.EMBEDDED);
}
if (fontDetail == null) {
fontDetail = new Font(bf, fontSize, Font.NORMAL);// 数据体字体
log.info("PDF文档字体初始化完成!");
}
} catch (DocumentException e) {
log.error("字体初始化失败{}", ExceptionUtils.getFullStackTrace(e));
throw new RuntimeException("字体初始化失败",e);
} catch (IOException e) {
log.error("字体初始化失败{}", ExceptionUtils.getFullStackTrace(e));
throw new RuntimeException("字体初始化失败",e);
}
}
private void initFontResource() {
// 字体、logo文件移动至临时目录,解决jar包运行时取不到resource目录绝对路径问题
if (!FileUtil.exist(PdfUtil.getFontsPath())) {
FileUtil.mkdir(PdfUtil.getFontsPath());
}
for(String font : FONTS) {
String path = PdfUtil.getFontsPath() + File.separator + font;
if (!FileUtil.exist(path)) {
String resourceFilePath = "fonts" + File.separator + font;
copyResourceFile(resourceFilePath, font);
}
}
}
private static void copyResourceFile(String resourceFilePath, String targetFileName) {
copyResourceFile(resourceFilePath,targetFileName, PdfUtil.getFontsPath());
}
private static void copyResourceFile(String resourceFilePath, String targetFileName, String targetPath) {
File targetFile = FileUtil.file(targetPath, targetFileName);
OutputStream outputStream = null;
if (!FileUtil.exist(targetFile)) {
InputStream inputStream = PdfUtil.class.getClassLoader().getResourceAsStream(resourceFilePath);
try {
outputStream = new FileOutputStream(targetFile);
IOUtils.copy(inputStream, outputStream);
} catch (Exception e) {
log.error("", e);
} finally {
IoUtil.close(outputStream);
IoUtil.close(inputStream);
}
}
}
public int getPresentFontSize() {
return fontSize;
}
public void setPresentFontSize(int fontSize) {
this.fontSize = fontSize;
}
public String getFontFileName() {
return fontFileName;
}
public void setFontFileName(String fontFileName) {
this.fontFileName = fontFileName;
}
}
页眉页脚相关接口和实现类:HeaderFooterBuilder
public interface HeaderFooterBuilder {
/**
* @description 写页眉
*/
void writeHeader(PdfWriter writer, Document document, Object data, Font font, PdfTemplate template);
/**
* @description 写页脚
*/
void writeFooter(PdfWriter writer, Document document, Object data, Font font, PdfTemplate template);
/**
* @description 关闭文档前,获取替换页眉页脚处设置模板的文本
*/
String getReplaceOfTemplate(PdfWriter writer, Document document, Object data);
}
public class PDFHeaderFooter implements HeaderFooterBuilder {
/**
* @param writer PDF编写类
* @param document PDF文档对象
* @param data 业务数据
* @param font 字体设置
* @param template PDF模板
* @description PDF页脚设置类
*/
public void writeFooter(PdfWriter writer,
Document document,
Object data,
Font font,
PdfTemplate template) {
if (data == null) {
return;
}
int pageS = writer.getPageNumber();
int currentPage = pageS - 1;
if (currentPage <= 0) {
return;
}
Phrase footer1 = new Phrase("页脚一", font);
Phrase footer2 = new Phrase("页脚二" + " " + currentPage + "/", font);
PdfContentByte cb = writer.getDirectContent();
ColumnText.showTextAligned(
cb,
Element.ALIGN_LEFT,
footer1,
(document.left() + 10),
document.bottom() - 20,
0);
ColumnText.showTextAligned(
cb,
Element.ALIGN_RIGHT,
footer2,
(document.right() - 30),
document.bottom() - 20, 0);
//设置模板位置
cb.addTemplate(template, document.right() - 30, document.bottom() - 20);
}
/**
* @param writer PDF编写类
* @param document PDF文档对象
* @param data 业务数据
* @param font 字体设置
* @param template PDF模板
* @description PDF页头设置类
*/
public void writeHeader(PdfWriter writer,
Document document,
Object data,
Font font,
PdfTemplate template) {
ColumnText.showTextAligned(
writer.getDirectContent(),
Element.ALIGN_LEFT,
new Phrase("", font),
document.left(),
document.top() + 20, 0);
}
/**
* @param writer PDF编写类
* @param document PDF文档对象
* @param data 业务数据
* @description 页头、页眉设置的模板替换类
*/
public String getReplaceOfTemplate(PdfWriter writer, Document document, Object data) {
int total = writer.getPageNumber() - 2;
return total + "";
}
}