WeHack BBS
为什么 MuPDF 的体积大? - 可打印的版本

+- WeHack BBS (https://bbs.wehack.space)
+-- 版块: 计算机技术 (https://bbs.wehack.space/forum-5.html)
+--- 版块: GNU/Linux 讨论区 (https://bbs.wehack.space/forum-6.html)
+--- 主题: 为什么 MuPDF 的体积大? (/thread-160.html)



为什么 MuPDF 的体积大? - vimacs - 05-17-2020

我一直很喜欢 MuPDF 这个 PDF 查看器。有一次我在水木社区提到 Windows 的 SumatraPDF 是基于 MuPDF 做的,有网友提了个问题,说为什么 SumatraPDF 只有几 M 的大小,而 MuPDF 却很大。我看了一下,/usr/bin/mupdf 居然有 35M,而且是在动态链接并 strip 了符号表的情况下的大小。为了解除我的疑惑,我当时就简单地分析了一下,找到了 mupdf 程序体积大的原因。当时我没有再详细地寻找证据,现在我从二进制文件里找到了证据,证实了我的想法,于是有了这个帖子。

首先说一下 MuPDF 这个项目。MuPDF 实现了一套自己的 PDF 引擎,在这套引擎的基础上,实现了一个简单而功能强大的基于 X11 或者 OpenGL 的 PDF 阅读器。当然了,在这个引擎之上实现其他 PDF 阅读器也是可以的,例如我上面说的 Sumatra,还有用 OCaml 实现的 llpp. 此外,也有人为 zathura 和 Okular 开发了基于 MuPDF 的 PDF 后端。MuPDF 官方还基于这套引擎开发了一个实用工具 mutool,可以很方便地做一些简单的 PDF 编辑和转换操作。

Arch 在打包 MuPDF 的时候,为了方便,把 MuPDF 的引擎和某些第三方库单独打包成了静态库,成为 libmupdf 软件包。而查看器主程序 mupdf 和 mutool 分别在软件包 mupdf (或 mupdf-gl) 和 mupdf-tools 里面。为了知道 mupdf 这个程序是由哪些文件链接起来的,看 libmupdf 的静态库就知道了。把 /usr/lib/libmupdf.a 的内容列出来一看,发现有好一些体积大的文件,如 SourceHanSerif-Regular.ttc.o,从名字上看,这些文件是字体文件。那么 mupdf 很可能就是因为嵌入了这些字体,导致体积变大。

这还只是猜测,为了证实我的想法,需要找到证据。由于 mupdf 已经被 strip 了,所以没法看到相关的符号,当然了,我可以拿 Arch 的构建脚本自己打包一个没 strip 的版本。为了方便,我直接用工具分析。

首先我把 libmupdf.a 解包,然后用 nm 查看这些字体文件的符号。我们还是看 SourceHanSerif-Regular.ttc.o.


代码:
$ nm SourceHanSerif-Regular.ttc.o
00000000017a94e4 D _binary_resources_fonts_han_SourceHanSerif_Regular_ttc_end
00000000017a94e4 A _binary_resources_fonts_han_SourceHanSerif_Regular_ttc_size
0000000000000000 D _binary_resources_fonts_han_SourceHanSerif_Regular_ttc_start


就3个符号,非常简单。我到 MuPDF 的源码里面 git grep _binary_resources, 定位到了 source/fitz/noto.c, 发现这个源文件就是用来查找内嵌字体的。noto.c 用了一些宏定义,构建的时候可以利用这些宏定义,决定链接哪些字体到最终的程序里面。

使用 strings 可以在 mupdf 里面找到 SourceHanSerif 这个字符串。现在我想知道 SourceHanSerif-Regular.ttc.o 是不是真的链接到 mupdf 里面。我用 r2 看了一下。


代码:
$ r2 SourceHanSerif-Regular.ttc.o
[0x00000000]> x @ loc._binary_resources_fonts_han_SourceHanSerif_Regular_ttc_start
- offset -  0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x08000040  7474 6366 0001 0000 0000 0004 0000 001c  ttcf............
0x08000050  0000 0118 0000 0214 0000 0310 4f54 544f  ............OTTO
0x08000060  000f 0080 0003 0070 4241 5345 edfa f516  .......pBASE....
0x08000070  0000 040c 0000 00f0 4346 4620 8b63 701a  ........CFF .cp.
0x08000080  0000 04fc 0159 5dff 4750 4f53 c495 328c  .....Y].GPOS..2.
0x08000090  0159 62fc 0000 d5e4 4753 5542 1c29 4914  .Yb.....GSUB.)I.
0x080000a0  015a 38e0 0002 358a 4f53 2f32 92cb 10d3  .Z8...5.OS/2....
0x080000b0  0163 6010 0000 0060 564f 5247 d912 3df7  .c`....`VORG..=.
0x080000c0  0163 60d0 0000 03e8 636d 6170 e665 3456  .c`.....cmap.e4V
0x080000d0  0163 64b8 0004 0fff 6865 6164 10fc cfd1  .cd.....head....
0x080000e0  0172 7b90 0000 0036 6868 6561 0c0f 0768  .r{....6hhea...h
0x080000f0  0172 7c70 0000 0024 686d 7478 b6ec 91ee  .r|p...$hmtx....
0x08000100  0172 7c94 0003 fa22 6d61 7870 ffff 5000  .r|...."maxp..P.
0x08000110  0176 76b8 0000 0006 6e61 6d65 8518 fb83  .vv.....name....
0x08000120  0176 76c0 0000 099c 706f 7374 ff86 0032  .vv.....post...2
0x08000130  0176 9d58 0000 0020 7668 6561 0ca5 12d1  .v.X... vhea....


然后我打开 mupdf 搜 ttcf 这 4 个字符,很幸运,r2 在 mupdf 里面发现有且仅有一处包含了这 4 个字符。


代码:
$ r2 /usr/bin/mupdf
[0x0003f860]> / ttcf
Searching 4 bytes in [0x2237280-0x22bd790]
hits: 0
Searching 4 bytes in [0x33ae30-0x2237280]
hits: 1
Searching 4 bytes in [0x1bb000-0x3397d8]
hits: 0
Searching 4 bytes in [0x3f000-0x1bac95]
hits: 0
Searching 4 bytes in [0x0-0x3e468]
hits: 0
0x003df591 hit0_0 .rlkryH_a?:ttcf.
[0x0003f860]> x @ 0x003df591
- offset -  0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x003df591  7474 6366 0001 0000 0000 0004 0000 001c  ttcf............
0x003df5a1  0000 0118 0000 0214 0000 0310 4f54 544f  ............OTTO
0x003df5b1  000f 0080 0003 0070 4241 5345 edfa f516  .......pBASE....
0x003df5c1  0000 040c 0000 00f0 4346 4620 8b63 701a  ........CFF .cp.
0x003df5d1  0000 04fc 0159 5dff 4750 4f53 c495 328c  .....Y].GPOS..2.
0x003df5e1  0159 62fc 0000 d5e4 4753 5542 1c29 4914  .Yb.....GSUB.)I.
0x003df5f1  015a 38e0 0002 358a 4f53 2f32 92cb 10d3  .Z8...5.OS/2....
0x003df601  0163 6010 0000 0060 564f 5247 d912 3df7  .c`....`VORG..=.
0x003df611  0163 60d0 0000 03e8 636d 6170 e665 3456  .c`.....cmap.e4V
0x003df621  0163 64b8 0004 0fff 6865 6164 10fc cfd1  .cd.....head....
0x003df631  0172 7b90 0000 0036 6868 6561 0c0f 0768  .r{....6hhea...h
0x003df641  0172 7c70 0000 0024 686d 7478 b6ec 91ee  .r|p...$hmtx....
0x003df651  0172 7c94 0003 fa22 6d61 7870 ffff 5000  .r|...."maxp..P.
0x003df661  0176 76b8 0000 0006 6e61 6d65 8518 fb83  .vv.....name....
0x003df671  0176 76c0 0000 099c 706f 7374 ff86 0032  .vv.....post...2
0x003df681  0176 9d58 0000 0020 7668 6561 0ca5 12d1  .v.X... vhea....


可以发现开头 256 字节都是一样的。在打开了 SourceHanSerif-Regular.ttc.o 的 r2 命令行里面用 "? loc._binary_resources_fonts_han_SourceHanSerif_Regular_ttc_end" 可以看到这个字体文件数据的结束地址是 0x97a9524, 而起始地址是 0x08000040, 于是大小是 0x17a94e4 (24M的大小). 可以用 wtf 保存整段数据。


代码:
[0x00000000]> wtf /tmp/objttc.data 0x17a94e4 @ 0x08000040
Dumped 24810724 bytes from 0x08000040 into /tmp/objttc.data


在 mupdf 里面也可以把从 0x003df591 开始的长度为 0x17a94e4 的数据读出来。(r2 似乎出了点问题,我尝试了好几次才执行成功)


代码:
[0x0003f860]> wtf /tmp/binttc.data 0x17a94e4 @ 0x003df591
Dumped 24810724 bytes from 0x003df591 into /tmp/binttc.data


之后对比两个文件:


代码:
$ md5sum /tmp/*.data
e9c9c8dfc52ef41c6be5c52d9d12dff3  /tmp/binttc.data
e9c9c8dfc52ef41c6be5c52d9d12dff3  /tmp/objttc.data

从而证实了 mupdf 的确链接了 SourceHanSerif-Regular.ttc.o, 程序里面内嵌了里面的字体数据。从而 MuPDF 体积大的原因找出来了。