抬头仰望星空,是否能发现自己的渺小。

伪斜杠青年

人们总是混淆了欲望和理想

浅谈 Android pdfium.so 编译

Google 从福昕阅读器那拿了7%的代码,搞了这么一个玩意儿:

https://pdfium.googlesource.com/pdfium/

实际上还有一个 Android 版本:

https://android.googlesource.com/platform/external/pdfium

通过对比源码,Android 这个链接源码差异主要是以下几点:

  • Android版本的头文件包含的接口偏少。
  • 由于开发者不同,命名上存在区别 Android 版多为驼峰命名。
  • 增加了 Android.bp 文件,大概是 Android 源码中的那套。
  • Android 版更新频率较慢,空方法也较多。

所以基于资料以及稳定性,这里都是使用这个版本:https://pdfium.googlesource.com/pdfium/

准备阶段

准备一个Ubuntu系统,如果使用衍生版Ubuntu则可能需要注释掉源码 build/install-build-deps.sh 脚本中的依赖判断语句(但依旧需要确认版本是debian系以及在下述的版本中):

下载源码

开终端,配置代理(不配置则无法下载):

export http_proxy=http://host:port
export https_proxy=http://host:port

可使用 curl https://google.com 进行查看是否成功。

下载编译链工具

git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

并将相关编译工具添加到环境变量以供使用:

export PATH=/path/to/depot_tools:$PATH

此时可以用gclient命令进行pdfium配置了,创建一个文件夹,终端切入该文件夹,同步源码:

gclient.py config https://pdfium.googlesource.com/pdfium.git

因为是编译 Android 库,所以需要配置下(必须):

echo "target_os = ['android']" >> .gclient

开始同步pdfium源码(没有代理则会无响应):

gclient.py sync

大概文件10gb,同步完成会有提示:

但这还不够,还需要继续下载一些工具(很重要):

gclient runhooks

接下来将终端切入到下载好的pdfium源码中,执行该脚本安装依赖:

cd pdfium
sudo ./build/install-build-deps-android.sh

等待安装完成。

PS:如果后面缺点什么,可能还需要装点,因为系统安装完成时附带的package并不是固定的。

apt-get update
apt-get install -y build-essential git subversion pkg-config python libtool cmake glib2.0-dev libatspi2.0-dev wget

配置修改

  1. 打开pdfium/BUILD.gn 文件,找到 config("pdfium_common_config") (大概在17行),将以下配置:
config("pdfium_common_config") {
   cflags = [ "" ]
   ldflags = []
   include_dirs = [ "." ]
   defines = [
     "PNG_PREFIX",
     "PNG_USE_READ_MACROS",
   ]

修改为(增加-fvisibility=default,增加"FPDFSDK_EXPORTS"):

config("pdfium_common_config") {
   cflags = [ "-fvisibility=default" ]
   ldflags = []
   include_dirs = [ "." ]
   defines = [
     "PNG_PREFIX",
     "PNG_USE_READ_MACROS",
     "FPDFSDK_EXPORTS"
   ]
  • 增加"-fvisibility=default"cflags ,将符号的可见性设置为默认值。这意味着其他组件可以引用该符号,并且符号定义可以被另一个组件中的同名定义覆盖。
  • 增加“FPDFSDK_EXPORTS”defines — 把符号表嵌入到共享库中。

2. 继续修改pdfium/BUILD.gn 文件,滚动到大约 152 行,在component("pdfium")前追加以下配置:

shared_library("pdfsdk") {
   deps = [":pdfium"]
   ldflags = [ "-latomic" ]
   if (target_os == "android") {
     configs -= [ "//build/config/android:hide_all_but_jni_onload" ]
   }
}
component("pdfium") {
//…

3. 修改pdfium/build_overrides/build.gni,大概在25行,注释掉以下代码:

#if (current_cpu == "arm") {
#arm_use_neon = true
#}

编译 so

编辑配置:

执行 gn 命令生成编译所必须的一些配置文件:

gn gen out/$arch

同时这里需要配置一些参数,参数配置的方式有两种:

  • 进入out/$arch,编辑文件args.gn,参数填上:
target_os="android"
target_cpu="$arch"

pdf_bundle_freetype=true
pdf_is_standalone=false
is_component_build=false
pdf_enable_xfa=false
pdf_enable_v8=false

is_debug=false
is_official_build=true
  • 直接跟在命令后面
gn gen out/$arch --args="target_os=\"android\" pdf_bundle_freetype=true pdf_is_standalone=false is_component_build=false pdf_enable_xfa=false pdf_enable_v8=false is_debug=false is_official_build=true target_cpu=\"$arch\""

例:

gn gen out/x86 --args="target_os=\"android\" pdf_bundle_freetype=true pdf_is_standalone=false is_component_build=false pdf_enable_xfa=false pdf_enable_v8=false is_debug=false is_official_build=true target_cpu=\"x86\""

这样可减少体积与编译时间,因为一些组件是用于web/chrome/js使用的。

执行编译:

ninja -C out/$arch pdfsdk

注:此处的$archx86x86_64armeabi-v7aarm64-v8a,也就是 ndk 中包含的 abis

最后文件pdfsdk.so会生成在out目录下,如:

该方式一次只能编译一个abi。编译多个abi文件可使用下文列出的脚本:

https://github.com/benjinus/android-support-pdfium/blob/master/BUILD.md

中间某些错误,大多数的失败原因都是源码未同步完整,包含编译工具链等,一共大概是10gb(其中包含Android SDK/NDK 以及一些引擎,时间巨长)。

其他问题(完全下载完则不存在问题)

如果pdfium/buildtools/linux64下没有gn则需要自编译gn(实际上在sync成功的情况下会自动去做这步),gn工具相关说明:https://gn.googlesource.com/gn/

git clone https://gn.googlesource.com/gn
cd gn
python build/gen.py
ninja -C out
To run tests:
out/gn_unittests

其他编译时报错则需要看缺少什么,如可能缺少clang,则装一下:

sudo apt install clang

相关资料:

ninja: error: loading ‘build.ninja’: No such file or directory #1463

https://pdfium.googlesource.com/pdfium/#get-the-code

How to build PDFium library for Android

https://medium.com/@johngray1965/i-wrote-a-script-to-handle-all-this-60ef838b49ee

主要参考:

https://github.com/benjinus/android-support-pdfium/blob/master/BUILD.md

So库

  • 基于版本:837fe726e639f823a62ed4aa4abda45d40f3c803(时间:文章书写时)
  • 基于版本:4f67a285e22100c9dc9d175e8269c8fed1734f34(2020.12.24最新)

更新到指定版本可使用命令:

gclient sync --revision 837fe726e639f823a62ed4aa4abda45d40f3c803

11条评论

  • 头像

    knziha

    我也在编译这个.so,拉取的是aosp上的pdfium,但是自己搭工具链。性能较旧版变低了,能否交流一下?

      • 头像

        knziha

        很简单,分享一下你用gn编译的。我用makefile编译了好几个版本,发现启用了skia后性能更低。

  • 头像

    knziha

    编译的好全,而且体积是最小的。https://github.com/KnIfER/LibPDFium/issues/1。不知支不支持安卓4?

    • Mosaic-C

      Mosaic-C

      试试?我没关注到4.4 官方似乎没有直接说明 但其他文章或者lib给到的是API 14,具体看实际测试情况吧~ (¬_¬)瞄

  • 头像

    knziha

    试了下不行。。这个跟工具链的平台版本有关的。比如makefile里可以指定$(CC)为 $(TOOLCHAIN)/bin/armv7a-linux-androideabi19-clang,就可以兼容安卓19了。

    • Mosaic-C

      Mosaic-C

      嗯,我猜也是需要调整编译参数,如果产品需要这样做,那就还是得重新打了,只是现在4.4 还有支持必要么?目测,也没几个软件能运行得动了

  • 头像

    knziha

    可以的哦,实机测试,pdfium可在安卓4流畅运行。关键安卓4省电……

    运行大厂的软件倒是很慢,什么抖音快手,微信支付宝,这类后台任务很多的跑不起。

    • Mosaic-C

      Mosaic-C

      哈哈,刚刚我看了你的项目,很棒,直接 cmake 编译会方便很多,厉害~

  • 头像

    knziha

    搭建的时候,cmake 一不小心就要整个重新编译。不过最后用来看代码很爽

发表评论