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,一经查实,立即删除!

相关文章

Ubuntu 防火墙 ufw

UbuntuHelp:UFW &#xff1a;http://wiki.ubuntu.org.cn/UbuntuHelp:UFW Ufw使用指南&#xff1a;http://wiki.ubuntu.org.cn/Ufw使用指南 ubuntu ufw防火墙&#xff1a;http://wap.dongnanshan.com/fn.php?subuntu ufw防火墙 UFW要领&#xff1a;通用防火墙规则和命令&#x…

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

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

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

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

小虎队 - 情难枕

现场感很好。。。时间在不远的前方穿梭&#xff0c;走在街头看着形色匆匆赶路回家的人群&#xff0c;这个城市&#xff0c;无声无息的变化........ 在这个意义上已到成熟的年纪&#xff0c;在茶语谈笑间总会想起往日的欢愉&#xff0c;在每个80后成长的人&#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 命令行基本…

PowerDesiGner数据库设计

原文地址&#xff1a;http://hi.baidu.com/shunkunl/blog/item/871c75ef8596faeace1b3e00.htmlPowerDesign&#xff1a;PowerDesign是 Sybase推出的主打数据库设计工具。PowerDesign致力于采用基于Entiry-Relation的数据模型&#xff0c;分别从概念数据模型 (Conceptual Data M…

mipony linux客户端,Mipony网盘下载工具

Mipony是一个特别设计的下载管理器下载的免费网盘如Rapidshare&#xff0c;Megaupload&#xff0c;Hotfiles&#xff0c;Gigasize&#xff0c;Filefactory&#xff0c;Mediafire&#xff0c;Netload。简介&#xff1a;支援“HJSplit”档案合并功能&#xff0c;还可以自动侦测网…

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

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

Effective Java~2.Builder代替多参数Constructor

Builder模式 // Builder Pattern public class NutritionFacts {private final int servingSize;private final int servings;private final int calories;private final int fat;private final int sodium;private final int carbohydrate;public static class Builder {// R…

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…

Effective Java~3. 私有Constructor 或Enum 强化单例

1、私有化构造器 // Singleton with static factory public class Elvis {private static final Elvis INSTANCE new Elvis();private Elvis() { ... }public static Elvis getInstance() { return INSTANCE; }public void leaveTheBuilding() { ... } } 可序列化对象单例&a…

VMware虚拟机软件

http://www.xuniji.com/vmware/ 虚拟机之家 http://www.ccw.com.cn/ 如何配置VMware虚拟机网络环境全程图解 nyytwngmail.com 8 转载于:https://www.cnblogs.com/hnytwn/archive/2009/10/28/1591044.html

linux 内置ssh,Linux ssh内置sftp配置说明

centos7 环境下已验证首先建立两个用户&#xff0c;用于sftp访问使用。eg:useradd -d /opt/sftp -s /bin/nologin sftp说明 -s /bin/nologin 禁止ssh登录服务器,其实如果将用户设置为用于sftp的话,不做此设置也是无法登录的会提示:This service allows sftp connections onl…

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;输入…