xhtmlrenderer + iText-HTML转PDF
來源:xhtmlrenderer + iText-HTML轉PDF_hunan961的博客-CSDN博客_xhtmlrenderer
xhtmlrendere+itext2.0.8 將html轉成pdf,帶樣式、圖片(也支持二維碼、條形碼)等
主要步驟
詳細過程
實際內容替換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 :
工具方法,生成二維碼,生成條形碼(用的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;總結
以上是生活随笔為你收集整理的xhtmlrenderer + iText-HTML转PDF的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java加密与解密的艺术~数字证书~模型
- 下一篇: Java测试驱动开发--总结