xhtmlrenderer + iText-HTML转PDF

来源:xhtmlrenderer + iText-HTML转PDF_hunan961的博客-CSDN博客_xhtmlrenderer

xhtmlrendere+itext2.0.8 将html转成pdf,带样式、图片(也支持二维码、条形码)等

主要步骤

  1. 生成html(css样式直接放在style中)
  2. html转换pdf方法
  3. 数据返回给前端

详细过程

  1. html模板:
private static final String DEFAULT_HTML = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n" +"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" +"<head>\n" +"<meta charset=\"utf-8\" />\n" +"<style> \n" +"  body{ padding:0; margin:0; font-family:Microsoft YaHei; @page {size:20mm, 35mm;}} \n" +"</style>\n" +"</head>\n" +"<body>\n" +"  ${CONTENT}\n" +"</body>\n" +"</html>";

实际内容替换DEFAULT_HTML中的${CONTENT}

2.html转pdf

在这里插入图片描述

方法代码: 

    public static void htmlToPdf2(String html, ByteArrayOutputStream os) throws IOException {try {ITextRenderer renderer = new ITextRenderer();renderer.setDocumentFromString(html);ITextFontResolver fontResolver = renderer.getFontResolver();// 获取字体文件路径fontResolver.addFont(getFontPath2(), BaseFont.IDENTITY_H, BaseFont.NOT_EMBEDDED);renderer.getSharedContext().setReplacedElementFactory(new ImgReplacedElementFactory());renderer.layout();renderer.createPDF(os);os.flush();} catch (Exception e) {logger.error("Html:" + html);logger.error("Html To Pdf Failed", e);//            throw new CommonException("Html To Pdf Failed:" + e.getMessage());} finally {if (os != null) {os.close();}}}// 字体路径  private static String getFontPath2() {return HrptConstants.FONT_PATH + File.separator + HrptConstants.TTF_NAME_2;}
    /*** <p>* 图片处理优化-支持html中img标签的src为url或者base64* </p>*/public class ImgReplacedElementFactory implements ReplacedElementFactory {private final static String IMG_ELEMENT_NAME = "img";private final static String SRC_ATTR_NAME = "src";private final static String URL_PREFIX_NAME = "data:image";private final static String URL_BASE64 = "base64,";private final static Logger LOGGER = LoggerFactory.getLogger(ImgReplacedElementFactory.class);@Overridepublic ReplacedElement createReplacedElement(LayoutContext c, BlockBox box, UserAgentCallback uac, int cssWidth, int cssHeight) {Element e = box.getElement();if (e == null) {return null;}String nodeName = e.getNodeName();// 找到img标签if (nodeName.equals(IMG_ELEMENT_NAME)) {String url = e.getAttribute(SRC_ATTR_NAME);FSImage fsImage;try {InputStream imageStream = this.getImageStream(url, BaseConstants.Digital.ZERO);byte[] bytes = IOUtils.toByteArray(imageStream);// 生成itext图像fsImage = new ITextFSImage(Image.getInstance(bytes));} catch (Exception e1) {fsImage = null;}if (fsImage != null) {// 对图像进行缩放if (cssWidth != -1 || cssHeight != -1) {fsImage.scale(cssWidth, cssHeight);}return new ITextImageElement(fsImage);}}return null;}@Overridepublic void reset() {}@Overridepublic void remove(Element e) {}@Overridepublic void setFormSubmissionListener(FormSubmissionListener listener) {}/*** 重复获取网络图片3次,若三次失败则不再获取* @param url* @param tryCount* @return*/private InputStream getImageStream(String url, int tryCount) {if (tryCount > BaseConstants.Digital.TWO) {return null;}if (URL_PREFIX_NAME.equals(url.substring(0, 10))) {byte[] bytes = Base64.decode(url.substring(url.indexOf(URL_BASE64) + 7));//转化为输入流return new ByteArrayInputStream(bytes);}try {HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();connection.setReadTimeout(5000);connection.setConnectTimeout(5000);connection.setRequestMethod("GET");if (connection.getResponseCode() == HttpURLConnection.HTTP_OK) {InputStream inputStream = connection.getInputStream();return inputStream;} else {tryCount += 1;LOGGER.info("connectionError : {} , msg : {}", connection.getResponseCode(), connection.getResponseMessage());return getImageStream(url, tryCount);}} catch (IOException e) {LOGGER.error("connectionIOException : {} , trace : {}", e.getMessage(), e.getStackTrace());}return null;}}

3.最后通过response导出pdf给前端

最后,对于在开发过程中碰到的问题,做下记录和总结。

  • 字体问题,汉字不显示
  • html模板body里面font-family属性不要落了

字体路径要找的到你的字体文件 

font-family属性中的字体要和应用的字体文件字体相对应,举例:font-family中设置的是Microsoft YaHei字体,那么添加的字体文件就一定要是微软雅黑的(图中的msyh.ttc就是微软雅黑的字体文件)

字体下载:常用的字体在windows的自带font文件夹下基本上都有,实在没有就去网上自己找吧 

  • html中的img图片标签后缀问题

xhtmlrenderer会对html转成xml,所以对于html格式要求严格,现在前端生成的html中img标签往往都是不带后缀的,所以在接口调用时会报错,小问题,把html中的img加上后缀就好

        // templateContent -- html内容Document doc = Jsoup.parse(templateContent);// img标签后缀处理Elements img = doc.getElementsByTag("img");if (!img.isEmpty()) {for (Element element:img) {if (!element.toString().contains("/>") && !element.toString().contains("</img>")) {templateContent = templateContent.replace(element.toString(),element.toString() + "</img>");}}}

接口报错,html转pdf报错。
Html To Pdf Failed:Cant load the XML resource (using TRaX transformer). org.w 3c.dom.DOMException: NOT_FOUND_ERR: An attempt is made to reference a node in a context where it does n ot exist.

 这个问题蛮困扰的,html明明没有问题,然后一步一步debug发现,是因为html中有些标签中加了id属性导致的,根据源码看到的是,id转xml默认给的namespace都是空字符串而导致,查看http://www.w3.org/1999/xhtml也没看到说div标签和img标签支持id属性,最后做了html字符串处理,把id替换成了title

    // id处理,Element对象不支持id属性templateContent = templateContent.replaceAll("id=","title=");
  • img图片src为base64 code出现的一点问题

这里的业务场景是HTML中会有二维码或者条形码,用的都是img标签,后端会使用实际数据(这里是资产的编码)的二进制内容转成base64编码然后放入src中
替换,主要注意要加前缀URL_PREFIX_NAME :

    private final static String ID_BAR = "stylesBarCode";private final static String ID_QR = "stylesQrCode";private final static String SRC_ATTR_NAME = "src";private final static String URL_PREFIX_NAME = "data:image/png;base64,";private final static String CHARACTER_ENCODING = "utf-8";// img的src处理Element barElement = doc.getElementById(ID_BAR);Element qrElement = doc.getElementById(ID_QR);Base64.Encoder encoder = Base64.getEncoder();if (barElement != null) {byte[] bytes = CodeUtils.generateBarCode(assetVO.getAspAssetNum(), 60, 5, CHARACTER_ENCODING, "code39");templateContent = templateContent.replace(barElement.attr(SRC_ATTR_NAME), URL_PREFIX_NAME + encoder.encodeToString(bytes));}if (qrElement != null) {byte[] bytes = CodeUtils.generateQrCode(assetVO.getAspAssetNum(), 20, 20, CHARACTER_ENCODING);templateContent = templateContent.replace(qrElement.attr(SRC_ATTR_NAME), URL_PREFIX_NAME + encoder.encodeToString(bytes));}

工具方法,生成二维码,生成条形码(用的zxing):

/*** 生成二维码** @param text              内容* @param width             宽* @param height            高* @param characterEncoding 字符编码* @return 二进制内容*/public static byte[] generateQrCode(String text, int width, int height, String characterEncoding) {QRCodeWriter writer = new QRCodeWriter();HashMap<EncodeHintType, Object> config = new HashMap<>(BaseConstants.Digital.SIXTEEN);config.put(EncodeHintType.CHARACTER_SET, characterEncoding);try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {BitMatrix bar = writer.encode(text, BarcodeFormat.QR_CODE, width, height, config);MatrixToImageWriter.writeToStream(bar, HrptConstants.ImageType.PNG, stream);return stream.toByteArray();} catch (Exception e) {throw new CommonException(HrptMessageConstants.ERROR_GENERATE_QRCODE);}}/*** 生成条形码** @param text              内容* @param width             宽* @param height            高* @param characterEncoding 字符编码* @param barCodeType       条形码类型* @return 二进制内容*/public static byte[] generateBarCode(String text, int width, int height, String characterEncoding, String barCodeType) {BarCodeType codeType = BarCodeType.valueOf2(barCodeType);switch (codeType) {case CODE_39:return generateBarCode39(text, width, height, characterEncoding);case CODE_93:return generateBarCode93(text, width, height, characterEncoding);case CODE_128:return generateBarCode128(text, width, height, characterEncoding);default:throw new CommonException(HrptMessageConstants.UNSUPPORTED_CODE_TYPE);}}/*** 生成Code39条形码** @param text              内容* @param width             宽* @param height            高* @param characterEncoding 字符编码* @return 二进制内容*/public static byte[] generateBarCode39(String text, int width, int height, String characterEncoding) {Code39Writer writer = new Code39Writer();HashMap<EncodeHintType, Object> config = new HashMap<>(BaseConstants.Digital.SIXTEEN);config.put(EncodeHintType.CHARACTER_SET, characterEncoding);try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {BitMatrix bar = writer.encode(text, BarcodeFormat.CODE_39, width, height, config);MatrixToImageWriter.writeToStream(bar, HrptConstants.ImageType.PNG, stream);return stream.toByteArray();} catch (Exception e) {throw new CommonException(HrptMessageConstants.ERROR_GENERATE_BARCODE);}}/*** 生成Code93条形码** @param text              内容* @param width             宽* @param height            高* @param characterEncoding 字符编码* @return 二进制内容*/public static byte[] generateBarCode93(String text, int width, int height, String characterEncoding) {Code93Writer writer = new Code93Writer();HashMap<EncodeHintType, Object> config = new HashMap<>(BaseConstants.Digital.SIXTEEN);config.put(EncodeHintType.CHARACTER_SET, characterEncoding);try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {BitMatrix bar = writer.encode(text, BarcodeFormat.CODE_93, width, height, config);MatrixToImageWriter.writeToStream(bar, HrptConstants.ImageType.PNG, stream);return stream.toByteArray();} catch (Exception e) {throw new CommonException(HrptMessageConstants.ERROR_GENERATE_BARCODE);}}/*** 生成Code128条形码** @param text              内容* @param width             宽* @param height            高* @param characterEncoding 字符编码* @return 二进制内容*/public static byte[] generateBarCode128(String text, int width, int height, String characterEncoding) {Code128Writer writer = new Code128Writer();HashMap<EncodeHintType, Object> config = new HashMap<>(BaseConstants.Digital.SIXTEEN);config.put(EncodeHintType.CHARACTER_SET, characterEncoding);try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {BitMatrix bar = writer.encode(text, BarcodeFormat.CODE_128, width, height, config);MatrixToImageWriter.writeToStream(bar, HrptConstants.ImageType.PNG, stream);return stream.toByteArray();} catch (Exception e) {throw new CommonException(HrptMessageConstants.ERROR_GENERATE_BARCODE);}}
  • ImgReplacedElementFactory类中的getImageStream方法(代码上文贴过了)

如果是base64地址的就不用http请求了,直接转二进制再转InputStream,需要注意的是Base64的import不要用错了,否则图片解析不出

import com.lowagie.text.pdf.codec.Base64;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/496197.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

NASA打算送机器蜜蜂去探索火星上的生命痕迹

来源&#xff1a;国际智能机器人用机械昆虫做侦察兵是科幻电影里存在了多年的场景&#xff0c;如今现实中已经有科学家在做这件事&#xff0c;譬如用机械蜜蜂探索太空。NASA最近就花了12.5万美元资助一个名为“Marsbees”的火星探测工具的科研项目。“Marsbees”是一款微型机器…

linux usb 驱动漏洞,不测不知道 这么多的USB漏洞要从何“补”起?

原标题&#xff1a;不测不知道 这么多的USB漏洞要从何“补”起?[PConline 杂谈]生活中&#xff0c;USB接口可以说无处不在&#xff0c;路由器、打印机、投影机、PC电脑、台式机等等&#xff0c;且使用频率极高。当然&#xff0c;作为硬件设备的输入输出接口&#xff0c;其安全…

xhtmlrenderer 将html转换成pdf,完美css,带图片,手动分页,解决内容断开的问题

来源&#xff1a;xhtmlrenderer 将html转换成pdf&#xff0c;完美css&#xff0c;带图片&#xff0c;手动分页&#xff0c;解决内容断开的问题 - 煮过的花朵 - 博客园 之前用itext7将html导出为pdf&#xff0c;比较方便&#xff0c;代码较少&#xff0c;而且支持base64的图片。…

商汤科技宣布C轮战略融资6亿美元 阿里领投苏宁跟投

来源&#xff1a;雷帝网 人工智能平台公司商汤科技SenseTime宣布完成6亿美元C轮融资&#xff0c;由阿里巴巴集团领投&#xff0c;新加坡主权基金淡马锡、苏宁等投资机构和战略伙伴跟投。商汤科技联合创始人、CEO徐立表示&#xff1a;商汤科技C轮融资将进一步夯实公司在人工智能…

MongoDB Shell和Robo3T使用以及与SQL语法比较

From&#xff1a;MongoDB Shell 了解使用 - 大葱哥 - 博客园 MongoDB基本管理命令&#xff1a;MongoDB基本管理命令_千与的专栏-CSDN博客_mongo查询命令 MongoDB常用操作命令大全&#xff1a;MongoDB常用操作命令大全_piaocoder-CSDN博客_mongodb常用命令 mongodb 命令行基本…

2018全球100个最有价值的科技品牌 18个中国品牌上榜

来源&#xff1a;全球企业动态英国品牌评估机构Brand Finance发布“2018全球100个最有价值的科技品牌榜”(Top 100 most valuable tech brands 2018)&#xff0c;前五位都是美国品牌。美国上榜品牌总价值9590亿美元&#xff0c;占百强品牌总价值14673亿美元的65%。亚马逊跃升至…

Alpine Linux 使用简介

From&#xff1a;https://www.aliyun.com/jiaocheng/137717.html Alpine Linux、CoreOS、RancherOS、Red Hat 原子项目、 VMware光子操作系统比较https://blog.csdn.net/hxpjava1/article/details/78482987 Alpine Linux配置使用技巧&#xff1a;https://www.aliyun.com/jiao…

实例学习SSIS(五)--理论介绍SSIS

导读&#xff1a; 实例学习SSIS&#xff08;一&#xff09;--制作一个简单的ETL包 实例学习SSIS&#xff08;二&#xff09;--使用迭代 实例学习SSIS&#xff08;三&#xff09;--使用包配置 实例学习SSIS&#xff08;四&#xff09;--使用日志记录和错误流重定向 实例学习SSIS…

MIT教授Tomaso Poggio演讲与专访:智能背后的科学与工程 | 腾讯AI Lab学术论坛

来源&#xff1a;腾讯AI实验室腾讯AI Lab第二届学术论坛在深圳举行&#xff0c;聚焦人工智能在医疗、游戏、多媒体内容、人机交互等四大领域的跨界研究与应用。全球30位顶级AI专家出席&#xff0c;对多项前沿研究成果进行了深入探讨与交流。腾讯AI Lab还宣布了2018三大核心战略…

linux捕捉信号sigint失败,为shell布置陷阱:trap捕捉信号方法论

本文目录&#xff1a;1.1 信号说明1.2 trap布置陷阱1.3 布置完美陷阱必备知识家里有老鼠&#xff0c;快消灭它&#xff01;哎&#xff0c;又给跑了。老鼠这小东西跑那么快&#xff0c;想直接直接消灭它还真不那么容易。于是&#xff0c;老鼠药、老鼠夹子或老鼠笼就派上用场了&a…

Trade Stages - The Trade Path

Gieno Trade Stages - The Trade Path STARTS OFF “greed orientated.”Loses because:1 Market problemsNot a zero sum game, a “very negative” sum gameMarket psychology – doing the wrong thing at the wrong timeThe majority is always wrongMarket exists on ch…

win10、oneplus7pro 使用 Kali

1、Windows 10 使用 Kali Linux子系统 微软为 Windows Subsystem for Linux (WSL) 带来了著名的 Kali Linux &#xff0c;无虚拟机&#xff0c;无Docker实现Windows 和 Kali Linux 交互。 window 开启 wsl 功能&#xff1a; 1.打开控制面板&#xff08; winR&#xff0c;输入…

干货|十大产业方向深度解析!《2020科技产业趋势报告》

来源&#xff1a;机器人大讲堂报告下载&#xff1a;https://pan.baidu.com/s/1BKf2rINXx0CVLhgokfYrgQ未来智能实验室是人工智能学家与科学院相关机构联合成立的人工智能&#xff0c;互联网和脑科学交叉研究机构。未来智能实验室的主要工作包括&#xff1a;建立AI智能系统智商评…

交互式数据包处理程序 Scapy 用法

From&#xff1a;https://www.cnblogs.com/hongxueyong/p/5641475.html Scapy 用法官方文档&#xff1a;http://scapy.readthedocs.io/en/latest/#starting-scapyAbout ScapyScapy is a Python program that enables the user to send, sniff and dissect and forge network pa…

展望2021年:智能机器人可监督工业机器人干活,效率提升30%

来源&#xff1a;极客网会帮我们吸地板、在公共场所担任导引员或是拆除炸弹的机器人呢可能感觉比较有趣&#xff0c;但那些负责组装汽车以及在工厂生产在线帮忙拾取物品的机器人&#xff0c;在整体价值上要高得多&#xff0c;而且也有越来越多的工/商业或消费性应用产品是由这种…

ASP。NET的设计思想

自从有了html与http&#xff0c;就有了浏览器与Web服务器&#xff0c;并有了Web应用&#xff0c;最初的交互模式是这样的&#xff1a; 该模式很好地运行了很多年。然而&#xff0c;随着计算机应用的发展&#xff0c;人们越来越不满足于只有静态内容的页面&#xff0c;而由某种机…

Kali linux 渗透测试技术之搭建WordPress Turnkey Linux及检测WordPress 应用程序漏洞

From&#xff1a;https://bbs.ichunqiu.com/thread-15716-1-1.html 怎样用 WPScan&#xff0c;Nmap 和 Nikto 扫描和检查一个 WordPress 站点的安全性&#xff1a;https://www.cnblogs.com/chayidiansec/p/7989274.html 为了收集用于测试的应用程序&#xff0c; Turnkey Linux…

AI芯片格局分布

来源&#xff1a;中国科学院自动化研究所 作者&#xff1a; 吴军宁如果说2016年3月份AlphaGo与李世石的那场人机大战只在科技界和围棋界产生较大影响的话&#xff0c;那么2017年5月其与排名第一的世界围棋冠军柯洁的对战则将人工智能技术推向了公众视野。阿尔法狗&#xff08;…

科学互驳:大脑细胞活到老,长到老?

来源&#xff1a;中国生物技术网 作者&#xff1a;格格科学家发现&#xff0c;人脑中与学习、记忆和情感相关的区域在成年后依然会持续产生新的神经元。这与过去的理论恰恰相反&#xff0c;即青春期之后大脑停止产生新的神经元。这项发现有助于我们开发治疗神经系统疾病的新方…

AI综述专栏 | 朱松纯教授浅谈人工智能:现状、任务、构架与统一

作者&#xff1a;朱松纯来源&#xff1a;人工智能前沿讲习班导读本文作者&#xff1a;朱松纯&#xff0c;加州大学洛杉矶分校UCLA统计学和计算机科学教授&#xff0c;视觉、认知、学习与自主机器人中心主任。文章前四节浅显探讨什么是人工智能和当前所处的历史时期&#xff0c;…