生成PDF工具
程序员文章站
2024-03-21 14:53:28
...
生成PDF工具
最近老大要我搞一个生成PDF的工具,看了一下相关资料,决定使用itext+freemarker实现,因为支持跨平台,并且可以动态生成pdf,先把代码放上。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.github.superad.pdf.kit</groupId>
<artifactId>pdf-kit</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<version>1.1-RELEASE</version>
<properties>
<java.src.version>1.7</java.src.version>
<java.target.version>1.7</java.target.version>
<project.encoding>UTF-8</project.encoding>
</properties>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
<version>7</version>
</parent>
<dependencies>
<!--pdf生成工具类-->
<!--pdf生成 itext-->
<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>
<!--freemarker-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.26-incubating</version>
</dependency>
<!--jfreechart-->
<dependency>
<groupId>jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.0.2</version>
</dependency>
<!--log-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-access</artifactId>
<version>1.0.13</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.5</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>log4j-over-slf4j</artifactId>
<version>1.7.21</version>
</dependency>
<!--util-->
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>20.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
</dependencies>
<!--增加的配置,过滤ttf文件的匹配-->
<profiles>
<profile>
<id>release</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<!--compile-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>${java.src.version}</source>
<target>${java.target.version}</target>
<encoding>${project.encoding}</encoding>
</configuration>
</plugin>
<!-- Source -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Javadoc -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- GPG -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>oss</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
</snapshotRepository>
<repository>
<id>oss</id>
<url>https://oss.sonatype.org/service/local/staging/deploy/maven2</url>
</repository>
</distributionManagement>
</profile>
</profiles>
<licenses>
<license>
<name>The Apache Software License, Version 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<scm>
<tag>alpha</tag>
<url>https://github.com/superad/pdf-kit.git</url>
<connection>scm:git:https://github.com/superad/pdf-kit.git</connection>
<developerConnection>scm:git:https://github.com/superad/pdf-kit.git</developerConnection>
</scm>
<developers>
<developer>
<name>fangguangming</name>
<email>[email protected]</email>
</developer>
</developers>
</project>
先写好模板
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="Content-Style-Type" content="text/css"/>
<title></title>
<style type="text/css">
body {
font-family: pingfang sc light;
}
.center{
text-align: center;
width: 100%;
}
</style>
</head>
<body>
<!--第一页开始-->
<div class="page" >
<div class="center"><p>${title}</p></div>
<div><p>第一行:${one}</p></div>
<div><p>第二行:${two}</p></div>
<div><p>第三行行:${three}</p></div>
</div>
</body>
</html>
注意这里的html一定要标准,不然会报错
freemarker工具类
public class FreeMarkerUtil {
private static final String WINDOWS_SPLIT = "\\";
private static final String UTF_8="UTF-8";
private static Map<String,FileTemplateLoader> fileTemplateLoaderCache=Maps.newConcurrentMap();
private static Map<String,Configuration> configurationCache= Maps.newConcurrentMap();
public static Configuration getConfiguration(String templateFilePath){
if(null!=configurationCache.get(templateFilePath)){
return configurationCache.get(templateFilePath);
}
Configuration config = new Configuration(Configuration.VERSION_2_3_25);
config.setDefaultEncoding(UTF_8);
config.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
config.setLogTemplateExceptions(false);
FileTemplateLoader fileTemplateLoader=null;
if(null!=fileTemplateLoaderCache.get(templateFilePath)){
fileTemplateLoader=fileTemplateLoaderCache.get(templateFilePath);
}
try {
fileTemplateLoader=new FileTemplateLoader(new File(templateFilePath));
fileTemplateLoaderCache.put(templateFilePath,fileTemplateLoader);
} catch (IOException e) {
}
config.setTemplateLoader(fileTemplateLoader);
configurationCache.put(templateFilePath,config);
return config;
}
/**
* @description 获取模板
*/
public static String getContent(String fileName, Object data) {
String templatePath = getPDFTemplatePath(fileName);
String templateFileName = getTemplateName(templatePath);
String templateFilePath = getTemplatePath(templatePath);
if (StringUtils.isEmpty(templatePath)) {
}
try {
Template template=getConfiguration(templateFilePath).getTemplate(templateFileName);
StringWriter writer = new StringWriter();
template.process(data, writer);
writer.flush();
return writer.toString();
} catch (Exception ex) {
}
}
private static String getTemplatePath(String templatePath) {
if (StringUtils.isEmpty(templatePath)) {
return "";
}
if (templatePath.contains(WINDOWS_SPLIT)) {
return templatePath.substring(0, templatePath.lastIndexOf(WINDOWS_SPLIT));
}
return templatePath.substring(0, templatePath.lastIndexOf("/"));
}
private static String getTemplateName(String templatePath) {
if (StringUtils.isEmpty(templatePath)) {
return "";
}
if (templatePath.contains(WINDOWS_SPLIT)) {
return templatePath.substring(templatePath.lastIndexOf(WINDOWS_SPLIT) + 1);
}
return templatePath.substring(templatePath.lastIndexOf("/") + 1);
}
/**
* @param fileName PDF文件名 (hello.pdf)
* @return 匹配到的模板名
* @description 获取PDF的模板路径,
* 默认按照PDF文件名匹对应模板
*/
public static String getPDFTemplatePath(String fileName) {
String classpath = PDFKit.class.getClassLoader().getResource("").getPath();
String templatePath = classpath + "/templates";
File file = new File(templatePath);
if (!file.isDirectory()) {
}
String pdfFileName = fileName.substring(0, fileName.lastIndexOf("."));
File defaultTemplate = null;
File matchTemplate = null;
for (File f : file.listFiles()) {
if (!f.isFile()) {
continue;
}
String templateName = f.getName();
if (templateName.lastIndexOf(".ftl") == -1) {
continue;
}
if (defaultTemplate == null) {
defaultTemplate = f;
}
if (StringUtils.isEmpty(fileName) && defaultTemplate != null) {
break;
}
templateName = templateName.substring(0, templateName.lastIndexOf("."));
if (templateName.toLowerCase().equals(pdfFileName.toLowerCase())) {
matchTemplate = f;
break;
}
}
if (matchTemplate != null) {
return matchTemplate.getAbsolutePath();
}
if (defaultTemplate != null) {
return defaultTemplate.getAbsolutePath();
}
return null;
}
}
设置页面附加属性(页眉页脚,字体)
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 PDFBuilder() {
}
public PDFBuilder(HeaderFooterBuilder headerFooterBuilder) {
this(headerFooterBuilder,null);
}
public PDFBuilder(HeaderFooterBuilder headerFooterBuilder, Object data) {
this(headerFooterBuilder,data,"ping_fang_light.ttf");
}
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;
}
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 PDFException("PDF文档字体未设置!");
}
try {
if (bf == null) {
//添加字体,以支持中文
String classpath=PDFBuilder.class.getClassLoader().getResource("").getPath();
String fontPath =classpath+"fonts/"+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) {
} catch (IOException e) {
}
}
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;
}
}
定制页眉页脚
/**
* 操作页眉接口
*/
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 + "";
}
}
PDF生成类
@Slf4j
public class PDFKit {
//PDF页眉、页脚定制工具
private HeaderFooterBuilder headerFooterBuilder;
private String saveFilePath;
/**
* @description 导出pdf到文件
* @param fileName 输出PDF文件名
* @param data 模板所需要的数据
*
*/
public String exportToFile(String fileName,Object data){
String htmlData= FreeMarkerUtil.getContent(fileName, data);
if(StringUtils.isEmpty(saveFilePath)){
saveFilePath=getDefaultSavePath(fileName);
}
File file=new File(saveFilePath);
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
FileOutputStream outputStream=null;
try{
//设置输出路径
outputStream=new FileOutputStream(saveFilePath);
//设置文档大小
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, outputStream);
//设置页眉页脚
PDFBuilder builder = new PDFBuilder(headerFooterBuilder,data);
builder.setPresentFontSize(10);
writer.setPageEvent(builder);
//输出为PDF文件
convertToPDF(writer,document,htmlData);
}catch(Exception ex){
}finally{
IOUtils.closeQuietly(outputStream);
}
return saveFilePath;
}
/**
* 生成PDF到输出流中(ServletOutputStream用于下载PDF)
* @param ftlPath ftl模板文件的路径(不含文件名)
* @param data 输入到FTL中的数据
* @param response HttpServletResponse
* @return
*/
public OutputStream exportToResponse(String ftlPath,Object data,
HttpServletResponse response){
String html= FreeMarkerUtil.getContent(ftlPath,data);
try{
OutputStream out = null;
ITextRenderer render = null;
out = response.getOutputStream();
//设置文档大小
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, out);
//设置页眉页脚
PDFBuilder builder = new PDFBuilder(headerFooterBuilder,data);
writer.setPageEvent(builder);
//输出为PDF文件
convertToPDF(writer,document,html);
return out;
}catch (Exception ex){
}
}
/**
* @description PDF文件生成
*/
private void convertToPDF(PdfWriter writer,Document document,String htmlString){
//获取字体路径
String fontPath=getFontPath();
document.open();
try {
XMLWorkerHelper.getInstance().parseXHtml(writer,document,
new ByteArrayInputStream(htmlString.getBytes()),
XMLWorkerHelper.class.getResourceAsStream("/default.css"),
Charset.forName("UTF-8"),new XMLWorkerFontProvider(fontPath));
} catch (IOException e) {
}finally {
document.close();
}
}
/**
* @description 创建默认保存路径
*/
private String getDefaultSavePath(String fileName){
String classpath=PDFKit.class.getClassLoader().getResource("").getPath();
String saveFilePath=classpath+"pdf/"+fileName;
File f=new File(saveFilePath);
if(!f.getParentFile().exists()){
f.mkdirs();
}
return saveFilePath;
}
/**
* @description 获取字体设置路径
*/
public static String getFontPath() {
String classpath=PDFKit.class.getClassLoader().getResource("").getPath();
String fontpath=classpath+"fonts";
return fontpath;
}
public HeaderFooterBuilder getHeaderFooterBuilder() {
return headerFooterBuilder;
}
public void setHeaderFooterBuilder(HeaderFooterBuilder headerFooterBuilder) {
this.headerFooterBuilder = headerFooterBuilder;
}
public String getSaveFilePath() {
return saveFilePath;
}
public void setSaveFilePath(String saveFilePath) {
this.saveFilePath = saveFilePath;
}
}
model类
@Data
public class TemplateBO {
private String title;
private String one;
private String two;
private String three;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getOne() {
return one;
}
public void setOne(String one) {
this.one= one;
}
public String getITwo() {
return two;
}
public void setTwo(String two) {
this.two= two;
}
public String getThree() {
return three;
}
public void setThreel(String three) {
this.three= three;
}
}
测试类
public static void main(String[] args) {
TemplateBO templateBO=new TemplateBO();
templateBO.setTemplateName("Hello iText! Hello freemarker! Hello jFreeChart!");
templateBO.setFreeMarkerUrl("http://www.zheng-hang.com/chm/freemarker2_3_24/ref_directive_if.html");
templateBO.setITEXTUrl("http://developers.itextpdf.com/examples-itext5");
templateBO.setJFreeChartUrl("http://www.yiibai.com/jfreechart/jfreechart_referenced_apis.html");
PDFHeaderFooter headerFooter=new PDFHeaderFooter();
PDFKit kit=new PDFKit();
kit.setHeaderFooterBuilder(headerFooter);
//设置输出路径
kit.setSaveFilePath("/Users/fgm/Desktop/pdf/hello.pdf");
String saveFilePath=kit.exportToFile("hello.pdf ",templateBO);
到此就ok啦
上一篇: VS2010制作PDF报表
下一篇: 关于java中实现word转pdf