简介:一份在VS2005环境下成功编译podofo0.9.7开源PDF读写库的完整工程包,面向有C++基础、需要维护旧版Visual Studio项目的开发者,可解决PDF解析库依赖项多、编译配置繁琐的问题。包内共1382个文件,压缩后42.38MB,以372个h头文件、327个c文件和102个cpp源文件为主体,含编译生成的obj、lib、dll等,还集成了freetype、libjpeg、libpng、libtiff、zlib、openssl、lua等第三方库的预编译产物。工程将所有静态库统一整理为lib库,减少程序链接负担;保留PODOFO_HAVE_OPENSSL宏开关,方便按需启用加密功能,另外两个依赖Linux的用例默认禁用。目前已有570人学习浏览。适合从事PDF底层解析、文档处理或老版本VS工程维护的研发人员参考。 上个月帮一个合作方处理历史遗留系统,业务逻辑还跑在VS2005(Visual C++ 8.0)建的工程上,需求算简单:在老程序里加PDF导出功能。结果打开PDFium、mupdf一看要求VS2015+,libharu倒是能编但功能又太弱。不是不能升级编译器,而是项目里还挂着一堆只能VC8编译的第三方SDK,动一个全盘失控。折腾到最后,真正能落地的方案就是标题里写的这条路:VS2005编译podofo 0.9.7开源PDF读写lib库,把静态库编出来链接进老工程。
podofo 0.9.7是C++98时代的产物,对VC8的兼容性比后面几个大版本好得多,读写PDF、页面对象、图像处理都覆盖得比较全。这篇就把我完整编译podofo 0.9.7的过程写清楚:依赖库怎么选、CMake怎么配、源码层面有哪些坑、编完后怎么验证,以及怎么在老项目里接起来。如果你也在维护VS2005时代的老工程,这篇文章应该能帮你少走不少弯路。
1. 为什么偏偏是podofo 0.9.7:老编译器下的现实选择
1.1 PDF开源方案在VS2005面前的真实处境
先说结论:VC8编译器下,可选的开源PDF库其实寥寥无几。PDFium、mupdf从源码结构开始就用了一堆较新的C++特性,强行移植到VS2005的成本,比自己写个简单PDF写入器还高。libharu是纯C库,理论能过,但解析能力约等于零,加密、字体、图像处理都缺,碰上稍微复杂点的PDF就露馅。
podofo的优势就是这个老版本恰好和VC8处于同一年代。podofo 1.x之后代码大量引入C++11,VS2005编译基本无望;0.9.7作为0.9系列的收尾版本,代码风格保留着老一代C++库的克制感,STL用得保守,类设计直给,CMake支持也完整。要配VS2005,0.9.7几乎就是唯一解。
1.2 podofo的依赖树与“最小化编译”思路
podofo不是单文件库,它按功能模块依赖了不少第三方库:
- zlib:承担PDF数据流的压缩和解压
- libxml2:解析PDF内嵌的XMP元数据
- libjpeg / libpng:处理JPEG、PNG图像解码
- openssl:数字签名和加密
- freetype:字体光栅化
看到这串依赖先别慌。第一轮编译建议只保留zlib和libxml2,图像库、加密库、字体库全关掉,先把podofo.lib编出来,能完成PDF创建和基本解析就好。我第一次编就是“全都要”,结果光openssl在VS2005下折腾了一整天,后来发现业务根本用不上加密,白白浪费时间。
做个减法比做加法快得多。后续有需要再重跑一次CMake,打开对应开关重新编就行,成本不高。
2. 构建前哨:CMake版本、VS2005生成器与依赖库选型
2.1 CMake与VS2005的适配边界
VS2005在CMake里的生成器名称是Visual Studio 8 2005。这里“8”是VS2005的内部版本号(VC7.1是VS2003,VC8是VS2005)。如果你本机CMake版本太新,生成器列表里根本找不到这一项。实测在CMake 3.6.3里这个选项还在,建议用命令行直接指定,避免GUI版本差异:
cmake -G "Visual Studio 8 2005" ..新CMake版本就算能用,生成的工程文件也未必匹配VS2005的工程格式,所以不要在这上面追求“新”。
2.2 依赖库版本对照表
老环境下选依赖库,版本匹配大于一切。我这次用的是这组版本,全部在VS2005下验证过:
| 依赖库 | 建议版本 | 说明 |
|---|---|---|
| zlib | 1.2.8 | 广泛验证过的稳定版,nmake一条命令编完 |
| libpng | 1.2.53 | 1.5以上源码引入较多C99写法,VC8支持不完整 |
| libjpeg | 9a | 自带win32的makefile.vc,编译最省心 |
| libxml2 | 2.7.8 | 和podofo 0.9.7同年代,较少依赖新SDK接口 |
| openssl | 1.0.1c | 第二轮再说,第一轮建议直接关掉 |
选老版本不是保守,而是VC8对C99支持不完整,很多新版库哪怕编译选项全开,也过不了is not a member of std这种坎。
2.3 先跑通基础配置:关闭非核心选项
进入podofo源码根目录,建一个build目录,执行:
cmake -G "Visual Studio 8 2005" ^ -DPODOFO_BUILD_SHARED=OFF ^ -DPODOFO_BUILD_STATIC=ON ^ -DPODOFO_BUILD_TOOLS=OFF ^ -DPODOFO_HAVE_JPEG_LIB=OFF ^ -DPODOFO_HAVE_PNG_LIB=OFF ^ -DPODOFO_HAVE_TIFF_LIB=OFF ^ -DPODOFO_ENABLE_OPENSSL=OFF ..几个开关逐个说:
PODOFO_BUILD_STATIC=ON:编出podofo.lib,这是目标产物PODOFO_BUILD_SHARED=OFF:不生成podofo.dll,至少第一轮不要PODOFO_BUILD_TOOLS=OFF:官方命令行工具如podofopdfinfo等,老工程用不到,关掉省一半编译时间PODOFO_HAVE_JPEG_LIB、PODOFO_HAVE_PNG_LIB、PODOFO_HAVE_TIFF_LIB:图像支持开关,第一轮全关PODOFO_ENABLE_OPENSSL:加密签名支持,第一轮关掉
如果这一步CMake报找不到zlib或libxml2,说明第三方库还没准备好,先掉头去编依赖库。
3. 第三方库逐个编:把隐患扼杀在最前面
3.1 zlib:最顺利的开胃菜
zlib在Windows下有官方nmake脚本。打开VS2005命令提示符,cd到zlib-1.2.8目录:
nmake -f win32/Makefile.msc编译完成后目录里会出现zlib.lib、zdll.lib和zlib1.dll。zlib.lib是静态库,zdll.lib是动态库的导入库。如果podofo走静态链接,后续优先认zlib.lib。
把zlib.h、zconf.h拷到第三方include目录,把lib拷到lib目录,这个库就算过了。
3.2 libpng与libjpeg:两个常用图像库的编法
libpng 1.2.53自带scripts/makefile.vcwin32,但默认去当前目录找zlib。建议先把zlib的头文件和zlib.lib拷到libpng目录下,或者直接改makefile里的ZLIB_LIB、ZLIB_INCLUDE路径:
nmake -f scripts/makefile.vcwin32libjpeg 9a更简单,源码根目录自带makefile.vc:
nmake -f makefile.vc生成libjpeg.lib。两个库都不需要额外定义宏,算是比较好惹的。注意所有编译都必须在VS2005命令提示符里做,普通cmd窗口里nmake多半不在PATH里。
3.3 libxml2:VC8下最容易出事故的依赖
libxml2老牌但难伺候。推荐用源码自带的win32/configure.js生成Makefile:
cscript configure.js compiler=msvc platform=win32 debug=no nmake常见的坑有两个。一个是缺失wsockcompat.h,这是老版本libxml2对Windows SDK头文件布局判断不准导致的,解法是从更高版本libxml2里把这个头文件拷过来补上。另一个是链接时报unresolved external symbol __imp__closesocket@4这类socket相关错误,需要在编译或链接阶段补上ws2_32.lib。
编完会生成libxml2_a.lib(静态版)和libxml2.lib(动态版导入库)。整条链路如果用静态库,就取libxml2_a.lib。
3.4 统一运行库:Debug与Release、MT与MD
这是老VS环境里最容易连环爆炸的一环。VS2005的C运行时选项有四个:/MT静态多线程、/MTd静态多线程调试版、/MD动态多线程、/MDd动态多线程调试版。podofo的CMake配置在Release下一般走/MD,Debug下走/MDd。
如果某个第三方库手工编译时不小心用了/MT,最后链接podofo.lib时就会出现一堆LNK2005或LNK2038运行时库不一致报错。我就在zlib上踩过这个坑,最后统一成/MD重编才过。所以从第一步就记下每个库的编译命令,包括是否debug、静态还是动态,后面链接阶段能省掉大量排查时间。
4. 编译podofo本体:那些逼疯人的源码级错误
4.1 vsnprintf缺失与VC8的CRT差异
第一次编podofo.lib,大概率会在io/PdfStream.cpp或几个util文件里遇到:
error C3861: 'vsnprintf': identifier not found原因很简单:VS2005的CRT只有下划线版本的_vsnprintf,标准的vsnprintf是VS2013之后才补上的,而podofo 0.9.7直接调了vsnprintf。
处理办法是在podofo的配置头文件里加条件映射:
#if defined(_MSC_VER) && (_MSC_VER < 1500) #define vsnprintf _vsnprintf #endif注意_vsnprintf的返回值和C99规范不一样,缓冲区不够时返回-1而不是所需长度,理论上可能引发截断判断问题。但在podofo 0.9.7里这类调用主要用于字符串拼装日志,实测截断风险可以接受。
4.2 老CRT的C4996与“警告当错误”的坑
VS2005从VC8开始推荐_s安全版本接口,fopen、strcpy这类老用法会报warning C4996。如果工程开了/WX(警告当错误),编译会直接中断。podofo的CMake默认不开/WX,但如果你自己在IDE里改过编译选项,或者某些文件带出了/WX,就会看到满屏C4996。
解决方法是加两个预处理宏:
_CRT_SECURE_NO_DEPRECATE _CRT_SECURE_NO_WARNINGS另外还有些文件会报error C2039: 'getline' is not a member of 'std',先查是不是漏了#include <string>,别急着改业务逻辑。
4.3 链接时一堆unresolved external symbol怎么办
如果CMake配置和编译都过了,最后链接老工程时冒出一堆无法解析的外部符号,先别怀疑podofo没编好。按顺序排查:
- 确认链接的是podofo.lib,而不是podofo.dll的导入库
- 确认链接器的附加库目录里同时包含了zlib、libxml2等第三方.lib
- 确认Debug工程不能链Release的静态库,反过来也一样
- 如果是libxml2的符号报错,大概率是ws2_32库没链上
多数时候查完这四点就能定位,真正需要改podofo源码的情况反而是少数。
5. 验证产物并接入老项目
5.1 最小测试工程与验证逻辑
podofo.lib编出来后,先建一个最简控制台工程验证库能不能正常工作,别直接塞进大项目。测试代码:
#include <podofo/podofo.h> int main() { using namespace PoDoFo; PdfStreamedDocument document("hello_podofo.pdf"); PdfPage* pPage = document.CreatePage(PdfRect(0.0, 0.0, 595.0, 842.0)); if (!pPage) return -1; PdfPainter painter; painter.SetPage(pPage); painter.DrawRectangle(100.0, 100.0, 200.0, 100.0); painter.FinishPage(); document.Close(); return 0; }编译链接通过后运行,用任意PDF阅读器打开hello_podofo.pdf,能看到一个黑色矩形,就说明podofo.lib和所有依赖库都正常工作了。如果输出文件打不开,回头查运行库是否一致,这是最常见的翻车点。
5.2 老工程中的包含目录、库目录与运行库配置
接入老工程的配置路径:
- 项目属性 -> C/C++ -> 常规 -> 附加包含目录:填
podofo-0.9.7/src和第三方头文件目录 - 链接器 -> 常规 -> 附加库目录:填第三方lib目录
- 链接器 -> 输入 -> 附加依赖项:加
podofo.lib、libxml2_a.lib、zlib.lib,别忘了ws2_32.lib
字符集方面,VS2005工程默认多字节字符集,podofo 0.9.7支持没问题。如果工程切了Unicode,传字符串给podofo API时注意用PoDoFo::PdfString::FromUtf8转换,否则中文内容会乱码。
5.3 部署到没有VS2005环境的目标机器
静态库不是把所有运行时都打进去了。VS2005程序运行时依赖msvcr80.dll,目标机器没装VC2005运行库的话,exe启动就会报错。处置办法是把msvcr80.dll放到exe同目录,并带上对应的manifest文件;或者部署时直接丢一个vcredist_x86.exe安装包过去。
如果podofo走的是动态库方式,zlib1.dll、libxml2.dll也要一并拷贝。这个部署清单最好写进项目文档,不然半年后发版时又有人问“为什么客户机器上跑不起来”。
这次编库折腾下来,我最大的感受是:VS2005加podofo 0.9.7这个组合能成,不是谁技术厉害,而是选型上老老实实匹配了编译器时代。依赖库版本、debug/release、运行库模型、链接配置,任何一个环节没对齐,后面就会连锁爆坑。建议动手之前先建个文本文件,把每个库的编译命令记下来,编完一个勾一个。这种老环境项目,半年后你大概率还得再编一次,到时就靠这份记录救命了。
本文还有配套的精品资源,点击获取