避坑??!webview如何加載pdf ?

問題來源

問題一切的來源還是得從那天說起...

有一天產(chǎn)品小王拿著電腦興致勃勃的來到我的工位旁:“誒,小付,這里有個(gè)在線pdf預(yù)覽的功能,你看下能不能做?!?/p>

聽完我心中暗想:這還不簡(jiǎn)單,加載在線pdf不就和加載網(wǎng)頁(yè)一樣的,webview加上pdf鏈接,搞定!

這一想完,立即比了個(gè)OK:“沒問題,簡(jiǎn)單!”

說完立馬開干,新建項(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)頁(yè)一點(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ù),但在國(guó)內(nèi)是無法訪問的,pass;比如下載后再進(jìn)行加載,但當(dāng)pdf體積大且網(wǎng)絡(luò)不好時(shí),下載就會(huì)出現(xiàn)問題,又pass;

方式有多種,第三方的輪子也有很多,但適合自己的開發(fā)需求,以及滿足UI設(shè)計(jì),則就需要進(jìn)行二次改造了。

經(jīng)過多方對(duì)比,使用webview加載pdf的方案更符合大多數(shù)的場(chǎng)景。

以下就會(huì)從webview加載pdf的方案出發(fā),描述在開發(fā)時(shí)所涉及到的問題點(diǎn)。

我的爬坑之旅開始了!

初步加載

webview加載pdf的初步設(shè)想是使用js的方式去渲染,

新建一個(gè)js

var url = location.search.substring(1);

PDFJS.cMapUrl = 'https://unpkg.com/pdfjs-dist@1.9.426/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

<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=4.0,user-scalable=no"/>
    <title>Document</title>
    <style type="text/css">
        canvas {
            width: 100%;
            height: 100%;
            border: 1px solid black;
        }
    </style>
    <script src="https://unpkg.com/pdfjs-dist@1.9.426/build/pdf.min.js"></script>
    <script type="text/javascript" src="index.js"></script>
</head>
<body>
</body>
</html>

準(zhǔn)備好js和html后,使用webview對(duì)在線pdf(https://www.gjtool.cn/pdfh5/git.pdf) 進(jìn)行加載,

webView?.loadUrl("file:///android_asset/index.html?https://www.gjtool.cn/pdfh5/git.pdf");

運(yùn)行成功后,pdf也加載出來了。

[圖片上傳失敗...(image-d55aea-1634226795111)]

添加雙指縮放

好家伙,終于是加載出了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-scalemaximum-scale屬性,以及將user-scalable置為yes,

運(yùn)行成功后,成功對(duì)pdf進(jìn)行雙指縮放。

產(chǎn)品看了過后,點(diǎn)了點(diǎn)頭。我也開開心心的提交了代碼。

簽章無法顯示

以為這個(gè)小功能已經(jīng)開發(fā)完成,沒有多大的問題,直到有一天測(cè)試小姐姐找到我,

“你這pdf顯示有問題,當(dāng)pdf上有簽章時(shí),簽章無法顯示”

“what?”

簽章無法顯示,這個(gè)倒是沒有自測(cè)過,趕緊找測(cè)試要了鏈接來驗(yàn)證,經(jīng)過驗(yàn)證,簽章的顯示確實(shí)有問題。所謂簽章,即在pdf上加蓋公章或者簽名。如下圖

(來源網(wǎng)絡(luò))

[圖片上傳失敗...(image-bea130-1634226795111)]

簽章是屬于后期添加在pdf上,對(duì)于簽章的加載,簡(jiǎn)單的js是無法加載成功的。

那該如何處理?

其實(shí)有個(gè)非常強(qiáng)大的第三方庫(kù)pdf.js已經(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下載出來,下載地址 ,copy到Android項(xiàng)目中assert文件夾中,

[圖片上傳失敗...(image-cfe1af-1634226795111)]

最后加載方式還是和上方一樣使用webview來加載。缺點(diǎn)就是包體積增大。

當(dāng)我們使用pdf.js默認(rèn)加載pdf時(shí),會(huì)發(fā)現(xiàn)效果圖的上方出現(xiàn)了多余的控制按鈕,比如下圖:

[圖片上傳失敗...(image-3bc62c-1634226795111)]

但是在UI設(shè)計(jì)圖中,是沒有包含這些控制按鈕的,如果就這么提交,估計(jì)不一會(huì)UI小姐姐就來找我了。

那該如何處理?

其實(shí)在本篇一開始使用的方式中,加載完成pdf是沒有這些控制按鈕的,那么問題來了,我們是不是可以將第一種方式與pdf.js相結(jié)合,來進(jìn)行加載?

pdf.js主要包含兩個(gè)核心庫(kù)文件,一個(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)簽中添加對(duì)pdf.js、pdf.worker.js等的引用,

<script type="text/javascript" src="pdf.js"></script>
<script type="text/javascript" src="pdf.worker.js"></script>
<script type="text/javascript" src="pdf.sandbox.js"></script>
<script type="text/javascript" src="index.js"></script>

修改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)行成功后,簽章成功展示且多余的控制按鈕也不會(huì)顯示,這里效果圖就不展示了。

我又開開心心的提交了代碼。

中文字符顯示不全

又過了一段時(shí)間,我正愉快的敲著代碼,這時(shí)候測(cè)試小姐姐又找到了我,

“這邊pdf顯示有點(diǎn)問題,一些文字、字符顯示不全,出現(xiàn)缺少字符的現(xiàn)象”

“what?”

我趕緊重現(xiàn)驗(yàn)證下,當(dāng)pdf上有多種字體時(shí),會(huì)有概率出現(xiàn)字符顯示不全的現(xiàn)象。查了查,當(dāng)運(yùn)行加載此類pdf時(shí),在控制臺(tái)上會(huì)出現(xiàn)了一些警告信息。

[圖片上傳失敗...(image-5bbc07-1634226795111)]

“Error during font loading”

是因?yàn)樵诮馕鰌df時(shí),默認(rèn)的字體庫(kù)已經(jīng)不能覆蓋多種字體,也就無法將所有字體顯示完全。

那如何處理?

默認(rèn)字體庫(kù)無法滿足,那就添加新的字體庫(kù),

在pdf.js文件中添加cMapUrl = "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/" ,

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/pdfjs-dist@2.2.228/cmaps/"

ok,運(yùn)行看看,中文已顯示完全。

以上,webview加載pdf的問題基本已經(jīng)解決。針對(duì)webview加載pdf的方案,主要解決問題如下:

  • 雙指縮放;
  • 簽章無法顯示;
  • 存在多余控制按鈕;
  • 中文字符顯示不全。

這幾個(gè)是加載pdf中最主要的問題,其他的小問題都好解決。

全部代碼已放置在github:pdf-webview

推薦閱讀:

仿微信聊天炸“屎”效果??!誒,就是玩!

Compose版來啦!仿自如裸眼3D效果

「性能優(yōu)化系列」APP內(nèi)存優(yōu)化理論與實(shí)踐

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容