之前发布的JAVA生成PDF相关功能的博客不够完善,最近又结合了添加水印与页眉页脚的功能,故在此重新梳理一版,结合实际代码,希望能帮到大家
功能准备:
本功能基于freemaker+openoffice+itext实现通过DOCX模板生成PDF文件或者直接转换DOC到PDF,支持水印页眉、页脚。
相关依赖,需要事先安装好openoffice并开启服务(自行百度)。
<dependency>
<groupId>com.artofsolving</groupId>
<artifactId>jodconverter</artifactId>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.itextpdf/itext-asian -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
1. 生成docx模板和xml模板
生成docx与XML模板,这样做是使结合数据生成的DOC文件是真正的.DOC格式的文件,而不是只是将后缀名改为.doc的XML文件(因为这种文件有些软件与浏览器解析不了,显示的是XML文件的内容)
准备需要用的DOCX的文档如test.docx作为DOCX模板。
用压缩文件打开test.docx,复制word目录下的document.xml,取出来作为XML模板。
XML模板里用${参数}代表传入的参数,如需遍历的话用<#list datalist(传入的集合名称) as datalist > ${datalist.shuxing} </#list>来控制遍历的开关
2.用freemaker填充模板数据,然后执行相关转换方法,最终下载。
/**
* @方法名称
* @功能描述 生成下载PDF
* @作者 fan
* @创建时间
* @param dataMap 模板参数
* @param xmlTempName xml模板名称 (即前文准备的test.xml)
* @param docxTempName docx模板名称(即前文准备的test.docx)
* @param pdfName 生成的PDF名称
* @param response
* @param request
* @return
*/
public static void downloadPdf(Map<String, Object> dataMap, String xmlTempName, String docxTempName, String pdfName,
HttpServletResponse response, HttpServletRequest request) {
try {
// Configuration 用于读取ftl文件
Configuration configuration = new Configuration(new Version("2.3.0"));
configuration.setDefaultEncoding("utf-8");
// 指定路径的第一种方式(根据某个类的相对路径指定)
// configuration.setClassForTemplateLoading(this.getClass(), "");
// 指定路径的第二种方式,我的路径是C:/a.ftl
// configuration.setDirectoryForTemplateLoading(new File("c:/"));
configuration.setServletContextForTemplateLoading(request.getSession().getServletContext(), "./WEB-INF/template");
// 输出文档路径及名称
File outFile = new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.xml"));
// 以utf-8的编码读取ftl文件
Template template = configuration.getTemplate(xmlTempName, "utf-8");
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "utf-8"), 10240);
template.process(dataMap, out);//将初始模板结合数据得到新的xml模板
out.close();
String wordFile = request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.doc");
String pdfFile = request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.pdf");
outDocx(new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp.xml")), docxTempName, wordFile, request);//将结合数据后的XML模板转换为标准的DOC格式文档
WordToPDF(wordFile, pdfFile);//将标准的DOC格式文档转换为PDF格式
addFooterAndWater(pdfFile, request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp22.pdf"), "水印", "页眉", "页脚");//为PDF添加水印、页眉页脚等
download(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp22.pdf"), pdfName, response, false);
new File(wordFile).delete();//下载完成后删除生成的模板文件
new File(pdfFile).delete();
new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/temp22.pdf")).delete();
outFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
3. 生成标准的DOC文件方法
/**
* @方法名称
* @功能描述 生成下载PDF
* @作者 fan
* @创建时间
* @param documentFile 原始xml结合数据集生成的xml模板
* @param docxTemplate 初始准备的docx格式的模板路径
* @param toFilePath 生成的文档路径(后缀名取.doc)
* @param response
* @param request
* @return
*/
// 生成标准的DOC文件方法
public static void outDocx(File documentFile, String docxTemplate, String toFilePath, HttpServletRequest request)
throws ZipException, IOException {
try {
File docxFile = new File(request.getSession().getServletContext().getRealPath("/WEB-INF/template/" + docxTemplate));
ZipFile zipFile = new ZipFile(docxFile);
Enumeration<? extends ZipEntry> zipEntrys = zipFile.entries();
ZipOutputStream zipout = new ZipOutputStream(new FileOutputStream(toFilePath));
int len = -1;
byte[] buffer = new byte[1024];
while (zipEntrys.hasMoreElements()) {
ZipEntry next = zipEntrys.nextElement();
InputStream is = zipFile.getInputStream(next);
// 把输入流的文件传到输出流中 如果是word/document.xml由我们输入
zipout.putNextEntry(new ZipEntry(next.toString()));
if ("word/document.xml".equals(next.toString())) {
// InputStream in = new FileInputStream(new
// File(XmlToDocx.class.getClassLoader().getResource("").toURI().getPath()+"template/test.xml"));
InputStream in = new FileInputStream(documentFile);
while ((len = in.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
in.close();
} else {
while ((len = is.read(buffer)) != -1) {
zipout.write(buffer, 0, len);
}
is.close();
}
}
zipout.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
4. WORD转换为PDF
// word转化为PDF方法
public static void WordToPDF(String startFile, String overFile) throws IOException {
// 源文件目录
File inputFile = new File(startFile);
if (!inputFile.exists()) {
System.out.println("源文件不存在!");
return;
}
// 输出文件目录
File outputFile = new File(overFile);
if (!outputFile.getParentFile().exists()) {
outputFile.getParentFile().exists();
}
// 调用openoffice服务线程
/** 我把openOffice下载到了 C:/Program Files (x86)/下 ,下面的写法自己修改编辑就可以**/
// 连接openoffice服务
OpenOfficeConnection connection = new SocketOpenOfficeConnection("127.0.0.1", 8100);
connection.connect();
// 转换
DocumentConverter converter = new OpenOfficeDocumentConverter(connection);
converter.convert(inputFile, outputFile);
// 关闭连接
connection.disconnect();
// 关闭进程
// p.destroy();
}
4.添加水印、页眉、页脚等
/** * 添加水印、页眉、页脚 * @param fileName 源文件路径 * @param savepath 目标文件路径 * @param waterMarkName 文字水印 * @param pageHeade 页眉 * @param foot 页脚 * @return */
public static int addFooterAndWater(String fileName, String savepath,
String waterMarkName, String pageHeade, String foot)
{
// 文档总页数
int num = 0;
Document document = new Document();
try
{
PdfReader reader = new PdfReader(fileName);
BaseFont base = BaseFont.createFont("STSong-Light", "UniGB-UCS2-H",
BaseFont.EMBEDDED);
num = reader.getNumberOfPages();
PdfCopy copy = new PdfCopy(document, new FileOutputStream(savepath));
document.open();
for (int i = 0; i < num;)
{
PdfImportedPage page = copy.getImportedPage(reader, ++i);
PageStamp stamp = copy.createPageStamp(page);
Font f = new Font(base);
// 添加页脚,左侧文字,右侧页码
ColumnText.showTextAligned(stamp.getUnderContent(),
Element.ALIGN_RIGHT,
new Phrase(String.format("第 %d 页/共 %d 页", i, num), f),
550f, 28, 0);
ColumnText.showTextAligned(stamp.getUnderContent(),
Element.ALIGN_LEFT, new Phrase(foot, f), 50f, 28, 0);
// 添加页眉 (文字页眉,居中)
ColumnText.showTextAligned(stamp.getUnderContent(),
Element.ALIGN_CENTER, new Phrase(pageHeade, f), 150f,
800, 0);
// 页眉添加logo (图片页眉,居右)
/*Image img = Image.getInstance("template/logo.png");// 选择图片
img.setAlignment(1);
img.scaleAbsolute(436 / 5, 96 / 5);// 控制图片大小
img.setAbsolutePosition(450f, 800);// 控制图片位置
stamp.getUnderContent().addImage(img);*/
// 添加水印
PdfContentByte under = stamp.getUnderContent();
under.beginText();
under.setColorFill(BaseColor.BLACK);
// 字符越长,字体越小,设置字体
int fontSize = getFontSize(waterMarkName);
under.setFontAndSize(base, fontSize);
// 设置水印文字字体倾斜 开始
float pageWidth = reader.getPageSize(i).getWidth();
float pageHeight = reader.getPageSize(i).getHeight();
under.showTextAligned(Element.ALIGN_CENTER, waterMarkName,
pageWidth / 2, pageHeight / 2, 60);// 水印文字成60度角倾斜,且页面居中展示
// 字体设置结束
under.endText();
stamp.alterContents();
copy.addPage(page);
}
}
catch (Exception e)
{
e.printStackTrace();
return -1;
}
finally
{
if (null != document)
{
document.close();
}
}
System.out.println("pdf totalpages:" + num);
return num;
}
/**
* 根据水印文字长度计算获取字体大小
* @param waterMarkName
* @return
*/
private static int getFontSize(String waterMarkName){
int fontSize = 80;
if(null != waterMarkName && !"".equals(waterMarkName)){
int length = waterMarkName.length();
if(length <=26 && length >= 18){
fontSize = 26;
}else if(length <18 && length >= 8){
fontSize = 40;
}else if(length <8 && length >= 1){
fontSize = 80;
}else {
fontSize = 16;
}
}
return fontSize;
}
//经过本人测试,2.2.1的jodconverter包只能转换后缀doc格式的文件,不能转换docx的文件,有人说应是jodconverter版本不够,但博主当时下载最高版本后,其余方法又会报异常。有问题欢迎讨论,水印功能参考https://www.it610.com/article/3640077.htm