Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 536|回复: 0

Java基于Itext7实现Html转PDF的方法,解决老版本缺陷。

[复制链接]
  • TA的每日心情
    奋斗
    2024-11-24 15:47
  • 签到天数: 804 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726782
    发表于 2021-6-6 23:21:27 | 显示全部楼层 |阅读模式

    写在前面

    以下路径问题根据项目结构自己修改,以下是我使用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();
        }
    }
    
    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-2 11:48 , Processed in 0.059500 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表