wkhtmltopdf+itext实现html生成pdf文件的打印下载(适用于linux及windows)
目中遇到个根据html转Java的功能,在java中我们itext可以快速的实现pdf打印下载的功能,在itext中我们一般有以下三中方式实现
- 配置pdf模板,通过Adobe Acrobat 来设置域最后通过代码将数据填充进去
- 通过FreeMarker或thymeleaf配置html模板填充数据
- Jsoup+XMLWorkerHelper对于上述的三种方式,我简述下我的体验:第一种方式对于入门简单,如果我们需求中的pdf文件是表格或者报表的样式还是很好实现的,但如果遇到要求和html样式一致的话就基本歇菜了。第二张方式比较理想,在项目基本完工的情况下再去改成模板不太现实,而且我只是个做后端的还没那么大的能耐,不选。第三种方式我也尝试了下,对于一些简单网页比如说博客,我们通过Jsoup可以获取到文章的内容和html样式,网上的demo也是通过博客来举例的,效果不错,但我看了下博客内容的基本样式就是一些基本的div、p、li这些标签,但在我实际的项目中样式比较复杂,生成的pdf无法打开。而且上面的三种方式都有一个致命的缺点:那就是需要去针对模板。现实的项目中我们可能多个地方需要实现pdf打印的功能,样式模板的配置将占用很大的开发工作量。第三种方式虽说不需要模板,但通过网页的标签去获取数据也会变得多样化,完全不能实现方法的复用。当然,itext也不是一无是处,在这个项目里面我们还是需要用到itext去生成水印、加密、不可编辑等一系列细致的活。
这里我想要的理想的pdf打印效果是:最少的改动,实现pdf的打印的效果与我请求的url的html样式一致。那就要使用到我们今天的主角wkhtmltopdf,完美的解决了我的难题,windows下解压即用,linux下需要安装三个依赖和一个宋体文字,下面让我们一起coding吧~
详细步骤:
- windows下wkhtmltopdf的下载安装测试
- linux下的wkhtmltopdf的下载安装测试
- 配置pdf生成的路径解读
- 自动调用java中Runtime.getRuntime()运行wkhtmltopdff插件的具体实现
windows下安装测试
通过https://wkhtmltopdf.org/downloads.html下载并解压缩wkhtmltox文件,通过cmd 命令找到 bin下面的wkhtmltopdf.exe 文件,执行wkhtmltopdf.exe +http url + pdf路径及文件名
http://resource.tianluzhi.com/view/windows.mp4 windows演示地址
Linux下安装测试
wget https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.4/wkhtmltox-0.12.4.tar.bz2 用wget下载wkhtmltopdf安装,这里我们采用的是0.12.4的版本,没有和上面windows的0.12.5保持一致原因是没有0.12.5的tar包,rpm的不习惯。安装完之后我们参考上面windows的方式运行,运行过程中在centos 7中会遇到缺少依赖的问题。执行yum -y install libXrender* libfontconfig* libXext* 安装后重新运行测试,还会遇到中文乱码问题,这这里我们还需将windows下的simsun字体放置到 /usr/share/fonts/ ,windows字体的存放位置为C:\Windows\Fonts,win10部分用户的simsun字体是没效果的,这里给大家提供一个下载 http://resource.tianluzhi.com/dev/SIMSUN.TTC ,linux下一般就这些问题。这里就不做演示了,我们开始真正的coding吧。
配置pdf生成的路径解读
# linux path
#wkhtmltopdfPath 指向wkhtmltopdfPath安装的bin目录下wkhtmltopdf
wkhtmltopdfPath=/home/wkhtmltox/bin/wkhtmltopdf
pdfLocationPathForLinux=/home/prd-gehcpp-01/otoResources/pdf/
waterPdfLocationPathForLinux=/home/prd-gehcpp-01/otoResources/waterPdf/ # windows path
#photoLocationPath=D://otoResources//photo//
#pdf文件路径
pdfLocationPath= D://otoResources//pdf//
#添加水印后文件路径
waterPdfLocationPath= D://otoResources//waterPdf//
上面配置中我们分别配置了linux和windows两套路径环境,在我们代码中我们会自动判别当前服务器的操作系统,然后选择相关的路径。下面我们分别解读下各个配置参数
wkhtmltopdfPath :指向wkhtmltopdfPath安装的bin目录下wkhtmltopdf,这段地址其实就是为了执行wkhtmltopdf插件而添加的,这里我们用全路径,这样在linux的环境下我们就不需要去配置环境了,另外细心的朋友会发现windows的没有配置,这是因为我已经将插件放到了资源文件夹下,
然后通过Java程序去拼接了一个完整的路径,具体我们往下看。
pdfLocationPathForLinux/photoLocationPath : 这两个路径分别指向了linux和Windows下的pdf生成的初始文件,如果我们不需要对pdf进行二次操作的话,在这里我们就可以把结果返回给用户。
waterPdfLocationPathForLinux/waterPdfLocationPath :这个路径是指经过加工后的路径,比如水印、加密、不可编辑等,上面的路径相当于是模子,这个才是最终返回客户的结果。
代码实现
部分代码参考 https://www.cnblogs.com/xionggeclub/p/6144241.html
controller
/**
*
* @param response
* @param httpUrl 需要生成pdf的请求地址
* @param downName 生成pdf后的文件名
* @return
*/
@RequestMapping("pdfConvert")
@ResponseBody
public Response htmlToPdf(HttpServletResponse response ,@RequestParam String httpUrl,String downName) {
return geFileService.htmlToPdf(response,httpUrl,downName);
}
service
Response htmlToPdf(HttpServletResponse response, String httpUrl,String downName);
impl
package com.ge.service.impl; import com.common.exception.MyException;
import com.ge.service.GeFileService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import pdf.HtmlToPdfUtil;
import utils.Response;
import utils.ResultHttpStatus; import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.UUID; import static utils.PropertiesUtil.getProperty; /**
* @Author: gaofeng_peng
* @Date: 2018/6/13 13:47
*/
@Service("GeFileService")
public class FileServiceImpl implements GeFileService {
private Logger logger = LoggerFactory.getLogger(FileServiceImpl.class); public static String pdfLocationPath; public static String waterPdfLocationPath; public static String systemOs;
/**
* 根据服务器系统设置pdf文件路径
*/
static {
systemOs = System.getProperty("os.name");
if (systemOs.toLowerCase().startsWith("win")) {
pdfLocationPath = getProperty("pdfLocationPath");
waterPdfLocationPath = getProperty("waterPdfLocationPath");
} else {
pdfLocationPath = getProperty("pdfLocationPathForLinux");
waterPdfLocationPath = getProperty("waterPdfLocationPathForLinux");
}
} @Override
public Response htmlToPdf(HttpServletResponse res, String httpUrl, String downName) {
Response response = new Response(ResultHttpStatus.OK.getValue(), ResultHttpStatus.OK.getName());
//判断路径是否存在,不存在则创建
File fileDir = new File(pdfLocationPath);
File waterFileDir = new File(waterPdfLocationPath);
if (!fileDir.exists()) {
fileDir.setWritable(true);
fileDir.mkdirs();
}
if (!waterFileDir.exists()) {
waterFileDir.setWritable(true);
waterFileDir.mkdirs();
}
String fileName = UUID.randomUUID().toString() + ".pdf";
String source = pdfLocationPath + fileName;
String outPath = waterPdfLocationPath + fileName;
try {
//调用HtmlToPdfUtilz中的convent的方法使html生成pdf
boolean isSuccess = HtmlToPdfUtil.convert(httpUrl, source,systemOs);
if (isSuccess) {
//水印加密
// HtmlToPdfUtil.setWaterMarkForPDF(source,outPath,"");
download(res, downName, fileName);
} else {
throw new MyException("pdf下载异常");
} } catch (Exception e) {
response.setMsg(e.getMessage());
response.setStatus(ResultHttpStatus.INTERNAL_ERROR.getValue());
}
return response;
}
/**
* 响应下载
* @param resp
* @param downloadName 下载的pdf文件名
* @param fileName 系统中真正的文件名
*/
public void download(HttpServletResponse resp, String downloadName, String fileName) {
try {
downloadName = new String(downloadName.getBytes("GBK"), "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String realPath = pdfLocationPath;
String path = realPath + fileName;
File file = new File(path);
resp.reset();
resp.setContentType("application/octet-stream");
resp.setCharacterEncoding("utf-8");
resp.setContentLength((int) file.length());
resp.setHeader("Content-Disposition", "attachment;filename=" + downloadName + ".pdf");
byte[] buff = new byte[1024];
BufferedInputStream bis = null;
OutputStream os = null;
try {
os = resp.getOutputStream();
bis = new BufferedInputStream(new FileInputStream(file));
int i = 0;
while ((i = bis.read(buff)) != -1) {
os.write(buff, 0, i);
os.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bis.close();
//完成后删除本地pdf文件
file.delete();
} catch (IOException e) {
e.printStackTrace();
}
} }
}
HtmlToPdfUtil类
package pdf; import com.itextpdf.text.BaseColor;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfReader;
import com.itextpdf.text.pdf.PdfStamper;
import org.apache.commons.lang3.StringUtils;
import utils.PropertiesUtil; import java.io.FileOutputStream; /**
* @Author: gaofeng_peng
* @Date: 2018/7/5 13:13
*/
public class HtmlToPdfUtil {
//wkhtmltopdf在系统中的路径
// private static final String toPdfTool = "D:\\wkhtmltox\\bin\\wkhtmltopdf.exe"; /**
* html转pdf
*
* @param srcPath html路径
* @param pdfLocationPath pdf保存路径
* @return 转换成功返回true
*/
public static boolean convert(String srcPath, String pdfLocationPath, String systemOs) throws Exception {
String toPdfTool="";
if (systemOs.toLowerCase().startsWith("win")) {
toPdfTool = HtmlToPdfUtil.class.getClassLoader().getResource("wkhtmltox/bin/wkhtmltopdf.exe").getPath();
}else {
toPdfTool= PropertiesUtil.getProperty("wkhtmltopdfPath");
}
StringBuilder cmd = new StringBuilder();
cmd.append(toPdfTool);
cmd.append(" ");
cmd.append(" --background");
cmd.append(" --debug-javascript");
cmd.append(" --header-line");//页眉下面的线
cmd.append(" --header-spacing 10 ");// (设置页眉和内容的距离,默认0) cmd.append(srcPath);
cmd.append(" ");
cmd.append(pdfLocationPath); boolean result = true;
try {
Process proc = Runtime.getRuntime().exec(cmd.toString());
HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream());
HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream());
error.start();
output.start();
proc.waitFor();
} catch (Exception e) {
result = false;
e.printStackTrace();
}
return result;
} /**
* 生成水印,加密在这里实现
* @param sourceFilePath 源文件路径
* @param fileWaterMarkPath 水印生成文件路径
* @throws Exception
*/
public static void setWaterMarkForPDF(String sourceFilePath, String fileWaterMarkPath, String waterMarkName) throws Exception {
// String waterPath = Class.class.getClass().getResource("/1.png").getPath();
PdfReader reader = new PdfReader(sourceFilePath);
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream(fileWaterMarkPath)); int total = reader.getNumberOfPages() + 1;
PdfContentByte under = null;
//Image img = Image.getInstance(waterPath);
//img.setAbsolutePosition(30, 100);//坐标
// img.setRotation(-20);//旋转 弧度
//img.setRotationDegrees(-35);//旋转 角度
//img.scaleAbsolute(200,100);//自定义大小
//img.scalePercent(100);//依照比例缩放
BaseFont bf = BaseFont.createFont(BaseFont.HELVETICA,
BaseFont.CP1252, BaseFont.NOT_EMBEDDED);
if (StringUtils.isBlank(waterMarkName)) {
waterMarkName = "Tech Mahindra";
} int j = waterMarkName.length();
char c = 0;
int rise = 0;
for (int i = 1; i < total; i++) // 每一页都加水印
{
rise = 500;
under = stamp.getUnderContent(i);
// 添加图片
// under.addImage(img);
under.beginText();
under.setColorFill(BaseColor.BLACK);
under.setFontAndSize(bf, 30);
// 设置水印文字字体倾斜 开始
if (j >= 15) {
under.setTextMatrix(200, 120);
for (int k = 0; k < j; k++) {
under.setTextRise(rise);
c = waterMarkName.charAt(k);
under.showText(c + "");
rise -= 20;
}
} else {
under.setTextMatrix(180, 100);
for (int k = 0; k < j; k++) {
under.setTextRise(rise);
c = waterMarkName.charAt(k);
under.showText(c + "");
rise -= 18;
}
}
// 字体设置结束
under.endText();
} stamp.close();
reader.close();
}
}
HtmlToPdfInterceptor类
package pdf; import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @Author: gaofeng_peng
* @Date: 2018/7/5 13:17
*/
public class HtmlToPdfInterceptor extends Thread {
private InputStream is; public HtmlToPdfInterceptor(InputStream is){
this.is = is;
} public void run(){
try{
InputStreamReader isr = new InputStreamReader(is, "utf-8");
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line.toString()); //输出内容
}
}catch (IOException e){
e.printStackTrace();
}
}
}
水印部分的功能被我注释了,实际效果是可行的,只是本人设计的太丑,没脸见人所以注释。后期改良再说,有兴趣的小伙伴可以自己改良。至此pdf打印下载的功能就完成了。需要注意的是html最好用原生的,使用less预处理可能会导致样式的丢失。
wkhtmltopdf+itext实现html生成pdf文件的打印下载(适用于linux及windows)的更多相关文章
- 在spring boot 中使用itext和itextrender生成pdf文件
转载请注明出处 https://www.cnblogs.com/majianming/p/9539376.html 项目中需要对订单生成pdf文件,在第一版本其实已经有了比较满意的pdf文档,但是还是 ...
- Java 动态生成 PDF 文件
每片文章前来首小诗: 今日夕阳伴薄雾,印着雪墙笑开颜.我心仿佛出窗前,浮在半腰望西天. --泥沙砖瓦浆木匠 需求: 项目里面有需要java动态生成 PDF 文件,提供下载.今天我找了下有关了,系 ...
- java调用wkhtmltopdf生成pdf文件,美观,省事
最近项目需要导出企业风险报告,文件格式为pdf,于是搜了一大批文章都是什么Jasper Report,iText ,flying sauser ,都尝试了一遍,感觉不是我想要的效果, 需要自己调整好多 ...
- 【Java】使用iText生成PDF文件
iText介绍 iText是著名的开放源码的站点sourceforge一个项目,是用于生成PDF文档的一个java类库.通过iText不仅可以生成PDF或rtf的文档,而且可以将XML.Html文件转 ...
- Itext生成pdf文件
来源:https://my.oschina.net/lujianing/blog/894365 1.背景 在某些业务场景中,需要提供相关的电子凭证,比如网银/支付宝中转账的电子回单,签约的电子合同等. ...
- Java Itext 生成PDF文件
利用Java Itext生成PDF文件并导出,实现效果如下: PDFUtil.java package com.jeeplus.modules.order.util; import java.io.O ...
- [itext]Java生成PDF文件
一.前言 最近在做也导出试卷的功能,刚开始是导出为doc,可是导出来格式都有变化,最后说直接将word转为pdf,可是各种不稳定,各种报错.最后想到直接将文件写入pdf(参考:http://www.c ...
- [轉載]史上最强php生成pdf文件,html转pdf文件方法
之前有个客户需要把一些html页面生成pdf文件,然后我就找一些用php把html页面围成pdf文件的类.方法是可谓是找了很多很多,什么html2pdf,pdflib,FPDF这些都试过了,但是都没有 ...
- 怎么用PHP在HTML中生成PDF文件
原文:Generate PDF from html using PHP 译文:使用PHP在html中生成PDF 译者:dwqs 利用PHP编码生成PDF文件是一个非常耗时的工作.在早期,开发者使用PH ...
随机推荐
- .net下4款不错的微信SDK
.net下4款不错的微信SDK: 一款值得推荐的.Net微信开发SDK http://www.17ky.net/soft/474.html.Net微信公开帐号接口 WeiXinSDK http://w ...
- Python 数据类型--集合(set)
一.集合(set) 集合也是一种数据类型,一个类似列表的,无序的,不重复的.它主要有两大作用 1.把一个列表变为集合,就自动去重了,不需要写额外的代码 2.关系测试,测试两组数据之间的交际.差集.并集 ...
- rdf-3x简要使用
github下载地址:https://github.com/gh-rdf3x/gh-rdf3x rdf3x是用C++编写的,linux系统中,需要对其编译进行编译 1.安装g++编译器sudo apt ...
- 【重大更新】DevExpress v17.2新版亮点—Bootstrap篇(二)
用户界面套包DevExpress v17.2日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了Bootstrap Controls v17.2 的CardView.Charts ...
- core servlets & server pages 上面的HttpClient GUI工具
我没怎么细读源码,等下次详细看的时候将这句话去掉. import java.awt.*; import java.awt.event.*; import java.io.*; import java. ...
- docker中的安全机制
有时候我们需要容器具有更多的权限,像如操作内核模块,控制swap交换分区,挂载usb磁盘,修改mac地址等.所以我们今天进行docker的安全设定. 一.使用docker命令设置docker的安全机制 ...
- mysql5.7高可用架构之MHA
一.MHA简介 MHA(Master High Availability)目前在mysql高可用方面比较成熟.是一套优秀的作为 mysql高可用性环境下故障切换和主从提升的高可用软件.在MySQL故障 ...
- Skynet服务器框架(十) CentOS 防火墙设置
引言: 今天修改了 skynet 服务器的 IP 地址(即 config 文件中的 address 和 master 两项参数,IP 与当前及其的保持一致,端口号为 2017),然后使用一个简单的客户 ...
- 利用git向github中推送文件
/*游戏或者运动才能让我短暂的忘记心痛,现如今感觉学习比游戏和运动还重要——曾少锋*/ 如果对git不够熟悉的学者,可以参考:http://www.cnblogs.com/zengsf/p/75062 ...
- [LeetCode&Python] Problem 104. Maximum Depth of Binary Tree
Given a binary tree, find its maximum depth. The maximum depth is the number of nodes along the long ...