之前碰到一個(gè)需求,需要在手機(jī) APP 中顯示 pdf 文件滚婉。經(jīng)過(guò)調(diào)研發(fā)現(xiàn)图筹,在電腦上的瀏覽器如 chrome、safari等让腹,可以直接顯示 pdf 文件远剩。由此聯(lián)想到,在 APP 中能否通過(guò)瀏覽器來(lái)加載顯示 pdf 文件呢骇窍?最后經(jīng)過(guò)測(cè)試發(fā)現(xiàn):
- iOS 的 WebView 可以直接加載 pdf 文件并顯示出來(lái)瓜晤;
- Android 的 WebView 不支持;
- 在 iOS 中有電子簽名的 pdf 文件腹纳,關(guān)于有電子簽名的地方無(wú)法顯示痢掠;
我們需要顯示的 pdf 文件基本上都是電子合同相關(guān)的 pdf 文件,也就是說(shuō)基本上都包含了電子簽名只估,典型的如電子發(fā)票上的公章志群。那么怎么實(shí)現(xiàn)該功能呢?首先我們排除掉采用原生解析 pdf 文件的方式蛔钙,網(wǎng)上找了下解析 pdf 文件的第三方開(kāi)源庫(kù)锌云,這些庫(kù)都很大,動(dòng)則 20M 以上吁脱,并且穩(wěn)定性也不保障桑涎,顯然對(duì) APP 來(lái)說(shuō)是不劃算的彬向。
最后,我們采用了 pdf.js 開(kāi)源庫(kù)攻冷,通過(guò) WebView 的方式來(lái)顯示 pdf 文件娃胆。
1. 自己構(gòu)建 pdf.js
1.下載源碼到本地:
git clone https://github.com/mozilla/pdf.js.git
cd pdf.js
2.安裝 gulp 工具
npm install -g gulp-cli
3.運(yùn)行本地 demo
npm install
gulp server
構(gòu)建成功后,瀏覽器打開(kāi)地址:http://localhost:8888/web/viewer.html等曼,可以看到里面提供的 demo里烦。
4.構(gòu)建 pdf.js 文件
//通用構(gòu)建
gulp generic
//混淆壓縮
gulp minified
我這里采用的是 gulp minified
的方式來(lái)構(gòu)建,構(gòu)建成功后禁谦,在 build/minified/
目錄下會(huì)輸出結(jié)果胁黑,如下所示:
圖中紅線(xiàn)標(biāo)注的 viewer.html 就是最終我們用來(lái)加載顯示 pdf 文件的入口 html 文件。
2. 如何在 WebView 中加載 pdf 文件
將前面構(gòu)建好的 pdf.js 相關(guān)文件拷貝到應(yīng)用程序里州泊,例如 Android 則拷貝到 assets 目錄中丧蘸,如下圖所示:
采用 WebView 來(lái)加載本地網(wǎng)頁(yè):
// pdf 文件的 url 地址,可以是本地文件遥皂,也可以是網(wǎng)絡(luò)文件
String pdfUrl = "......";
//很重要力喷,允許 js 執(zhí)行
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
//很重要,設(shè)置允許跨域訪(fǎng)問(wèn)
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
webView.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + Uri.encode(pdfUrl));
以上是 Android 中的例子演训,有幾點(diǎn)很重要弟孟,必須要配置好:
- 必須允許 js 能夠執(zhí)行;
- WebView 必須設(shè)置成能夠跨域訪(fǎng)問(wèn)仇祭;
- pdf 文件地址可以是以 file:// 開(kāi)頭的本地文件披蕉,也可以是以 http:// 開(kāi)頭的網(wǎng)絡(luò)文件;
- file 參數(shù)值必須 uri encode乌奇;
iOS 的配置與此相似没讲,這里不贅述。但是運(yùn)行后發(fā)現(xiàn)礁苗,網(wǎng)頁(yè)會(huì)報(bào)錯(cuò)爬凑,無(wú)法顯示 pdf 文件,這時(shí)候我們需要修改源碼试伙,這是因?yàn)樵创a里限制了 pdf 文件地址必須是同源的嘁信。
打開(kāi)源碼目錄下面的 web/app.js 文件,找到如下代碼疏叨,將之注釋掉:
if (origin !== viewerOrigin && protocol !== 'blob:') {
throw new Error('file origin does not match viewer\'s');
}
這句代碼對(duì) pdf 文件地址來(lái)源做了限制潘靖,如果涉及到跨域訪(fǎng)問(wèn),就會(huì)直接拋出異常蚤蔓。代碼注釋掉之后卦溢,重新 build 后再運(yùn)行,應(yīng)該就可以正常加載顯示 pdf 文件了。
3. 如何顯示電子簽章
這樣 build 出的 pdf.js 单寂,是無(wú)法顯示電子簽章的贬芥,要顯示電子簽章,還需要對(duì)源碼做出修改宣决,找到源碼目錄下的 src/core/annotation.js 文件蘸劈,找到如下代碼:
// Hide signatures because we cannot validate them, and unset the fieldValue
// since it's (most likely) a `Dict` which is non-serializable and will thus
// cause errors when sending annotations to the main-thread (issue 10347).
if (data.fieldType === 'Sig') {
data.fieldValue = null;
this.setFlags(AnnotationFlag.HIDDEN);
}
同樣將這段代碼給注釋掉,看源碼可以知道這里是隱藏掉電子簽名了尊沸。最后再重新構(gòu)建之后威沫,就可以通過(guò) WebView 正常加載顯示 pdf 文件了。
這種方式對(duì) iOS椒丧、Android 都是適用的壹甥,并且能夠顯示 pdf 文件中的電子簽名救巷。需要注意的是壶熏,最終你需要把構(gòu)建文件中的一些不必要的文件給刪除掉,例如 .map 文件浦译、.pdf 文件棒假,這些都是多余的,最終總的文件大小約 4M 多的樣子精盅。