Android App 集成 MuPdf 实现 Pdf 阅读、编辑、标记等功能

By Long Luo

之前开发 Android电子书阅读器App 时,需要支持 Pdf 文件的阅读和标记功能,经过分析和测试不同实现方案,最终选择 MuPdf 方案。

Pdf解决方案

目前手机上实现 Pdf 文件的支持,有很多解决方案:

  1. Andorid原生自带的Pdf解决方案,主要提供两个类 PdfRendererPdfDocument ,但是 Lollipop 才有的类,PdfRenderer 中核心代码是用的 native 方法,所以没办法将 PdfRenderer 从 SDK 中抽取出来用,局限性大,不采用。

  2. 开源 AndroidPdfViewer ,完全使用 Java 实现,但问题在于太大,性能不及原生,所以放弃。

  3. MuPdf,一款轻量级的 pdf 框架,支持前面两者功能,同时如果是文本的 pdf 文档还支持搜索、标注等功能,使用 native C 代码实现,快,编译好的so库也只有10M大小。

  4. 调起手机中第三方支持 Pdf 阅读的应用;

  5. 通过 pdf.js 实现在线预览,需要调用网页,性能不及原生,放弃。

MuPdf

MuPdf 是一个轻量级 PDF、XPS 和 E-book 阅读器,支持全部平台。

源码下载地址:https://mupdf.com/downloads/archive/

官网提供了一个例子:MuPDF Android Viewer,下面我们来编译并实现。

MuPdf编译

下载源码:

1
$ git clone --recursive git://git.ghostscript.com/mupdf-android-viewer.git

安装Cygwin或者直接在Linux下使用:

1
2
mupdf-android-viewer$cd jni
mupdf-android-viewer/jni$make generate

会调用 make -j4 -C libmupdf generate 命令编译字体文件,然后在 libmupdf 目录下生成 generated 文件夹,里面主要是一些字体文件。

编译libmupdf_java.so

Linux 下 NDK 编译环境配置可以参考之前的文章 Linux下Android开发环境搭建指南

或者在 Windows 下配置好NDK编译,然后进入 ~/mupdf-android-viewer/jni/libmupdf 目录下,ndk-build 编译:

1
~\mupdf-android-viewer\jni\libmupdf>ndk-build -j8 APP_BUILD_SCRIPT=platform/java/Android.mk APP_PROJECT_PATH=build/android APP_PLATFORM=android-16 APP_OPTIM=debug APP_ABI=arm64-v8a

编译中出现下列错误:

1
2
3
process_begin: CreateProcess(... ...)
harfbuzz...
make (e=87): 参数错误。

原因是参数太长,解决方案是在 mupdf-android-viewer\jni\libmupdf\platform\java的:

在 Android.mk 文件中添加:LOCAL_SHORT_COMMANDS := true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Android makefile to be used with ndk-build.
#
# Run ndk-build with the following arguments:
# APP_BUILD_SCRIPT=platform/java/Android.mk (this file)
# APP_PROJECT_DIR=build/android (where you want the output)
# APP_PLATFORM=android-16
# APP_OPTIM=release (or debug)
# APP_ABI=all (or armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64)
#
# The top-level Makefile will invoke ndk-build with appropriate arguments
# if you run 'make android'.
#
# Use the MUPDF_EXTRA_CFLAGS, MUPDF_EXTRA_CPPFLAGS, MUPDF_EXTRA_LDFLAGS,
# and MUPDF_EXTRA_LDLIBS variables to add more compiler flags.
#
# LOCAL_C_INCLUDES paths are relative to the NDK root directory.
# LOCAL_SRC_FILES paths are relative to LOCAL_PATH.
#
# We make sure to use absolute paths everywhere, so this makefile works
# regardless of where it is called from.

LOCAL_PATH := $(call my-dir)
MUPDF_PATH := $(realpath $(LOCAL_PATH)/../..)

LOCAL_SHORT_COMMANDS := true

ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
HAVE_NEON := yes
endif

在 Application.mk 文件中添加:APP_SHORT_COMMANDS := true

1
2
3
4
5
6
7
APP_SHORT_COMMANDS := true

ifdef USE_TESSERACT

APP_STL := c++_static

endif

重新编译,OK的话会在 \jni\libmupdf\build\android\libs\arm64-v8a 目录下生成下列文件:

1
~\mupdf-android-viewer\jni\libmupdf\build\android\libs\arm64-v8a\libmupdf_java.so

Pdf Viewer 集成MuPdf

Demo具体源码可参考:https://github.com/longluo/AndroidPdfViewer

已测试OK!