Webview加載pdf遇到的一些坑及解決方法
大廠技術(shù)??高級前端??Node進(jìn)階
點(diǎn)擊上方?程序員成長指北,關(guān)注公眾號
回復(fù)1,加入高級Node交流群
問題來源
問題一切的來源還是得從那天說起...
有一天產(chǎn)品小王拿著電腦興致勃勃的來到我的工位旁:“誒,小付,這里有個(gè)在線pdf預(yù)覽的功能,你看下能不能做。”
聽完我心中暗想:這還不簡單,加載在線pdf不就和加載網(wǎng)頁一樣的,webview加上pdf鏈接,搞定!
這一想完,立即比了個(gè)OK:“沒問題,簡單!”
說完立馬開干,新建項(xiàng)目,準(zhǔn)備好webview,pdf鏈接,webView?.loadUrl("https://www.gjtool.cn/pdfh5/git.pdf"),點(diǎn)擊Run,滿心歡喜等待pdf加載出來的那一刻。
咦?怎么一片空白,難道是webview設(shè)置項(xiàng)有問題,但是加載網(wǎng)頁一點(diǎn)問題都沒啊。這時(shí)候瞥見隔壁iOS老大哥已經(jīng)成功加載出pdf了,一問也是用webview加載的,那為啥擱我這就不行?看不起我?
帶著疑惑查了下。
原來Android的webview壓根就不支持加載pdf。
Android與iOS不同,iOS加載pdf,不管本地還是在線,直接使用webview渲染就可以了,而Android卻做不到。
那該怎樣去加載pdf?
加載的方案有很多,比如直接跳到第三方瀏覽器加載,但產(chǎn)品要求只能在app內(nèi)部預(yù)覽,pass;比如在pdf鏈接前加上谷歌服務(wù),但在國內(nèi)是無法訪問的,pass;比如下載后再進(jìn)行加載,但當(dāng)pdf體積大且網(wǎng)絡(luò)不好時(shí),下載就會出現(xiàn)問題,又pass;
方式有多種,第三方的輪子也有很多,但適合自己的開發(fā)需求,以及滿足UI設(shè)計(jì),則就需要進(jìn)行二次改造了。
經(jīng)過多方對比,使用webview加載pdf的方案更符合大多數(shù)的場景。
以下就會從webview加載pdf的方案出發(fā),描述在開發(fā)時(shí)所涉及到的問題點(diǎn)。
我的爬坑之旅開始了!
初步加載
webview加載pdf的初步設(shè)想是使用js的方式去渲染,
新建一個(gè)js
var?url?=?location.search.substring(1);
PDFJS.cMapUrl?=?'https://unpkg.com/[email protected]/cmaps/';
PDFJS.cMapPacked?=?true;
var?pdfDoc?=?null;
function?createPage()?{
????var?div?=?document.createElement("canvas");
????document.body.appendChild(div);
????return?div;
}
function?renderPage(num)?{
????pdfDoc.getPage(num).then(function?(page)?{
????????var?viewport?=?page.getViewport(2.0);
????????var?canvas?=?createPage();
????????var?ctx?=?canvas.getContext('2d');
????????canvas.height?=?viewport.height;
????????canvas.width?=?viewport.width;
????????page.render({
????????????canvasContext:?ctx,
????????????viewport:?viewport
????????});
????});
}
PDFJS.getDocument(url).then(function?(pdf)?{
????pdfDoc?=?pdf;
????for?(var?i?=?1;?i?<=?pdfDoc.numPages;?i++)?{
????????renderPage(i)
????}
});
新建Html
"en">
????"UTF-8">
????"viewport"
??????????content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=4.0,user-scalable=no"/>
????Document
????
????
????
準(zhǔn)備好js和html后,使用webview對在線pdf(www.gjtool.cn/pdfh5/git.p…[1] 進(jìn)行加載,
webView?.loadUrl("file:///android_asset/index.html?https://www.gjtool.cn/pdfh5/git.pdf");
運(yùn)行成功后,pdf也加載出來了。

添加雙指縮放
好家伙,終于是加載出了pdf,我滿心歡喜的拿著效果給產(chǎn)品看一看。
“你這是加載出來了,但是字體看著有點(diǎn)小,你看能不能加上雙指縮放的功能”。產(chǎn)品小王看了一眼,
“那必須能啊。”
將webview設(shè)置為支持縮放狀態(tài),并且useWideViewPort設(shè)置為true,讓W(xué)ebivew支持meta標(biāo)簽的viewport屬性,
settings?.useWideViewPort?=?true
settings?.builtInZoomControls?=?true
settings?.setSupportZoom(true)
settings?.displayZoomControls?=?false?//不顯示縮放按鈕
并且修改html中的meta屬性,設(shè)置minimum-scale,maximum-scale屬性,以及將user-scalable置為yes,
運(yùn)行成功后,成功對pdf進(jìn)行雙指縮放。
產(chǎn)品看了過后,點(diǎn)了點(diǎn)頭。我也開開心心的提交了代碼。
簽章無法顯示
以為這個(gè)小功能已經(jīng)開發(fā)完成,沒有多大的問題,直到有一天測試小姐姐找到我,
“你這pdf顯示有問題,當(dāng)pdf上有簽章時(shí),簽章無法顯示”
“what?”
簽章無法顯示,這個(gè)倒是沒有自測過,趕緊找測試要了鏈接來驗(yàn)證,經(jīng)過驗(yàn)證,簽章的顯示確實(shí)有問題。所謂簽章,即在pdf上加蓋公章或者簽名。如下圖
(來源網(wǎng)絡(luò))

簽章是屬于后期添加在pdf上,對于簽章的加載,簡單的js是無法加載成功的。
那該如何處理?
其實(shí)有個(gè)非常強(qiáng)大的第三方庫pdf.js[2]已經(jīng)幫我們處理好了,pdf.js可通過pdf文件的地址或pdf數(shù)據(jù)流獲取pdf,具體實(shí)現(xiàn)是調(diào)用接口函數(shù) PDFJs.getDocument(url/buffer)將pdf載入html,通過canvas處理, 然后渲染pdf文件,當(dāng)然也能夠顯示出簽章。
只不過它的使用有點(diǎn)麻煩,需要先將pdf.js下載出來,下載地址[3] ,copy到Android項(xiàng)目中assert文件夾中,

最后加載方式還是和上方一樣使用webview來加載。缺點(diǎn)就是包體積增大。
當(dāng)我們使用pdf.js默認(rèn)加載pdf時(shí),會發(fā)現(xiàn)效果圖的上方出現(xiàn)了多余的控制按鈕,比如下圖:

但是在UI設(shè)計(jì)圖中,是沒有包含這些控制按鈕的,如果就這么提交,估計(jì)不一會UI小姐姐就來找我了。
那該如何處理?
其實(shí)在本篇一開始使用的方式中,加載完成pdf是沒有這些控制按鈕的,那么問題來了,我們是不是可以將第一種方式與pdf.js相結(jié)合,來進(jìn)行加載?
pdf.js主要包含兩個(gè)核心庫文件,一個(gè)pdf.js和一個(gè)pdf.worker.js,一個(gè)負(fù)責(zé)API解析,一個(gè)負(fù)責(zé)核心解析。如果需要與第一種方式結(jié)合,我們就將pdf.js、pdf.worker.js以及pdf.sandbox.js三個(gè)文件copy出來,放到assert中。
在html中的script標(biāo)簽中添加對pdf.js、pdf.worker.js等的引用,
修改index.js文件
var?url?=?location.search.substring(1);
function?createPage()?{
????var?div?=?document.createElement("canvas");
????document.body.appendChild(div);
????return?div;
}
alert(url);
function?renderPage(num)?{
????pdfDoc.getPage(num).then(function?(page)?{
????????var?viewport?=?page.getViewport({?scale:?2.0?});
????????var?canvas?=?createPage();
????????var?ctx?=?canvas.getContext('2d');
????????canvas.height?=?viewport.height;
????????canvas.width?=?viewport.width;
????????page.render({
????????????canvasContext:?ctx,
????????????viewport:?viewport
????????}).promise.then(()?=>?{});
????});
}
pdfjsLib.getDocument(url).promise.then(function?(pdf)?{
????pdfDoc?=?pdf;
????for?(var?i?=?1;?i?<=?pdfDoc.numPages;?i++)?{
????????renderPage(i)
????}
});
可以看到運(yùn)行成功后,簽章成功展示且多余的控制按鈕也不會顯示,這里效果圖就不展示了。
我又開開心心的提交了代碼。
中文字符顯示不全
又過了一段時(shí)間,我正愉快的敲著代碼,這時(shí)候測試小姐姐又找到了我,
“這邊pdf顯示有點(diǎn)問題,一些文字、字符顯示不全,出現(xiàn)缺少字符的現(xiàn)象”
“what?”
我趕緊重現(xiàn)驗(yàn)證下,當(dāng)pdf上有多種字體時(shí),會有概率出現(xiàn)字符顯示不全的現(xiàn)象。查了查,當(dāng)運(yùn)行加載此類pdf時(shí),在控制臺上會出現(xiàn)了一些警告信息。

“Error during font loading”
是因?yàn)樵诮馕鰌df時(shí),默認(rèn)的字體庫已經(jīng)不能覆蓋多種字體,也就無法將所有字體顯示完全。
那如何處理?
默認(rèn)字體庫無法滿足,那就添加新的字體庫,
在pdf.js文件中添加cMapUrl = "cdn.jsdelivr.net/npm/pdfjs-d…[4]" ,
params.rangeChunkSize?=?params.rangeChunkSize?||?DEFAULT_RANGE_CHUNK_SIZE;
params.CMapReaderFactory?=?params.CMapReaderFactory?||?DefaultCMapReaderFactory;
params.ignoreErrors?=?params.stopAtErrors?!==?true;
params.fontExtraProperties?=?params.fontExtraProperties?===?true;
params.pdfBug?=?params.pdfBug?===?true;
params.enableXfa?=?params.enableXfa?===?true;
params.cMapPacked?=?true
params.cMapUrl?=?"https://cdn.jsdelivr.net/npm/[email protected]/cmaps/"
ok,運(yùn)行看看,中文已顯示完全。
以上,webview加載pdf的問題基本已經(jīng)解決。針對webview加載pdf的方案,主要解決問題如下:
雙指縮放; 簽章無法顯示; 存在多余控制按鈕; 中文字符顯示不全。
這幾個(gè)是加載pdf中最主要的問題,其他的小問題都好解決。
全部代碼已放置在github:pdf-webview[5]
關(guān)于本文
作者:付十一?
https://juejin.cn/post/7017840637450043422
參考資料
https://www.gjtool.cn/pdfh5/git.pdf%EF%BC%89
[2]https://github.com/mozilla/pdf.js
[3]https://mozilla.github.io/pdf.js/
[4]https://cdn.jsdelivr.net/npm/[email protected]/cmaps/
[5]https://github.com/fuusy/PdfWebview
我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。
???“分享、點(diǎn)贊、在看” 支持一波??
