代码先锋网 代码片段及技术文章聚合

Java基于Itext7实现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();
    }
}

版权声明:本文为weixin_30294295原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30294295/article/details/99769719

智能推荐

Java实现HTML页面转PDF(iText+FreeMarker)

Java实现HTML页面转PDF(iText+FreeMarker) 由于需求需要,但是又不会操作,从网上找了一大堆,都发现有点坑,可能我这份也有点坑,但是希望能给你一个思路。 一开始找的是word模板---->word---->pdf,走完流程步,发现最后下载的是pdf版的xml文件 找了很多方法,发现都没什么解决方式 OpenOffice 是无效的,其他的没测试 如果本来是word...

java使用itext实现html代码转pdf

1、引入jar包 <dependency>   <groupId>com.lowagie</groupId>   <artifactId>itext</artifactId>   <version>4.2.2</version>   <type>pom</t...

使用itext7把html导出为pdf,并设置中文

导包 代码 https://gitee.com/yue126/itext7-demo.git html导出为pdf(问题:中文无法显示) html导出为pdf(解决中文无法显示的问题)...

itext7填充PDF模板(二)填充pdf模板

本篇文章主要讲解的是如何采用itext7进行模板填充。 创建PdfReader PdfWriter,构造函数支持路径已经流的形式。 创建PDFDocument 创建并设置中文字体 获取文本域的信息 判断文本框长度是否够单行显示 填充信息 设置模板不可编辑 注意点: 需要设置中文字体否则填充中文信息时可能无法显示 需要对单行的文本域进行宽度判断,目前代码中是打印了异常日志 如果未设置文本不可编辑的话...

itext7将pdf模板转成pdf文件

使用adobe Acrobat Pro DC制作pdf模板 这里就略了,大家可以上网搜下,有很多方法 撸代码 在itext7第一节的基础上在pom文件添加junit依赖方便测试 写实现方法 亲测有效...

猜你喜欢

解决iText7输出pdf没有中文字符问题

背景 遇到个需求需要用Java导出PDF文件,权衡之下选择了iText,Java操作pdf的各个开源库之间的对比可以参考https://blog.csdn.net/u012397189/article/details/80196974 然后就是在pom文件中引包,网上很多都用iText5,这里我用新版的iText7 这里附上iText7的官方文档中文翻译 https://github.com/iT...

itext实现html转pdf下载

maven项目为例 pom.xml 主要实现代码 url需要下载的html地址 解决中文:fonts/simsun.ttc,需要将字体文件放入到项目当中,由于是项目的jar包中,只能读取到流文件,所以,需要将流文件读取到文件目录下存储,当然自己也可以改成配置真实物理路径,这样就不需要读取流文件,直接传字体文件的实际路径就好。然后把pdf流输出下载即可。 如果需要每页添加水印,需要使用到PdfRea...

通过itext实现html转pdf

使用到的jar包:【支持识别CSS换行】 PDFUtil工具类...

HTML转PDF——Itext

1.依赖 2.代码...

数据集合与分组操作:数据聚合,逐列及多函数应用,返回不含行索引的聚合数据, 数据透视表与交叉表(crosstab)

2.1 数据聚合 聚合是指所有根据数组产生标量值的数据转换过程。就像mean count min sum等, 函数 描述 count 分组中的非NA 值数量 sum 非NA值的累计和 mean 非NA 值的均值 median 非NA 值的算术中位数 std,var 无偏的(n-1 分母)标准差和方差 min max 非NA 值的最大值最小值 prod 非NA 值 的乘积 first,last 非N...