PStill: 提高pdf文档中字体的兼容性
07/09/2011 一条评论
Update: 更好的解决方案:
PStill仍然不是那么好安装,而且在Windows/Mac上不能免费用。
在Linux上,如果问题仅仅是替换字体,更容易的方法是:
pdftops filename.pdf ps2pdf14 -dPDFSETTINGS=/prepress -dEmbedAllFonts=true \ filename.ps filename.new.pdf
============原文分割线=============
曾经使用Word写论文并提交到IEEE会议的人应该都深有体会,保证pdf嵌入所有的非常用字体是一个令人挠头的技术问题。这一次,我觉得我找到了一个彻底的解决方案。
首先,何为字体嵌入?pdf文档可使用任意字体,但并非每一种字体在所有的计算机上都有安装。如果一个pdf文件使用了一种特殊字体,在一台没有安装此字体的计算机上显示该文档可能会出现两种后果:(1)使用该字体的文字不能直接显示;(2)使用该字体的文字为另一种常用字体替换。无论哪一种都不是我们想看到的结果。于是,pdf就允许将字体嵌入到文档中,这样在没有安装该字体的计算机上也可正常显示。嵌入的方式有两种:字体嵌入(embed)和子集嵌入(subset)。字体嵌入很好理解,就是把整个字体都嵌入到pdf中。但可能字体中的某些文字并没有在文档中出现,实为冗余。子集嵌入这只将文档中出现的使用该字体的字符嵌入,不会使用更多的存储空间。
看起来这个主意不错,不会有什么问题了,可是实际环境中即使软件自动嵌入字体,也并非所有的字体都被嵌入。往往软件只会嵌入非常用字体,默认常用字体在所有的计算机上都有安装。对于只在Window上查看的pdf文档来说这不是个大问题,Window默认安装常用字体。但问题是,不同的操作系统对常用字体有不同定义,比如windows上使用Arial,但Linux则默认Helvetica。此外,字体的格式也不一样。常用的有两种:Type1和TrueType。其中Linux多使用Type1而Windows的ttf字体都是TrueType。
于是问题出现了。以我的文档为例子,我习惯使用office visio做图,用Latex写文章。visio的图片先输出为内嵌字体的pdf文件,然后使用pdftops和ps2eps命令在Linux中转化为eps文件方便Latex使用。以这样的方法生成的pdf,图片中的所有文字有自己的字体并能被拷贝。但由于源pdf由window生成,比如我用的consolas (一种等宽Arial字体)被嵌入,但嵌入为TrueType字体。在较新的Linux系统中还能正常显示,但在较老的,比如Unix系统上,图片中使用consolas的文字成了乱码。
在尝试过多种方法之后,我现在基本认为Latex的命令和ghostscript命令基本对eps中的字体没有控制能力,不能将其替换成其他字体。我需要借助于其他的工具,现在我找到了PStill。
PStill不免费,但是个人非商业用会可在Linux上免费使用。网页地址为http://www.wizards.de/~frank/pstill.html
在Linux上安装后,将所有的pfa和pfb文件拷贝到pstill中的PSFonts文件夹,运行pstill –v -I,pstill即可识别系统中的所有Type1文件。在此之后,我们即可使用PStill将一个pdf中所有没有嵌套或者嵌套为非Type1的字体,替换成对等的内嵌Type1字体。
比如,我有一个文件,使用pdffonts查看字体如下:
name type emb sub uni object ID ------------------------------------ ----------------- --- --- --- --------- ZDTJQW+NimbusRomNo9L-Medi Type 1C yes yes no 4 0 PYGHTE+NimbusRomNo9L-Regu Type 1C yes yes no 5 0 AINPGQ+NimbusRomNo9L-ReguItal Type 1C yes yes no 6 0 ZSTGUP+CMMI10 Type 1C yes yes no 24 0 TTMMUA+CMR7 Type 1C yes yes no 25 0 GWDCFW+CMMI7 Type 1C yes yes no 26 0 YXUGEN+CMSY10 Type 1C yes yes no 27 0 KHWZWC+CMR10 Type 1C yes yes no 28 0 KTICPW+CONSOLAS TrueType yes yes no 17 0 MSGNYE+ARIAL#20UNICODE#20MS_10 TrueType yes yes no 21 0 MUCBHF+CONSOLAS TrueType yes yes no 35 0 DOPWBF+CONSOLAS TrueType yes yes no 47 0 HXVFHU+CONSOLAS TrueType yes yes no 59 0 OWJARN+CONSOLAS TrueType yes yes no 68 0 IUAHFU+CONSOLAS TrueType yes yes no 77 0 Helvetica Type 1 no no no 87 0 Symbol Type 1 no no no 88 0 Helvetica Type 1 no no no 95 0 Helvetica Type 1 no no no 102 0 Helvetica Type 1 no no no 112 0
可见Helvetica和Symbol没有嵌套,consolas和arial嵌套为TrueType。使用PStill替换字体:
pstill –M defaultall –o file.ps file.pdf ps2pdf file.ps file_new.pdf
然后查看file_new.pdf的字体:
name type emb sub uni object ID ------------------------------------ ----------------- --- --- --- --------- QIAMBY+NimbusRomNo9L-Medi Type 1C yes yes no 8 0 IKPUDX+NimbusRomNo9L-Regu Type 1C yes yes no 10 0 WCYKGJ+NimbusRomNo9L-ReguItal Type 1C yes yes no 12 0 YSGMKE+CMSY10 Type 1C yes yes no 29 0 CAPFWR+CMR10 Type 1C yes yes no 31 0 KTICPW+CONSOLAS Type 1C yes yes no 19 0 MSGNYE+ARIAL#20UNICODE#20MS_10 Type 1C yes yes no 21 0 MUCBHF+CONSOLAS Type 1C yes yes no 33 0 TXLMQL+CMMI10 Type 1C yes yes no 23 0 UXAXYP+CMR7 Type 1C yes yes no 25 0 YWDNBN+CMMI7 Type 1C yes yes no 27 0 DOPWBF+CONSOLAS Type 1C yes yes no 40 0 OWJARN+CONSOLAS Type 1C yes yes no 49 0 IUAHFU+CONSOLAS Type 1C yes yes no 51 0 JWBBOE+CONSOLAS Type 1C yes yes no 47 0 OSPTOS+NimbusSanL-Regu Type 1C yes yes no 58 0
Helvetica被替换成Nimbus,所有非Type1字体被重新编译为Type1字体。这样,新的pdf文件可以在所有的类Unix系统上正常显示。Windows有较好的兼容能力,能直接显示Type1字体,还未发现问题。
关于字体的替换,PStill安装目录下的fontsub.table文件提供了具体的配置。
另外附上将系统所有字体文件软连接到PSFonts目录的脚本:
#!/bin/bash rm pfb.list rm pfa.list locate *.pfb | grep fonts > pfb.list locate *.pfa | grep fonts > pfa.list for ff in `cat pfb.list` do ln -s $ff done for ff in `cat pfa.list` do ln -s $ff done
也可以将以下的脚本放到全局PATH所指向的目录里面,就可以在任意地方使用pstill转化文件
#!/bin/bash # Wei Song # 15/05/2013 # set PSTILL_PATH to your own install directory PSTILL_PATH=$HOME/tool/pstill src_file="" tar_file="" while test $# -gt 0; do case "$1" in -h|--help) echo "pstill: convert and embedded fonts in a pdf" echo " " echo "pstill [options] pdf_file " echo " " echo "options:" echo "-h, --help show brief help" echo "-o output_file specify the output file" exit 0 ;; -o) shift if test $# -gt 0; then tar_file=$1 else echo "ERROR: no output files specified!" exit 1 fi shift ;; *) src_file=$1 shift ;; esac done src_file=`echo $src_file | sed -e 's/\.pdf$//g'` if [ -z $tar_file ]; then tar_file=$src_file.new.pdf fi $PSTILL_PATH/pstill -M defaultall -o /tmp/tmp$$.ps $PWD/$src_file.pdf > /dev/null 2>&1 if [ -f /tmp/tmp$$.ps ]; then ps2pdf /tmp/tmp$$.ps $tar_file > /dev/null 2>&1 if [ -f $tar_file ]; then echo "write $tar_file" else echo "fail to run: ps2pdf /tmp/tmp$$.ps $tar_file" exit 1 fi else echo "fail to run: $PSTILL_PATH/pstill -M defaultall -o /tmp/tmp$$.ps $PWD/$src_file.pdf" exit 1 fi