AndroidAndroid收藏集Android开发之路

Android 预览 PDF 技术方案总结

2018-12-27  本文已影响160人  Little丶Jerry

一、借助第三方应用

Intent viewIntent = new Intent();

viewIntent.setAction(Intent.ACTION_VIEW);

viewIntent.setDataAndType(pdfUri, "application/pdf");

viewIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

startActivity(viewIntent);

二、Google 文档服务

webView.loadUrl("http://docs.google.com/gviewembedded=true&url=" + pdfUrl);

三、Android PdfViewer 框架

该框架是目前最流行,最稳定,速度最快的框架,使用起来非常简单,可以访问 GitHub 主页查看使用方法:

https://github.com/barteksc/AndroidPdfViewer

四、PDF.js

PDF.js 官网

PDF.js GitHub

如果说 Google Docs 有高墙限制,那么 PDF.js 则是墙内人的福音。

方式一:使用 mozilla 部署在 github pages 上的 Viewer

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

方式二:下载 PDF.js 放到 assets 目录下

如果 pdf 文件不能跨域访问的话可以使用这种方式,先把文件下载到本地然后传入本地文件路径:

pdfWebView.loadUrl("file:///android_asset/pdfjs/web/viewer.html?file=" + pdfUri);

方式三:自定义预览界面,PDF.js 使用 CDN 的方式导入

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="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>
2. 实现预览 index.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)
    }
});
3. 示例代码
WebSettings webSettings = pdfWebView.getSettings();

webSettings.setJavaScriptEnabled(true);
webSettings.setAllowFileAccess(true);
webSettings.setAllowFileAccessFromFileURLs(true);
webSettings.setAllowUniversalAccessFromFileURLs(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setSupportZoom(true);
webSettings.setDisplayZoomControls(true);

pdfWebView.loadUrl("file:///android_asset/index.html?" + pdfUri);

4.1 遇到的问题

可以在 index.js 文件中设置 scale 系数来解决。

var viewport = page.getViewport(2.0);//设置为2.0

可以在 index.js 文件中设置 cMapUrl 和 cMapPacked 来解决。

PDFJS.cMapUrl = 'https://unpkg.com/pdfjs-dist@1.9.426/cmaps/';
PDFJS.cMapPacked = true;

4.2 PDF.js 方案总结

2018-12-27 17:42:53.771 12498-12498/com.example.jerry.mypdfapplication A/chromium: 
[FATAL:aw_browser_terminator.cc(79)] Render process (12623)'s crash 
wasn't handled by all associated  webviews, triggering application crash.

4.3 效果图

mozilla viewer 自定义 Viewer

五、PDFRenderer

PdfRenderer API

这个类可以用来渲染 PDF 文档,它是将 PDF 里的每一页渲染成一张 Bitmap,以此显示出来。该类是线程不安全的。PdfRenderer 中核心代码是用的是 native 方法,所以很难将 PdfRenderer 从 SDK 中抽取出来用。

5.1 简单示例代码

 // 首先创建一个 PdfRenderer
 PdfRenderer renderer = new PdfRenderer(getSeekableFileDescriptor());

 // 渲染全部页面
 final int pageCount = renderer.getPageCount();
 for (int i = 0; i < pageCount; i++) {
     Page page = renderer.openPage(i);

     // 将一页的 PDF 渲染到一个 Bitmap 上,Bitmap 必须是 ARGB,不可以是 RGB
     Bitmap mBitmap = Bitmap.createBitmap(currentPage.getWidth(), currentPage.getHeight(),
                    Bitmap.Config.ARGB_8888);

     page.render(mBitmap, null, null, Page.RENDER_MODE_FOR_DISPLAY);

     // 显示该 Bitmap
     imageView.setImageBitmap(mBitmap);

     // 注意:每次渲染完成之后都要关闭 page
     page.close();
 }

 // 注意:渲染完成之后要关闭 renderer
 renderer.close();

5.2 Demo 代码

public class PdfRendererActivity extends AppCompatActivity {

    private static final String TAG = "MyPDF";

    private static final int REQUEST_PDF_OPEN = 1;

    private Button getButton;

    private Button openButton;

    private Button previousButton;

    private Button nextButton;

    private ImageView imageView;

    private String pdfUri;

    private PdfRenderer.Page currentPage;

    private PdfRenderer pdfRenderer;

    private ParcelFileDescriptor parcelFileDescriptor;

    private int pageCount;

    private int currentIndex;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pdfrenderer);
        initView();
    }

    private void initView() {

        getButton = findViewById(R.id.button_get);

        openButton = findViewById(R.id.button_open);

        imageView = findViewById(R.id.imageview_pdf_page);

        previousButton = findViewById(R.id.button_previous);

        nextButton = findViewById(R.id.button_next);

        getButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                selectPdf();
            }
        });

        openButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                openPdf();
            }
        });

        previousButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (currentIndex == 0) {
                    return;
                }
                currentIndex--;

                currentPage = pdfRenderer.openPage(currentIndex);

                Bitmap bitmap = Bitmap.createBitmap(currentPage.getWidth(), currentPage.getHeight(),
                        Bitmap.Config.ARGB_8888);

                // say we render for showing on the screen
                currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

                // do stuff with the bitmap
                imageView.setImageBitmap(bitmap);
                // close the page
                currentPage.close();
            }
        });

        nextButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                if (currentIndex == pageCount - 1) {
                    return;
                }

                currentIndex++;

                currentPage = pdfRenderer.openPage(currentIndex);

                Bitmap bitmap = Bitmap.createBitmap(currentPage.getWidth(), currentPage.getHeight(),
                        Bitmap.Config.ARGB_8888);

                // say we render for showing on the screen
                currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

                // do stuff with the bitmap
                imageView.setImageBitmap(bitmap);
                // close the page
                currentPage.close();
            }
        });
    }

    private void selectPdf() {

        Intent getIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

        getIntent.setType("*/*");

        getIntent.addCategory(Intent.CATEGORY_OPENABLE);

        PdfRendererActivity.this.startActivityForResult(getIntent, REQUEST_PDF_OPEN);
    }

    private void openPdf() {

        try {
            currentIndex = 0;
            // create a new renderer
            pdfRenderer = new PdfRenderer(getSeekableFileDescriptor());

            // let us just render all pages
            pageCount = pdfRenderer.getPageCount();

            currentPage = pdfRenderer.openPage(currentIndex);

            Bitmap bitmap = Bitmap.createBitmap(currentPage.getWidth(), currentPage.getHeight(),
                    Bitmap.Config.ARGB_8888);

            // say we render for showing on the screen
            currentPage.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);

            // do stuff with the bitmap
            imageView.setImageBitmap(bitmap);
            // close the page
            currentPage.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private ParcelFileDescriptor getSeekableFileDescriptor() {

        try {
            parcelFileDescriptor = getApplicationContext().getContentResolver().openFileDescriptor(Uri.parse(pdfUri), "r");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return parcelFileDescriptor;
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {

        if (requestCode == REQUEST_PDF_OPEN && resultCode == RESULT_OK) {
            pdfUri = data.getDataString();
            Log.d(TAG, "onActivityResult: " + Uri.decode(pdfUri));
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // close the renderer
        if (null != pdfRenderer) {
            pdfRenderer.close();
        }
        if (null != parcelFileDescriptor) {
            try {
                parcelFileDescriptor.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5.3 效果图

第一页 第二页
上一篇 下一篇

猜你喜欢

热点阅读