mupdf-android-viewer 设计与实现浅析
目前在 Android 應(yīng)用開發(fā)中,可用的 PDF 文檔展示的開源項目好幾個,最為方便的是 AndroidPdfViewer,它基于 PdfiumAndroid 開發(fā)而來,而后者則是由 AOSP 中的 pdfium 封轉(zhuǎn)而來。另外一個 PDF 文檔顯示的開源項目 mupdf 也非常強大。本文簡單分析 MuPDF 庫的 Android 封裝。
MuPDF 是一個用于查看和改變 PDF,XPS 和 E-book 文檔的開源軟件框架。它為多個不同的平臺提供了查看器,命令行工具,以及軟件庫以用于構(gòu)建工具和應(yīng)用程序。
mupdf-android-viewer 是 MuPDF 為 Android 平臺提供的查看器,它的代碼可以通過 Git 下載得到:
$ git clone git://git.ghostscript.com/mupdf-android-viewer.gitmupdf-android-viewer 工程的目錄結(jié)構(gòu)很簡單:
mupdf-android-viewer$ tree . ├── app │?? ├── build.gradle │?? └── src │?? └── main │?? ├── AndroidManifest.xml │?? ├── java │?? │?? └── com │?? │?? └── artifex │?? │?? └── mupdf │?? │?? └── viewer │?? │?? └── app │?? │?? └── LibraryActivity.java │?? └── res │?? ├── drawable │?? │?? └── ic_mupdf.xml │?? └── values │?? └── strings.xml ├── build.gradle ├── COPYING ├── gradle │?? └── wrapper │?? ├── gradle-wrapper.jar │?? └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jni ├── lib │?? ├── build.gradle │?? └── src │?? └── main │?? ├── AndroidManifest.xml │?? ├── java │?? │?? └── com │?? │?? └── artifex │?? │?? └── mupdf │?? │?? └── viewer │?? │?? ├── CancellableAsyncTask.java │?? │?? ├── CancellableTaskDefinition.java │?? │?? ├── DocumentActivity.java │?? │?? ├── MuPDFCancellableTaskDefinition.java │?? │?? ├── MuPDFCore.java │?? │?? ├── OutlineActivity.java │?? │?? ├── PageAdapter.java │?? │?? ├── PageView.java │?? │?? ├── ReaderView.java │?? │?? ├── SearchTask.java │?? │?? ├── SearchTaskResult.java │?? │?? └── Stepper.java │?? └── res │?? ├── drawable │?? │?? ├── button.xml │?? │?? ├── ic_chevron_left_white_24dp.xml │?? │?? ├── ic_chevron_right_white_24dp.xml │?? │?? ├── ic_close_white_24dp.xml │?? │?? ├── ic_link_white_24dp.xml │?? │?? ├── ic_search_white_24dp.xml │?? │?? ├── ic_toc_white_24dp.xml │?? │?? ├── page_indicator.xml │?? │?? ├── seek_line.xml │?? │?? └── seek_thumb.xml │?? ├── layout │?? │?? └── document_activity.xml │?? └── values │?? ├── colors.xml │?? └── strings.xml ├── Makefile ├── README └── settings.gradle27 directories, 41 files其中 settings.gradle 內(nèi)容如下:
include ':jni' include ':lib' include ':app'mupdf-android-viewer 主要由三個模塊組成,分別為 app,lib 和 jni,app 是 Android MuPDF 查看器的應(yīng)用程序主工程,jni 是 MuPDF 的核心庫,lib 是基于 MuPDF 核心庫封裝出來的用于顯示 PDF 文檔的 UI 控件庫。Android MuPDF 查看器應(yīng)用程序基于 lib 庫構(gòu)建而成。
MuPDF 的 核心庫可以有兩個來源,一是遠程 Maven 倉庫,其中 Maven 倉庫的地址為 http://maven.ghostscript.com/,Android 核心庫的坐標(biāo)為 com.artifex.mupdf:fitz:1.13.0,這可以從 mupdf-android-viewer/build.gradle 和 mupdf-android-viewer/lib/build.gradle 文件中看出來;二是由源碼編譯而來,編譯的方法如 為 Android 編譯 MuPDF 查看器 一文所示,MuPDF 的 核心庫的源碼下載之后位于 mupdf-android-viewer/jni 目錄下。
MuPDF 的 Android 核心庫又由三個部分組成,分別為它所依賴的第三方庫,MuPDF 本地層庫和 MuPDF 本地層庫的 JNI 封裝。其中 MuPDF 本地層庫的 JNI 封裝位于 mupdf-android-viewer/jni/libmupdf/platform/java,MuPDF 本地層庫位于 mupdf-android-viewer/jni/libmupdf/source,依賴的第三方庫則位于 mupdf-android-viewer/jni/libmupdf/thirdparty。
MuPDF Android 查看器的整體組件結(jié)構(gòu)如下圖所示:
MuPDF UI 控件庫的代碼位于 mupdf-android-viewer/lib,它連接了上層的 Android 應(yīng)用程序和下層的 MuPDF 核心庫,其中 MuPDFCore 類封裝了 MuPDF 核心庫用 JNI 封裝的底層 MuPDF 庫的 Java 接口;ReaderView、PageAdapter、PageView、SearchTask 和 SearchTaskResult 基于 MuPDFCore 類實現(xiàn) Android 應(yīng)用程序的 UI 組件,以方便在 Android 應(yīng)用中查看 PDF 文件;DocumentActivity 和 OutlineActivity 是兩個 Activity,這兩個頁面分別用于顯示 PDF 文檔及 PDF 文檔的目錄。
MuPDFCore 類的定義如下:
package com.artifex.mupdf.viewer;import com.artifex.mupdf.fitz.Cookie; import com.artifex.mupdf.fitz.Document; import com.artifex.mupdf.fitz.Outline; import com.artifex.mupdf.fitz.Page; import com.artifex.mupdf.fitz.Link; import com.artifex.mupdf.fitz.DisplayList; import com.artifex.mupdf.fitz.Rect; import com.artifex.mupdf.fitz.RectI; import com.artifex.mupdf.fitz.Matrix; import com.artifex.mupdf.fitz.android.AndroidDrawDevice;import android.content.Context; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF;import java.util.ArrayList;public class MuPDFCore {private int resolution;private Document doc;private Outline[] outline;private int pageCount = -1;private int currentPage;private Page page;private float pageWidth;private float pageHeight;private DisplayList displayList;public MuPDFCore(String filename) {doc = Document.openDocument(filename);pageCount = doc.countPages();resolution = 160;currentPage = -1;}public MuPDFCore(byte buffer[], String magic) {doc = Document.openDocument(buffer, magic);pageCount = doc.countPages();resolution = 160;currentPage = -1;}public String getTitle() {return doc.getMetaData(Document.META_INFO_TITLE);}public int countPages() {return pageCount;}private synchronized void gotoPage(int pageNum) {/* TODO: page cache */if (pageNum > pageCount-1)pageNum = pageCount-1;else if (pageNum < 0)pageNum = 0;if (pageNum != currentPage) {currentPage = pageNum;if (page != null)page.destroy();page = null;if (displayList != null)displayList.destroy();displayList = null;page = doc.loadPage(pageNum);Rect b = page.getBounds();pageWidth = b.x1 - b.x0;pageHeight = b.y1 - b.y0;}}public synchronized PointF getPageSize(int pageNum) {gotoPage(pageNum);return new PointF(pageWidth, pageHeight);}public synchronized void onDestroy() {if (displayList != null)displayList.destroy();displayList = null;if (page != null)page.destroy();page = null;if (doc != null)doc.destroy();doc = null;}public synchronized void drawPage(Bitmap bm, int pageNum,int pageW, int pageH,int patchX, int patchY,int patchW, int patchH,Cookie cookie) {gotoPage(pageNum);if (displayList == null)displayList = page.toDisplayList(false);float zoom = resolution / 72;Matrix ctm = new Matrix(zoom, zoom);RectI bbox = new RectI(page.getBounds().transform(ctm));float xscale = (float)pageW / (float)(bbox.x1-bbox.x0);float yscale = (float)pageH / (float)(bbox.y1-bbox.y0);ctm.scale(xscale, yscale);AndroidDrawDevice dev = new AndroidDrawDevice(bm, patchX, patchY);displayList.run(dev, ctm, cookie);dev.destroy();}public synchronized void updatePage(Bitmap bm, int pageNum,int pageW, int pageH,int patchX, int patchY,int patchW, int patchH,Cookie cookie) {drawPage(bm, pageNum, pageW, pageH, patchX, patchY, patchW, patchH, cookie);}public synchronized Link[] getPageLinks(int pageNum) {gotoPage(pageNum);return page.getLinks();}public synchronized RectF[] searchPage(int pageNum, String text) {gotoPage(pageNum);Rect[] rs = page.search(text);RectF[] rfs = new RectF[rs.length];for (int i=0; i < rs.length; ++i)rfs[i] = new RectF(rs[i].x0, rs[i].y0, rs[i].x1, rs[i].y1);return rfs;}public synchronized boolean hasOutline() {if (outline == null)outline = doc.loadOutline();return outline != null;}private void flattenOutlineNodes(ArrayList<OutlineActivity.Item> result, Outline list[], String indent) {for (Outline node : list) {if (node.title != null)result.add(new OutlineActivity.Item(indent + node.title, node.page));if (node.down != null)flattenOutlineNodes(result, node.down, indent + " ");}}public synchronized ArrayList<OutlineActivity.Item> getOutline() {ArrayList<OutlineActivity.Item> result = new ArrayList<OutlineActivity.Item>();flattenOutlineNodes(result, outline, "");return result;}public synchronized boolean needsPassword() {return doc.needsPassword();}public synchronized boolean authenticatePassword(String password) {return doc.authenticatePassword(password);} }由 MuPDFCore 類的實現(xiàn)不難看出,MuPDF 核心庫在查看 PDF 文件方面所提供的功能如下:
1. :解析 PDF 文檔。這主要是在 MuPDFCore 類的構(gòu)造函數(shù)里完成的,通過 com.artifex.mupdf.fitz.Document 類的 openDocument() 方法執(zhí)行。PDF 文檔解析完成之后,可以借助于 com.artifex.mupdf.fitz.Document 的對象獲得 PDF 文檔的標(biāo)題,頁數(shù)等信息。
2. :跳轉(zhuǎn)到 PDF 文檔的特定頁上。這主要通過 gotoPage() 方法完成。這將為目標(biāo)頁創(chuàng)建 com.artifex.mupdf.fitz.Page 對象,并可以獲得這一頁繪制之后的尺寸。
3. :將 PDF 文檔特定頁的內(nèi)容繪制到 Bitmap 上。繪制通過 com.artifex.mupdf.fitz.Page 對象創(chuàng)建的 com.artifex.mupdf.fitz.DisplayList 對象完成。在繪制的時候,還會根據(jù) Bitmap 的尺寸和 PDF 目標(biāo)文檔的尺寸做一定的放縮。
4. :在 PDF 文檔中搜索特定的字符串。在 PDF 文檔的特定頁執(zhí)行的搜索操作以找到的文本在該頁中占據(jù)的矩形框的數(shù)組的形式返回。
5. :獲得 PDF 文檔的目錄信息。這主要是通過 hasOutline() 和 getOutline() 方法實現(xiàn)的。其中目錄項由 OutlineActivity.Item 描述:
OutlineActivity.Item 描述了特定目錄項的標(biāo)題及該目錄項在 PDF 文檔中的頁碼。
6. :判斷查看 PDF 文檔是否需要密碼,以及配置認證密碼。這主要是通過 needsPassword() 和 authenticatePassword(String password) 方法實現(xiàn)的。
MuPDF 核心庫提供的最主要的功能是解析 PDF 文檔,跳轉(zhuǎn)到特定頁,并將該頁的實際內(nèi)容繪制到 Bitmap 上。基于 MuPDF 核心庫實現(xiàn) Android 應(yīng)用程序的 UI 控件,最主要要做的即是創(chuàng)建、管理并展示 PDF 文檔中各頁的 Bitmap 圖片。
總結(jié)
以上是生活随笔為你收集整理的mupdf-android-viewer 设计与实现浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 为 Android 编译 MuPDF 查
- 下一篇: [译]BitTorrent协议规范