VS2005编译podofo 0.9.7:老工程集成PDF导出库实战指南
简介一份在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工程维护的研发人员参考。 上个月帮一个合作方处理历史遗留系统业务逻辑还跑在VS2005Visual C 8.0建的工程上需求算简单在老程序里加PDF导出功能。结果打开PDFium、mupdf一看要求VS2015libharu倒是能编但功能又太弱。不是不能升级编译器而是项目里还挂着一堆只能VC8编译的第三方SDK动一个全盘失控。折腾到最后真正能落地的方案就是标题里写的这条路VS2005编译podofo 0.9.7开源PDF读写lib库把静态库编出来链接进老工程。podofo 0.9.7是C98时代的产物对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之后代码大量引入C11VS2005编译基本无望0.9.7作为0.9系列的收尾版本代码风格保留着老一代C库的克制感STL用得保守类设计直给CMake支持也完整。要配VS20050.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是VS2003VC8是VS2005。如果你本机CMake版本太新生成器列表里根本找不到这一项。实测在CMake 3.6.3里这个选项还在建议用命令行直接指定避免GUI版本差异cmake -G Visual Studio 8 2005 ..新CMake版本就算能用生成的工程文件也未必匹配VS2005的工程格式所以不要在这上面追求“新”。2.2 依赖库版本对照表老环境下选依赖库版本匹配大于一切。我这次用的是这组版本全部在VS2005下验证过依赖库建议版本说明zlib1.2.8广泛验证过的稳定版nmake一条命令编完libpng1.2.531.5以上源码引入较多C99写法VC8支持不完整libjpeg9a自带win32的makefile.vc编译最省心libxml22.7.8和podofo 0.9.7同年代较少依赖新SDK接口openssl1.0.1c第二轮再说第一轮建议直接关掉选老版本不是保守而是VC8对C99支持不完整很多新版库哪怕编译选项全开也过不了is not a member of std这种坎。2.3 先跑通基础配置关闭非核心选项进入podofo源码根目录建一个build目录执行cmake -G Visual Studio 8 2005 ^ -DPODOFO_BUILD_SHAREDOFF ^ -DPODOFO_BUILD_STATICON ^ -DPODOFO_BUILD_TOOLSOFF ^ -DPODOFO_HAVE_JPEG_LIBOFF ^ -DPODOFO_HAVE_PNG_LIBOFF ^ -DPODOFO_HAVE_TIFF_LIBOFF ^ -DPODOFO_ENABLE_OPENSSLOFF ..几个开关逐个说PODOFO_BUILD_STATICON编出podofo.lib这是目标产物PODOFO_BUILD_SHAREDOFF不生成podofo.dll至少第一轮不要PODOFO_BUILD_TOOLSOFF官方命令行工具如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.vcnmake -f makefile.vc生成libjpeg.lib。两个库都不需要额外定义宏算是比较好惹的。注意所有编译都必须在VS2005命令提示符里做普通cmd窗口里nmake多半不在PATH里。3.3 libxml2VC8下最容易出事故的依赖libxml2老牌但难伺候。推荐用源码自带的win32/configure.js生成Makefilecscript configure.js compilermsvc platformwin32 debugno nmake常见的坑有两个。一个是缺失wsockcompat.h这是老版本libxml2对Windows SDK头文件布局判断不准导致的解法是从更高版本libxml2里把这个头文件拷过来补上。另一个是链接时报unresolved external symbol __imp__closesocket4这类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下一般走/MDDebug下走/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、运行库模型、链接配置任何一个环节没对齐后面就会连锁爆坑。建议动手之前先建个文本文件把每个库的编译命令记下来编完一个勾一个。这种老环境项目半年后你大概率还得再编一次到时就靠这份记录救命了。本文还有配套的精品资源点击获取