最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Java生成PDF的若干坑

2020-11-13 22:34 作者:只有神知道的歌詞特效  | 我要投稿

轉(zhuǎn)自知乎@鞠騫:

https://zhuanlan.zhihu.com/p/34519204


知乎搜索“開源 PDF 嵌入字體 Java”的結(jié)果

本文首發(fā)于個人微信公眾號《andyqian》,期待你的關(guān)注~

前言

在工作中,有使用Java生成大量的電子憑證,也就是PDF。在之前的文章中,有寫過,如何通過Java生成PDF。這里就不再描述,寫這篇文章主要是為了記錄我在使用Java生成PDF過程中犯過的錯,以及踩過的坑。

內(nèi)存溢出

之前在使用生成方案的時候,我們一開始采用的是《PD4ML》框架,這個框架相對來說,要小巧些。但是不足的是:它是閉源的。在批量生成PDF文件時會出現(xiàn)內(nèi)存泄漏的現(xiàn)象。每每我們在分析內(nèi)存原因時,都止步于下面這一段代碼:

?private void createPdfByHtml(String html, File file) throws Exception{ ? ? ? ?FileOutputStream fos = new FileOutputStream(file); ? ? ? ? ?... ? ? ? ?// 這一行 ? ? ? ?pd4ml.render(sr, fos, new URL("http://"), "UTF-8"); ? ? ?}

因此,我們不得不考慮換一個第三方庫。目前我們使用的是開源庫《iText》,到目前為止,穩(wěn)定運(yùn)行。

(我們在選擇第三方框架時,盡量選擇開源的,因為在出現(xiàn)問題后,我們可以通過查看源代碼分析,甚至可以修改源代碼進(jìn)行優(yōu)化)。

字體路徑

這個問題是這樣的,我們生成PDF時為了支持中文字體,需要通過路徑加載指定的字體文件。而開發(fā)環(huán)境是windows,測試/生產(chǎn)環(huán)境均是Linux。字體放在

/resources/fonts/

因為:

  1. 在Window下獲取Thread.currentThread()為null值

  2. 在Linux下獲取this.class.getClass().getResource(“/“).getPath();獲取為null值

所以在加載路徑時,我們通過下述方法進(jìn)行了兼容處理。

/** ? ? * 應(yīng)用場景: ? ? * 1.在windows下,使用Thread.currentThread()獲取路徑時,出現(xiàn)空對象,導(dǎo)致不能使用 ? ? * 2.在linux下,使用PdfUtils.class獲取路徑為null, ? ? * 獲取字體路徑 ? ? * @return ? ? */ ? ?private static String getFontPath(){ ? ? ? ?String path=""; ? ? ? ?// 1. 生產(chǎn)環(huán)境路徑 ? ? ? ?ClassLoader classLoader= Thread.currentThread().getContextClassLoader(); ? ? ? ?URL url = (classLoader==null)?null:classLoader.getResource("/"); ? ? ? ?String threadCurrentPath = (url==null)?"":url.getPath(); ? ? ? ?// 2. 如果線程獲取為null,則使用當(dāng)前PdfUtils.class加載路徑 ? ? ? ?if(StringUtil.isBlank(threadCurrentPath)){ ? ? ? ? ? ? path = PdfUtils.class.getClass().getResource("/").getPath(); ? ? ? ?} ? ? ? ?// 3.拼接字體路徑 ? ? ? ?StringBuffer stringBuffer = new StringBuffer(path); ? ? ? ?stringBuffer.append("/fonts/SIMKAI.TTF"); ? ? ? ?path = stringBuffer.toString(); ? ? ? ?logger.info("getFontPath threadCurrentPath: {} ?path: {}",threadCurrentPath,path); ? ? ? ?return path; ? ?}

從這個方面也能體現(xiàn)一個程序員常見現(xiàn)象。

  1. 為什么在我電腦上是好的,在你電腦上不行,一定是你電腦問題。

  2. 不應(yīng)該啊,在我電腦上是好的。絕對沒問題。


小伙伴們知道這個梗嗎? 這也是很多程序員使用與生產(chǎn)環(huán)境一致的系統(tǒng)作為開發(fā)機(jī)的原因。

字符編碼

問題是這樣的。是由于在生成PDF文件時轉(zhuǎn)換字節(jié)流時沒有指定編碼導(dǎo)致的: 如下所示:

public static void createdPdfByItextHtml(String htmlContent,File file){ ? ? ? ? ? ?... ? ? ? ? ? ?try { ? ? ? ? ? ? ? ?inputStream= new ByteArrayInputStream(htmlContent.getBytes()); ? ? ? ? ? ? ? ?outputStream = new FileOutputStream(file); ? ? ? ? ? ?...

此處省略掉部分代碼,此時htmlContent.getBytes()該方法沒有指定默認(rèn)的編碼導(dǎo)致的。具體的原因,在《初探JDK源碼之默認(rèn)字符集》這篇文章中有詳細(xì)的描述。

生僻字

這個坑是這樣的,因為正式公文的字體是:Kaiti_GB2312的字體。我們使用過一段時間發(fā)現(xiàn),Kaiti_GB2312字體對于生僻字支持不友好。
例如:

例如: 陳垚,通過Kaiti_GB2312生成后就只能顯示為: 陳

其中字就沒有正常顯示。這個問題是非常嚴(yán)重的。如果電子憑證中姓名錯誤,也就代表,這份電子憑證是沒有法律效應(yīng)的。在經(jīng)過測試后,我們最終使用了Kaiti字體,代替了Kaiti_GB2312。(這個坑,還真的不容易發(fā)現(xiàn))

5. 枚舉傳遞類型
如果Dubbo接口中使用枚舉作為參數(shù)時,如果只有單方面更新枚舉的值。會導(dǎo)致序列化出錯的問題。也就是說:

  1. 調(diào)用方在更新枚舉后,如果接受方不同時更新枚舉,會導(dǎo)致接收方的參數(shù)為空對象。

注意點:

  1. Dubbo服務(wù)調(diào)用之間,盡量不要使用enum類型。

嚴(yán)格的HTML語義標(biāo)簽

使用過itext的朋友應(yīng)該知道。HTML內(nèi)容是需要嚴(yán)格的標(biāo)簽的。也就是說,一個開始標(biāo)簽,對應(yīng)一個結(jié)束標(biāo)簽。如果不匹配,則會生成失敗。(像<img src="www.baidu.com"/>) 標(biāo)簽除外。

錯誤的html:

<html> ?<head> ? ? <title></title> ?</head> ?<body> ? ?<span>Hello World!</span></span> ?</body> </html>

上面這段html內(nèi)容,就多了一個</span>結(jié)束標(biāo)簽,就屬于非嚴(yán)格的HTM語義標(biāo)簽。

執(zhí)行后,就會有下面這類錯誤:

com.itextpdf.tool.xml.exceptions.RuntimeWorkerException: Invalid nested tag span found, expected closing tag body. ? ?at com.itextpdf.tool.xml.XMLWorker.endElement(XMLWorker.java:134) ? ?at com.itextpdf.tool.xml.parser.XMLParser.endElement(XMLParser.java:396)

最后

有些系統(tǒng),看起來容易。做起來可并非易事。在實現(xiàn)期間可能會遇到各種各樣的奇葩問題。但恰恰,就是這些怪問題。給我們積累了經(jīng)驗。我一直覺得,在寫代碼方面,是一個“倒霉”的人,因為各種奇怪的問題,都能讓我遇到。


公眾號內(nèi)回復(fù):【PDF】,即可獲取項目。記得更換字體哦!


相關(guān)閱讀:

《說說面試那些事》

《Java PDF生成方案之iText》

《一個Java細(xì)節(jié)!》

《記一個有趣的Java OOM!》




Java生成PDF的若干坑的評論 (共 條)

分享到微博請遵守國家法律
平泉县| 巴东县| 包头市| 寿阳县| 岳池县| 濉溪县| 额敏县| 六盘水市| 什邡市| 宜章县| 韶山市| 梧州市| 河池市| 华容县| 临沂市| 南京市| 安化县| 深水埗区| 西乌珠穆沁旗| 九江县| 化德县| 呼和浩特市| 调兵山市| 金湖县| 大港区| 辽宁省| 普定县| 灵石县| 罗源县| 斗六市| 东乡族自治县| 沿河| 临潭县| 敖汉旗| 绥芬河市| 民乐县| 晋宁县| 涿鹿县| 香港 | 丰顺县| 阳山县|