Java实现Html转PDF的方法
写在前面
以下路径问题根据项目结构自己修改,以下是我使用spring boot打成jar包的写法。
一、需求背景
在前端编辑器中输入任意的文本,包括css样式变化,保存为html文本。通过Java后台将html文本转换为PDF文档并加上页眉、页脚、水印等。因为网上开源的方案用的工具版本都比较老,也无法满足要求。所以只能用目前比较新的Itext7,网上的资料不多,只能看文档自己学习。
二、解决方案
1.开发工具Itext7 (https://itextpdf.com/itext7): 首先jar包一定要引对,要不Demo也运行不了。我项目使用的是maven,以下是pom.xml最新版jar可以通过官方文档中寻找。
<!-- pdfHTML -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>html2pdf</artifactId>
<version>1.0.2</version>
</dependency>
<!-- add all iText 7 Community modules -->
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext7-core</artifactId>
<version>7.0.5</version>
<type>pom</type>
</dependency>
itext7-core包含了9个jar包,直接都导入就好
2.最简单的HTML转PDF(包含中文字体、粗体、表格等基本,不支持PDF的页眉、页脚、页边距、水印等)方法的参数和返回值可以灵活变通
public class Html2PdfUtil {
public static void main(String[] args) throws Exception {
String html = "<p><span style=\"font-family: Microsoft YaHei;\">微软雅黑: 粗体前A<strong>A粗体A</strong>A粗体后</span></p>\n" +
"<p><span style=\"font-family: SimSun;\">宋体: 粗体前A<strong>A粗体A</strong>A粗体后</span></p>\n" +
"<p><span style=\"font-family: STHeiti;\">黑体: 粗体前A<strong>A粗体A</strong>A粗体后</span></p>" +
"<p><span style=\"font-family: Times New Roman;\">Times New Roman: pre bdA<strong>AbdA</strong>Aaft bd</span></p>\n";
FileOutputStream fileOutputStream = new FileOutputStream("D:/Test/a.pdf");
fileOutputStream.write(convert(html));
fileOutputStream.close();
}
public static byte[] convert(String html) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider(); // 提供解析用的字体
fp.addStandardPdfFonts(); // 添加标准字体库、无中文
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
fp.addDirectory(classLoader.getResource("fonts").getPath()); // 自定义字体路径、解决中文,可先用绝对路径测试。
props.setFontProvider(fp);
// props.setBaseUri(baseResource); // 设置html资源的相对路径
HtmlConverter.convertToPdf(html, outputStream, props); // 无法灵活设置页边距等
byte[] result = outputStream.toByteArray();
outputStream.close();
return result;
}
}
这段代码添加了中文字体库,否则中文无法显示。设置了解析html时资源的相对路径。还有就是如果html编辑器有加粗样式类的需求时,需要把该字体和加粗字体都上传,否则无法正常显示,字体不太好找,大部分字体系统盘中有,附宋体加粗字体,和华文黑体加粗,黑体加粗没找到。
https://files.cnblogs.com/files/Sigurd/STHeitibd.rar
https://files.cnblogs.com/files/Sigurd/simsunbd.rar
3.如何灵活设置生成pdf的页边距
首先要找到在哪里设置页边距,找了一圈发现只有Document类中有这个方法,所有刚开始我用了HtmlConvert.convertToDocument()方法,但是发现得到的Document类immediateFlush为true,没办法手动重新布局,只能换方法了。
然后看到了List HtmlConvert.convertToElements(), 之后就好办了自己新建一个Document设置好页边距,然后foreach插入(IBlockElement)IElement就好。生成pdf之后确实有了边距,但是段间距变得不正常了。
IBlockElement中没有修改边距的方法,看一下IBlockElement的实现类BlockElement中可以修改边距,然后强转成BlockElement就可以修改行句了。最后代码如下。
public class Html2PdfUtil {
public static final float topMargin = 114f;
public static final float bottomMargin = 156f;
public static final float leftMargin = 90f;
public static final float rightMargin = 90f;
public static byte[] convert(String html) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
PdfWriter writer = new PdfWriter(outputStream);
PdfDocument pdfDocument = new PdfDocument(writer);
try {
ConverterProperties props = new ConverterProperties();
FontProvider fp = new FontProvider();
fp.addStandardPdfFonts();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
fp.addDirectory(classLoader.getResource("fonts").getPath());
props.setFontProvider(fp);
List<IElement> iElements = HtmlConverter.convertToElements(html, props);
Document document = new Document(pdfDocument, PageSize.A4, true); // immediateFlush设置true和false都可以,false 可以使用 relayout
document.setMargins(topMargin, rightMargin, bottomMargin, leftMargin);
for (IElement iElement : iElements) {
BlockElement blockElement = (BlockElement) iElement;
blockElement.setMargins(1, 0, 1, 0);
document.add(blockElement);
}
document.close();
return outputStream.toByteArray();
} catch (Exception e) {
throw e;
} finally {
outputStream.close();
}
}
}
4.页眉页脚水印的写法
页眉页脚水印都是在每页插入相同内容,所以做法类似。都是实现IEventHandler接口,然后添加监听。页眉页脚水印、基本都是图片和文字调用的api也比较简单,原点坐标在左下角,在第一象限内做图,以下面的代码为例吧。
/**
* Description html转pdf
* Created by shuxiaogang
* date on 2017/11/22
*/
public class Html2PdfUtil {
public static final float topMargin = 114f;
public static final float bottomMargin = 156f;
public static final float leftMargin = 90f;
public static final float rightMargin = 90f;
public static byte[] convert(String html) throws IOException {
...同代码3
Header headerHandler = new Header();
Footer footerHandler = new Footer();
WatermarkingEventHandler watermarkingEventHandler = new WatermarkingEventHandler();
pdfDocument.addEventHandler(PdfDocumentEvent.START_PAGE, headerHandler);
pdfDocument.addEventHandler(PdfDocumentEvent.END_PAGE, footerHandler);
pdfDocument.addEventHandler(PdfDocumentEvent.INSERT_PAGE, watermarkingEventHandler);
...同代码3
}
// 页眉
protected static class Header implements IEventHandler {
protected float width = 102f;
protected float height = 32f;
protected float x = 42f;
protected float y = 740f;
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.getLastContentStream(), page.getResources(), pdf);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
ImageData image = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream logo = loader.getResourceAsStream("imgaes/logo.jpg");
try {
image = ImageDataFactory.create(toByteArray(logo));
} catch (IOException e) {
e.printStackTrace();
}
Image img = new Image(image);
img.scaleAbsolute(width, height); // 图片宽高
img.setFixedPosition(x, y); // 图片坐标 左下角(0,0)
canvas.add(img);
}
}
// 页脚
protected static class Footer implements IEventHandler {
protected PdfFormXObject placeholder; // 相对坐标系
protected float x = 82f;
protected float y = 50f;
protected float imageWidth = 6f;
protected float imageHeight = 78f;
protected float space = 10f;
public Footer() {
placeholder =
new PdfFormXObject(new Rectangle(0, 0, 500, 78));
}
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdf = docEvent.getDocument();
PdfPage page = docEvent.getPage();
Rectangle pageSize = page.getPageSize();
PdfCanvas pdfCanvas = new PdfCanvas(
page.getLastContentStream(), page.getResources(), pdf);
pdfCanvas.addXObject(placeholder, x + space, y);
Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
ImageData image = null;
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream buleRed = loader.getResourceAsStream("imgaes/bule_red.JPG");
try {
image = ImageDataFactory.create(toByteArray(buleRed));
} catch (IOException e) {
e.printStackTrace();
}
Image img = new Image(image);
img.scaleAbsolute(imageWidth, imageHeight);
img.setFixedPosition(x, y);
canvas.add(img);
writeInfo(pdf);
pdfCanvas.release();
}
public void writeInfo(PdfDocument pdf) {
Canvas canvas = new Canvas(placeholder, pdf);
canvas.setFontSize(7.5f);
PdfFont pdfFont = null;
try {
// 微软雅黑
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream msyh = loader.getResourceAsStream("fonts/msyh.ttf");
pdfFont = PdfFontFactory.createFont(toByteArray(msyh), PdfEncodings.IDENTITY_H, false);
} catch (IOException e) {
e.printStackTrace();
}
canvas.setFont(pdfFont); // 需要单独设置一下字体才能使用中文
canvas.showTextAligned("http://www.xxxx.com",
0, 65, TextAlignment.LEFT);
canvas.showTextAligned("深圳市南山区学府路东xxxxx xxxxxx",
0, 50, TextAlignment.LEFT);
canvas.showTextAligned("xxxxx Ixxxxxx,Xuefu Road Ease,Nan Shan District, Shenzhen xxxxxx",
0, 35, TextAlignment.LEFT);
canvas.showTextAligned("Tel:0755-xxxxx Fax:212-xxxxxx",
0, 20, TextAlignment.LEFT);
}
}
// 水印
protected static class WatermarkingEventHandler implements IEventHandler {
protected float x = 298f;
protected float y = 421f;
@Override
public void handleEvent(Event event) {
PdfDocumentEvent docEvent = (PdfDocumentEvent) event;
PdfDocument pdfDoc = docEvent.getDocument();
PdfPage page = docEvent.getPage();
PdfFont font = null;
try {
font = PdfFontFactory.createFont(FontConstants.HELVETICA_BOLD);
} catch (IOException e) {
e.printStackTrace();
}
PdfCanvas canvas = new PdfCanvas(page.newContentStreamBefore(), page.getResources(), pdfDoc);
new Canvas(canvas, pdfDoc, page.getPageSize())
.setFontColor(Color.LIGHT_GRAY)
.setFontSize(60)
.setFont(font)
.showTextAligned(new Paragraph("W A T E R M A R K"), x, y, pdfDoc.getPageNumber(page),
TextAlignment.CENTER, VerticalAlignment.MIDDLE, 45);
}
}
public static byte[] toByteArray(InputStream input) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
int n = 0;
while (-1 != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
return output.toByteArray();
}
}
Java实现Html转PDF的方法的更多相关文章
- Linux系统下Java 转换Word到PDF时,结果文档内容乱码的解决方法
本文分享在Linux系统下,通过Java 程序代码将Word转为PDF文档时,结果文档内容出现乱码该如何解决.具体可参考如下内容: 1.问题出现的背景 在Windows系统中,使用Spire.Doc ...
- Java实现HTML转PDF的总结
Java实现HTML转PDF的几种方法—主要解决中文乱码问题 第一:同事在做HTML转PDF中遇到乱码问题 ********************************************** ...
- Java 加密、解密PDF文档
本篇文章将介绍通过Java编程来设置PDF文档保护的方法.我们可以设置仅用于查阅文档的密码,即该通过该密码打开文档仅用于文档阅读,无法编辑:也可以设置文档编辑权限的密码,即通过该密码打开文档时,文档为 ...
- Java 合并、拆分PDF文档
处理PDF文档时,我们可以通过合并的方式,来任意组几个不同的PDF文件或者通过拆分将一个文件分解成多个子文件,这样的好处是对文档的存储.管理很方便.下面将通过Java程序代码介绍具体的PDF合并.拆分 ...
- java操作Excel、PDF文件
java操作Excel.PDF文件 分享者:Vashon 分享来源:CSDN博客 下面这些是在开发中用到的一些东西,有的代码贴的不是完整的,只是贴出了关于操作EXCEL的代码: jxl是一个*国人写的 ...
- Java 添加、验证PDF 数字签名
在设置文档内容保护的方法中,除了对文档加密.添加水印外,应用数字签名也是一种有效防伪手段.数字签名的文件比较容易验证,并且具有较高的权威性和可信度.在PDF文档中,有可直接添加或验证数字签名的功能方法 ...
- Java 将Excel转为PDF
本文将介绍在Java程序中如何将Excel工作簿转为PDF文档的,包括: 将整个工作簿转为PDF 将指定工作表转为PDF 使用工具:Free Spire.XLS for Java (免费版) Ja ...
- 01.在Java中如何创建PDF文件
1.简介 在这篇快速文章中,我们将重点介绍基于流行的iText和PdfBox库从头开始创建 PDF 文档. 2. Maven 依赖 <dependency> <groupId> ...
- Java编程实战宝典PDF (中文版带书签)
Java编程实战宝典PDF 目录 第1篇 Java基础知识入门第1章 Java的开发运行环境( 教学视频:57分钟)1.1 Java运行原理与Java虚拟机1.1.1 Java运行原理简述1.1.2 ...
随机推荐
- spring容器启动扩展
我们可以在spring容器启动时添加自己的定制化初始化工作. 1)具体方法为:在web.xml中配置初始化属性contextInitializerClasses的值例如: <context-pa ...
- python数据结构之树和二叉树(先序遍历、中序遍历和后序遍历)
python数据结构之树和二叉树(先序遍历.中序遍历和后序遍历) 树 树是\(n\)(\(n\ge 0\))个结点的有限集.在任意一棵非空树中,有且只有一个根结点. 二叉树是有限个元素的集合,该集合或 ...
- Akka(29): Http:Server-Side-Api,Low-Level-Api
Akka-http针对Connection的两头都提供了方便编程的Api,分别是Server-Side-Api和Client-Side-Api.通过这两个Api可以大大提高编程效率.当然,上期我们提到 ...
- 入侵必练的CMD命令
入侵必练的CMD命令 我们都知道和目标主机建立IPC$连接后,要把后门,木马之类的软件传过去,其实这个命令是DOS基础的 命令,我这里就写个格式. 一.呵呵,命令一写就知道了吧,在网上看的太多了,其他 ...
- (转)log4j使用介绍
原文出自: log4j使用介绍 日志是应用软件中不可缺少的部分,Apache的开源项目Log4j是一个功能强大的日志组件,提供方便的日志记录.以下是个人经验,具体请参考Log4j文档指南. Log4j ...
- VirtualBox虚拟磁盘文件.vdi从C盘转移到D盘
起因很简单:C盘快爆满了,用PS修个图的都无法保存.用Everything搜索了下C盘,发现有个WIN7的.vdi文件,竟然占据了15G的宝贵C盘空间!记得有一次,我直接把.vdi文件转移,然后在Vi ...
- 如何内网搭建NuGet服务器
NuGet 是.NET程序员熟知的给.NET项目自动配置安装library的工具,它可以直接安装开源社区中的各个公用组件,可以说是非常方便.不过,有些时候,公司内部的公用的基础类库,各个项目都有引用, ...
- 使用python实现计算器功能
学习python过程中的作业.实现了+.-.×./.及幂运算,支持括号优先级. 代码为python3.5 import re def formatEquation(string): string = ...
- Quart.Net分布式任务管理平台
无关主题:一段时间没有更新文章了,与自己心里的坚持还是背驰,虽然这期间在公司做了统计分析,由于资源分配问题,自己或多或少的原因,确实拖得有点久了,自己这段时间也有点松懈,借口就不说那么多 ...
- 使用grunt-init自动创建gruntfile.js和package.json文件
使用grunt-init可以自动创建gruntfile.js和package.json文件.下面说一下过程: 1.全局安装grunt-init npm install -g grunt-init 2. ...