之前碰到一個需求,需要在手機 APP 中顯示 pdf 文件。經(jīng)過調(diào)研發(fā)現(xiàn),在電腦上的瀏覽器如 chrome、safari等,可以直接顯示 pdf 文件。由此聯(lián)想到,在 APP 中能否通過瀏覽器來加載顯示 pdf 文件呢?最后經(jīng)過測試發(fā)現(xiàn):
- iOS 的 WebView 可以直接加載 pdf 文件并顯示出來;
- Android 的 WebView 不支持;
- 在 iOS 中有電子簽名的 pdf 文件,關(guān)于有電子簽名的地方無法顯示;
我們需要顯示的 pdf 文件基本上都是電子合同相關(guān)的 pdf 文件,也就是說基本上都包含了電子簽名,典型的如電子發(fā)票上的公章。那么怎么實現(xiàn)該功能呢?首先我們排除掉采用原生解析 pdf 文件的方式,網(wǎng)上找了下解析 pdf 文件的第三方開源庫,這些庫都很大,動則 20M 以上,并且穩(wěn)定性也不保障,顯然對 APP 來說是不劃算的。
最后,我們采用了 pdf.js 開源庫,通過 WebView 的方式來顯示 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.運行本地 demo
npm install
gulp server
構(gòu)建成功后,瀏覽器打開地址:http://localhost:8888/web/viewer.html,可以看到里面提供的 demo。
4.構(gòu)建 pdf.js 文件
//通用構(gòu)建
gulp generic
//混淆壓縮
gulp minified
我這里采用的是 gulp minified 的方式來構(gòu)建,構(gòu)建成功后,在 build/minified/ 目錄下會輸出結(jié)果,如下所示:

圖中紅線標注的 viewer.html 就是最終我們用來加載顯示 pdf 文件的入口 html 文件。
2. 如何在 WebView 中加載 pdf 文件
將前面構(gòu)建好的 pdf.js 相關(guān)文件拷貝到應用程序里,例如 Android 則拷貝到 assets 目錄中,如下圖所示:

采用 WebView 來加載本地網(wǎng)頁:
// pdf 文件的 url 地址,可以是本地文件,也可以是網(wǎng)絡文件
String pdfUrl = "......";
//很重要,允許 js 執(zhí)行
settings.setJavaScriptEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAllowFileAccess(true);
//很重要,設置允許跨域訪問
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
webView.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + Uri.encode(pdfUrl));
以上是 Android 中的例子,有幾點很重要,必須要配置好:
- 必須允許 js 能夠執(zhí)行;
- WebView 必須設置成能夠跨域訪問;
- pdf 文件地址可以是以 file:// 開頭的本地文件,也可以是以 http:// 開頭的網(wǎng)絡文件;
- file 參數(shù)值必須 uri encode;
iOS 的配置與此相似,這里不贅述。但是運行后發(fā)現(xiàn),網(wǎng)頁會報錯,無法顯示 pdf 文件,這時候我們需要修改源碼,這是因為源碼里限制了 pdf 文件地址必須是同源的。
打開源碼目錄下面的 web/app.js 文件,找到如下代碼,將之注釋掉:
if (origin !== viewerOrigin && protocol !== 'blob:') {
throw new Error('file origin does not match viewer\'s');
}
這句代碼對 pdf 文件地址來源做了限制,如果涉及到跨域訪問,就會直接拋出異常。代碼注釋掉之后,重新 build 后再運行,應該就可以正常加載顯示 pdf 文件了。
3. 如何顯示電子簽章
這樣 build 出的 pdf.js ,是無法顯示電子簽章的,要顯示電子簽章,還需要對源碼做出修改,找到源碼目錄下的 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)建之后,就可以通過 WebView 正常加載顯示 pdf 文件了。
這種方式對 iOS、Android 都是適用的,并且能夠顯示 pdf 文件中的電子簽名。需要注意的是,最終你需要把構(gòu)建文件中的一些不必要的文件給刪除掉,例如 .map 文件、.pdf 文件,這些都是多余的,最終總的文件大小約 4M 多的樣子。