Android应用中预览pdf

前言

我前面写过一篇文章:系统下载管理器下载pdf。下载完弹出选择打开方式弹框。
但是我需要的是在应用内预览,所以这种方式pass。

思考

IOS的同事使用WebView直接加载PDF的Url就实现了这个功能。因为IOS的WebView自带浏览器组件,算是个移动版的Safari。
Android的WebView和IOS的实现不一样,不能直接通过远程文件Url实现预览。但是可以另辟蹊径:使用如谷歌文档服务或者mozilla的文档服务(操作Url,拼接字符串)
直接使用一些第三方的自定义控件,实现方式基本是将pdf下载到本地,解析之后显示出来。这里列举几个:AndroidPdfViewerPdfViewPager。可能出现的问题也挺多的,而且会大大增加apk的体积。
个人极力推荐使用pdf.js,可以直接拉到本地,放到asset里面,然后使用WebView加载。但是会造成apk体积增大5M,跟其他第三方控件相比,已经很小了;还可以让前端的同事把库部署到服务器或者cdn,这样的话本地就只需要写一个h5和js文件,指向cdn地址,最后通过WebView加载。

1. Google PDF Viewer

讲实话,谷歌的文档服务我认为是几种实现方式中最优雅的。不过,需要科学上网(翻墙),最终放弃了这种方式。

重点是这个要拼接的url:https://drive.google.com/viewerng/viewer?embedded=true&url=

WebView webview = (WebView) findViewById(R.id.webview);
webview.getSettings().setJavaScriptEnabled(true); 
String pdf = "http://www.adobe.com/devnet/acrobat/pdfs/pdf_open_parameters.pdf";
webview.loadUrl("https://drive.google.com/viewerng/viewer?embedded=true&url=" + pdf);

2.mozilla PDF Viewer

挺好的,不需要翻墙。但是存在跨域问题。(由于浏览器的同源策略,即属于不同域的页面之间不能相互访问各自的页面内容)

重点是这个要拼接的url:http://mozilla.github.io/pdf.js/web/viewer.html?file=
还有一个: http://mozilla.github.io/pdf.js/es5/web/viewer.html?file=

//先设置WebView
webview.getSettings().setJavaScriptEnabled(true);
webview .getSettings().setAllowFileAccess(true);
webview .getSettings().setAllowFileAccessFromFileURLs(true);
//能否访问来自于任何源的文件标识的URL
webview .getSettings().setAllowUniversalAccessFromFileURLs(true);

webview .loadUrl("http://mozilla.github.io/pdf.js/web/viewer.html?file=" + pdfUrl);

可能出现问题,原因多指向库里面的js文件。

No ‘Access-Control-Allow-Origin’ header is present on the requested resource.(所请求的资源上没有“ Access-Control-Allow-Origin”标头。)

chromium: [INFO:CONSOLE(0)] “Uncaught (in promise) UnknownErrorException: The browser/environment lacks native support for critical functionality used by the PDF.js library.please use an ES5-compatible build instead.”(浏览器/环境缺少对PDF.js库使用的关键功能的本机支持。请改用与ES5兼容的版本。)

mozilla PDF Viewer整到项目里

pdfjs下载地址,我选择的ES5版本的稳定版。当然你也可以去pdf.js直接拉master分支或者选择某个TAG版本。不过要注意,下载下的资源build文件夹里的东西可不能少。如果没有请选择其他版本。
下载之后放到asset文件下
在这里插入图片描述

//先设置WebView
webview.getSettings().setJavaScriptEnabled(true);
webview .getSettings().setAllowFileAccess(true);
webview .getSettings().setAllowFileAccessFromFileURLs(true);
//能否访问来自于任何源的文件标识的URL
webview .getSettings().setAllowUniversalAccessFromFileURLs(true);
webview .loadUrl("file:///android_asset/pdfjs/web/viewer.html?file="+pdfUrl);
小插曲:关于跨域检查

查看viewer.js发现有一个validateFileURL变量。

var validateFileURL;
{
  var HOSTED_VIEWER_ORIGINS = ['null', 'http://mozilla.github.io', 'https://mozilla.github.io'];

  validateFileURL = function validateFileURL(file) {
    if (file === undefined) {
      return;
    }

    try {
      var viewerOrigin = new URL(window.location.href).origin || 'null';

      if (HOSTED_VIEWER_ORIGINS.includes(viewerOrigin)) {
        return;
      }

      var _ref8 = new URL(file, window.location.href),
          origin = _ref8.origin,
          protocol = _ref8.protocol;

      if (origin !== viewerOrigin && protocol !== 'blob:') {
        throw new Error('file origin does not match viewer\'s');
      }
    } catch (ex) {
      var message = ex && ex.message;
      PDFViewerApplication.l10n.get('loading_error', null, 'An error occurred while loading the PDF.').then(function (loadingErrorMessage) {
        PDFViewerApplication.error(loadingErrorMessage, {
          message: message
        });
      });
      throw ex;
    }
  };
}

  • 先看HOSTED_VIEWER_ORIGINS这个数组,如果数组里有viewerOrigin,直接返回,意味着这次校验通过。所以我们可以把我们的地址加到这个数组里,就不会执行后面的检查了。

往下看,当前源与目标源不一样同时没有使用伪协议blob的时候会抛出一个错误:

if (origin !== viewerOrigin && protocol !== 'blob:') {
   throw new Error('file origin does not match viewer\'s');
}
  • 干脆不让他抛出错误得了,注释掉这行throw的代码。
  • 或者直接不调用validateFileURL方法。(我就是这么干的)
简单写了个Demo

地址:PDFPreViewDemo
在这里插入图片描述
如果想修改上图的样式,具体就是viewer.html这个文件。我为了隐藏掉顶上的toobar,用chrome浏览器打开viewer.html,摁【F12】后找到了toolbar的div,设置了属性:style=“display: none;”

实现手势放大缩小

直接修改html文件的属性,查看viewer.html文件中有一行:

<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">

把最大缩放规模改成3.0,并添加属性user-scalable=yes:

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=3.0,user-scalable=yes">

最后确保webview里有这3句设置:

// 可以缩放
webview.getSettings().setSupportZoom(true);
// 显示放大缩小
webview.getSettings().setBuiltInZoomControls(true);
//设置此属性,可任意比例缩放
webview.getSettings().setUseWideViewPort(true);

mozilla PDF Viewer部署到服务器或者cdn

本来引入到项目里只会造成apk大小变大5M,如果把库放到服务器的话,可能这5M都省了。只需要写2个文件,然后里面的变量指向服务器的资源就行了。

  1. 首先写一个预览的index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
    <title>Document</title>
    <style type="text/css">
        canvas {
            width: 100%;
            height: 100%;
            border: 1px solid black;
        }
    </style>
    <script src="此处为配置到服务器的地址/build/pdf.min.js"></script>
    <script type="text/javascript" src="index.js"></script>
</head>
<body>
</body>
</html>

  1. 再写一个index.js
var url = location.search.substring(1);

PDFJS.cMapUrl = '此处为服务器地址/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)
    }
});

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值