简介:面向需要在VS2005环境下集成PDF解析与生成能力的C++开发者,这份资源提供了podofo 0.9.7开源库的完整编译产物。作者已将依赖的freetype、libjpeg、libpng、libtiff、zlib、openssl等第三方库统一编译为静态lib,并保留lua、cppunit等可选模块,极大降低了开发时链接外部库的负担。资源包共1382个文件,以372个h头文件、327个c源文件、102个cpp文件及279个obj目标文件为主体,另有工程配置sln/vcproj、编译脚本mk等,压缩包整体42.38MB。目前已有571人学习下载。启用PODOFO_HAVE_OPENSSL宏后需额外配置两个dll及对应lib,跳过文档加密相关功能则可简化编译;两个依赖Linux库的用例默认禁用。适合具备一定编程基础、希望快速在VS2005中获得可用PDF读写库的开发者直接打开工程进行编译使用。
1. VS2005 编译 podofo 0.9.7 是什么:老工程里的 PDF 读写能力从哪来
手头还跑着十年前生产的 MFC 老系统,编译器锁死在 Visual Studio 2005,突然要加一个 PDF 读写模块,这套组合拳怎么打?近几年的 PDF 库基本都要 C++11 起步,VS2005 的编译器只认 C++98,翻遍开源项目,podofo 0.9.7 是为数不多还能在老编译环境里站起来的开源 PDF 读写库。它能做到解析 PDF、生成 PDF、提取文本、切分合并页面,编译产物直接给一个 lib 静态库,塞进老工程即可完成链接。这篇笔记解决的就是这条落地路径:依赖怎么备、CMake 怎么配、静态库怎么编、老工程怎么引用,以及我在重复这个过程中踩过的五个典型坑。适合还在维护 VS2005 老项目、或者被要求给遗留系统补 PDF 功能的从业者。
2. 编译前先看限制:podofo 0.9.7 的依赖树与 VS2005 的 C++98 边界
2.1 podofo 0.9.7 的源码组织:base、doc、aux 三层
先不急着敲命令。podofo 0.9.7 的源码结构并不复杂,但编译前把目录关系看明白,后面配 CMake 时能少走一半弯路。源码里最核心的是 src/base、src/doc、src/aux 三块:base 负责内存管理、编解码、加密算法这类基础设施;doc 是文档模型层,负责 PDF 的解析和写入,我们常说的 pdf解析能力就在这一层;aux 是上层工具,提供字体管理、Painter 绘制、图形状态这类带“画图”性质的接口。include/podofo 下放的是对外发布的公共头文件,编译完成后这个 include 目录会被原样复制到安装前缀里,老工程引用时只需要指到这里。
依赖方面,0.9.7 默认要带 zlib 和 libjpeg,可选依赖是 libtiff 和 OpenSSL。zlib 用于解压 PDF 里的 FlateDecode 流,libjpeg 用于处理 DCTDecode 图像,这两个在 VS2005 下都有老版本能用,属于必选项。libtiff 和 OpenSSL 就要看你实际业务了:如果不需要处理 TIFF 图像,可以直接关掉;如果不需要 AES/RSA 加密与签名,OpenSSL 也可以关掉。我一般建议第一次编译全部关闭这两个可选依赖,先把主链路跑通,再加功能。原因很现实:OpenSSL 的老版本在 VS2005 下能编,但新版本已经放弃对 VC80 的支持,为了一次性减少变量,不值得在第一步就引入一个更复杂的依赖。
2.2 VS2005 编译器边界:C++98、TR1 缺失、Unicode 选项
VS2005 就是 Visual C++ 8.0,_MSC_VER 是 1400,它对 C++ 标准的支持停留在 C++98 加极少扩展。有一件事必须提前知道:podofo 0.9.7 的源码里用到了 TR1 里的智能指针,而 TR1 正式进 Visual C++ 是 VS2008 以后的事。这意味着在 VS2005 下编译 podofo 原版源码,大概率会遇到 tr1/memory 找不到头文件的错误。这不是 podofo 的 bug,纯粹是编译器标准实现的时间差。常见做法是准备一份老版本 Boost 头文件,把 include 路径指过去,让 podofo 回退到 boost::shared_ptr。这个点放到避坑章再细说,这里先记住一个结论:VS2005 + podofo 0.9.7 的组合,需要额外备一份 Boost,哪怕只用到头文件。
字符集也要在编译前想清楚。VS2005 的 IDE 里默认字符集是 MBCS,但 podofo 内部统一走 std::string 加 UTF-8,Windows 侧的字体接口又需要 UTF-16。这个三层编码结构意味着:你在源码里写一个中文字符串字面量,源文件是什么编码,编译器就按什么编码塞进二进制。如果源文件是 GBK 保存的,字符串就是 GBK 字节流;podofo 画文本时再按 UTF-8 去解释,出来的 PDF 必然是乱码。所以后面所有带中文的源文件,我统一用带 BOM 的 UTF-8 保存。把这个编码问题放在编译之前解决,比事后查 PDF 乱码省事得多。
2.3 为什么选静态 lib 而不是 DLL
标题里写的是 lib 库,落地时我也建议做静态库。podofo 0.9.7 的 CMake 配置里可以同时生成 DLL 和静态库,但要塞进一个还在维护的老系统,静态库有三个不可替代的好处。第一,部署简单,不用额外把 podofo.dll、zlib.dll 和 libjpeg.dll 带进安装目录,老系统升级包越小,现场出问题的概率越低。第二,符号域干净,静态库只在链接时把用到的 obj 拉进来,不会像 DLL 那样导出全部接口,对老工程里可能存在的大量符号重名问题更友好。第三,调试方便,静态库的 PDB 和测试程序在一个进程里,断点可以直接进 podofo 内部,DLL 方式还得配符号路径。代价就是链接产物体积大一点,以及必须保证 podofo、zlib、libjpeg 的运行时库选项完全一致,这个一致性就是下一章配置的重点。
3. 用 CMake 和 VS2005 生成器配置 podofo 0.9.7:三个必改参数
3.1 选择合适的 CMake 版本与生成器
不要想着用 c++ 在线编译站点去跑 podofo,那些环境要么是 Linux 的 gcc,要么是新版 MSVC,给不了 VS2005 的 cl.exe 行为。本地必须装 VS2005 本体,很多人问 vs2005 下载问题,装完后确认 cl.exe 能跑,版本输出是 14.00 开头,编译环境才算就绪。CMake 这边要注意:新版 CMake 虽然还在保留 Visual Studio 8 2005 这个生成器,但 VS2005 的 IDE 和工具链太老,过新的 CMake 在探测环境时可能有兼容问题。我常用的做法是找一份还带 VS2005 生成器支持、且能在当前 Windows 上运行的老版 CMake,用绿色版解压就行,不用装。生成器字符串固定是 "Visual Studio 8 2005",这是 CMake 对 VS2005 的官方命名,拼错会直接报错。
生成出来的方案默认是 Win32 平台,这点对老程序是对的。VS2005 时代没有 Win64 的概念普及,podofo 0.9.7 在 64 位下的表现也没有经过充分验证,建议老老实实编 Win32 静态库,不要一上来挑战 x64。
3.2 准备依赖库位置:zlib、libjpeg 的 VS2005 版本
依赖库的准备工作比 podofo 本身更磨人,这一步做不好,后面链接阶段全是血泪。zlib 我选用 1.2.3 时代的老源码,它自带一个 Visual Studio 工程文件,直接打开编一个 Release 静态库,产物名叫 zlib.lib。libjpeg 选用 6b 版本,网上有现成的 VS2005 移植工程,编出来的库名叫 libjpeg.lib。注意这两份老代码在 VS2005 下编的时候,工程属性里的运行时库必须选“多线程 /MT”,而不是“多线程调试 DLL /MDd”,因为最终 podofo 也要统一用 /MT。/MT的意思是静态链接 CRT,部署时不需要带 msvcr80.dll;如果选/MD,那目标机器上就要装 VC++ 2005 运行库,老系统上这是额外风险。
我把两个库统一放在一个目录树里,方便 CMake 的变量去指:
D:/libs/ zlib-1.2.3/ include/zlib.h Release/zlib.lib jpeg-6b/ include/jpeglib.h Release/libjpeg.lib头文件和库文件分离放置,是为了后续 CMake 配置时,ZLIB_INCLUDE_DIR和ZLIB_LIBRARY两个变量不会指向混乱。很多人把 zlib.h 和 zlib.lib 放在同一个目录,CMake 也能找到,但分开放更清晰,尤其当你要同时维护 Debug 和 Release 两套库时。
3.3 三个必改参数:PODOFO_BUILD_TOOLS、PODOFO_WITH_OPENSSL、CMAKE_INSTALL_PREFIX
0.9.7 的顶层 CMakeLists.txt 里有一批 option 开关,具体名字可能随小版本略有差异,编之前先打开文件搜一遍 “option(” 把所有开关过目。我这里给出三个在 VS2005 场景下几乎必改的参数,按优先级排列。第一个是 PODOFO_BUILD_TOOLS,这个默认值可能把 podofo 附带的一堆命令行工具全编进去,包括 podofomerge、podofocrop、podofoencrypt 等,这些工具对我们的目标是多余的,必须关掉,能省下大量编译时间。第二个是 PODOFO_WITH_OPENSSL,前面说了,不要加密特性就必须关;如果你的业务确实要生成带 AES 加密的 PDF,那 OpenSSL 这一关绕不开,建议先把不加密版本跑通再考虑升级路径。第三个是 CMAKE_INSTALL_PREFIX,这是安装根目录,后续编译完成执行 install 步骤会把头文件、库文件统一放到这个目录,提前设好,后面引用时路径就一个字都不会错。
完整命令大致是这样,Windows 命令行下用^换行:
cmake -G "Visual Studio 8 2005" ^ -DPODOFO_BUILD_TOOLS=OFF ^ -DPODOFO_BUILD_CPP_API=OFF ^ -DPODOFO_WITH_OPENSSL=OFF ^ -DPODOFO_WITH_TIFF=OFF ^ -DZLIB_INCLUDE_DIR=D:/libs/zlib-1.2.3/include ^ -DZLIB_LIBRARY=D:/libs/zlib-1.2.3/Release/zlib.lib ^ -DJPEG_INCLUDE_DIR=D:/libs/jpeg-6b/include ^ -DJPEG_LIBRARY=D:/libs/jpeg-6b/Release/libjpeg.lib ^ -DCMAKE_INSTALL_PREFIX=D:/libs/podofo-0.9.7-install ^ D:/src/podofo-0.9.7参数解释:-G指定 VS2005 生成器,CMake 会生成一个 .sln 解决方案和你可以直接用 VS2005 打开的 .vcproj 工程文件。PODOFO_BUILD_TOOLS关闭命令行工具集,PODOFO_WITH_OPENSSL和PODOFO_WITH_TIFF关闭可选依赖。四个*_INCLUDE_DIR/*_LIBRARY变量是给 CMake 指路用的,注意库文件路径必须精确到.lib文件本身。CMAKE_INSTALL_PREFIX是后续cmake --build完成后安装动作的目标目录。最后一个参数是 podofo 源码根目录,里面必须能看到顶层 CMakeLists.txt。
配置命令跑完后检查一下输出,确认生成器是 “Visual Studio 8 2005”,确认没有出现 “Could NOT find ZLIB” 之类的红色警告。有一个常见误用是:把源码路径写到了 include 子目录,CMake 会报 “CMakeLists.txt not found”,这不是什么深层问题,把路径指回源码根目录即可。
4. 编出 podofo.lib 的完整操作:从编译到老工程引用的可复现步骤
4.1 用 VS2005 打开生成的工程并按 Release|Win32 编译
CMake 完成后,CMakeCache.txt 所在目录里会有一个 podofo.sln。直接用 VS2005 打开这个解决方案,解决方案配置选 Release,平台选 Win32。先编 podofo 这个项目本身,它就是要的静态库目标。有些版本会要求先编一次 Install 项目才能产生完整头文件集合,但 0.9.7 的公共头文件已经在源码 include 目录里了,不需要额外生成。在解决方案管理器里找到 podofo 项目,右键生成,VS2005 的 Output 窗口会滚动编译信息,只要不出现 error C、error LNK 开头的行,就算通过。
如果这一步出现了 tr1/memory 之类的报错,不要慌,回到第 2 章的结论:准备一份老 Boost,例如 boost 1.34 或 1.35 的头文件目录,放进 podofo 工程的附加包含目录。0.9.7 源码在#include <tr1/memory>失败后,其实还有回退到 boost 的预处理分支,只要让预处理器能找到 boost/tr1/memory.hpp 等文件,它就会自动切换过去。这个分支行为不是每个版本都在源码里写死的,所以以实际编译器报错为准,见招拆招。
4.2 核对产物:podofo.lib、头文件清单
编译完成后,在 build 目录下的 Release 子目录里找到 podofo.lib。我们要确认三件事:库文件存在、大小合理、且没有附带生成一堆 DLL。0.9.7 静态库的大小通常有几兆字节,具体取决于编译选项和依赖裁剪,小于几百 KB 就要警惕是不是编成了空壳。还要在 Release 目录里看有没有 podofo.dll,出现 DLL 说明 CMake 配置里静态库选项没生效,或者你编的是默认的动态目标。静态库模式下只有 .lib,没有 .dll,也没有 .exe。工具集 podofomerge 那些如果没关干净,Release 目录里会混入一堆 exe,可以忽略,但建议回头把 PODOFO_BUILD_TOOLS 确认关掉。
头文件不需要手工拷贝。在 VS2005 的解决方案里找到 INSTALL 项目,右键生成,CMake 会把 include 下的 podofo 头文件、Release 下的 podofo.lib,以及依赖 zlib.lib 按之前配置的 CMAKE_INSTALL_PREFIX 目录 D:/libs/podofo-0.9.7-install 组织好。生成后目录结构如下:
D:/libs/podofo-0.9.7-install/ include/podofo/podofo.h include/podofo/base/PdfString.h include/podofo/doc/PdfDocument.h ... lib/podofo.lib这个目录就是你的交付物。老工程引用的就是 include 和 lib 这两个子目录,其他文件一概不用带。
4.3 老工程引用的最少配置:头文件路径、库路径、预处理器
在 VS2005 的老工程里接入,不用做任何 COM 注册或环境变量配置,只需在工程属性里做最少三项修改。第一,C/C++ 的“附加包含目录”加上D:/libs/podofo-0.9.7-install/include。第二,链接器的“附加库目录”加上D:/libs/podofo-0.9.7-install/lib。第三,由于 podofo 的头文件里用了 zlib 的类型定义,必须在链接器输入里显式加入 zlib.lib,或者用代码里的#pragma comment(lib)声明。常见做法是在统一的预编译头或公共头文件里写死依赖,避免每个 cpp 重复维护:
#pragma comment(lib, "podofo.lib") #pragma comment(lib, "zlib.lib") #pragma comment(lib, "libjpeg.lib")注意这里的 pragma 写的是库文件名,不是完整路径,链接器靠上一段说的附加库目录去查找。如果 zlib.lib 和 podofo.lib 不在同一个目录,就再补一条对应的附加库目录。另一个容易漏的地方是预处理器:podofo 0.9.7 的头文件在某些配置下需要_CRT_SECURE_NO_DEPRECATE,否则 VS2005 会疯狂报警,如果老工程把警告视为错误,编译期异常会直接终止。建议在工程预处理定义里统一加上这个宏,或者在包含 podofo 头文件之前 define 掉。这个细节属于“早晚要撞上”的问题,提前加好省得返工。
5. VS2005 编译 podofo 0.9.7 避坑:五条血泪排查记录
5.1 C4819 中文注释乱码
现象:VS2005 编译 podofo 源码里的 cpp 文件,输出窗口刷出一排 warning C4819,而且注释里的中文变成乱码,甚至提示“无法识别”的字符替换。Debug 时中文断点字符串也显示不对。
原因:podofo 0.9.7 源码里带中文注释的文件不多,但一旦遇到,本质是源文件用 UTF-8 无 BOM 保存,VS2005 按系统 ANSI 代码页 936 去解码,字节流错位。新版 VS 默认能正确识别 UTF-8,VS2005 不行。
解决:在 VS2005 里打开报 C4819 的文件,另存为,编码下拉框选“Unicode (UTF-8 带签名)”,再重新编译。如果文件多,用脚本批量把 UTF-8 无 BOM 转成带 BOM。注意不要顺手把整个工程的代码页改成 UTF-8,VS2005 的全局选项里没有这个开关,老实的做法就是逐文件调整。
5.2 tr1 头文件找不到,智能指针编译失败
现象:编译 podofo 源码时,报错fatal error C1083: Cannot open include file: 'tr1/memory': No such file or directory,位置通常在 base 层的 PdfMemoryManagement 或 doc 层的 PdfDocument 实现里。
原因:podofo 0.9.7 的部分代码用了std::tr1::shared_ptr,VS2005 的标准库没有 TR1 实现,这个组件从 VS2008 才开始引入。不是 podofo 选错了依赖,是 VS2005 太老。
解决:准备一份支持 VS2005 的老版 Boost 头文件目录,例如 boost 1.34 或 1.35,在 podofo 工程或 CMake 的 include 路径里添加该目录。podofo 源码里有检测 Boost 和 TR1 的预处理分支,头文件能找到后会回退到boost::shared_ptr。如果源码分支里没有这个回退,那就再定义一个PODOFO_HAVE_BOOST之类的宏去激活对应分支,具体宏名以源码里#if defined的标注为准。这一条是 VS2005 场景下最典型的“源码看得见但编译不过”案例。
5.3 链接报错:zlib 符号带 _imp前缀,解析不了
现象:链接 podofo.lib 时,报 LNK2019 或 LNK2001,符号名长得像__imp__inflateInit2_、__imp__compress2,常规手段检查没发现拼写错误。
原因:__imp_前缀代表链接器在找 DLL 的导入库符号。也就是说,编译时用的 zlib.lib 是一个“导入库”,真正的 zlib 实现在 zlib.dll 里。podofo 静态库在编译时如果链接了 zlib 导入库,生成的代码里对 zlib 函数的调用就会引用 _imp开头的符号,最终链接时找不到对应的 DLL 导入符号就报错。这是静态库和 DLL 混合使用时最经典的翻车现场。
解决:重新用 VS2005 编一份 zlib 静态库,确保 zlib 工程属性里的“配置类型”是“静态库 (.lib)”,不是“动态链接库 (.dll)”旁边附带生成的那个导入库。替换掉 CMake 配置里的 ZLIB_LIBRARY 路径后,重新配置和编译 podofo。验证办法:用 VS2005 的 dumpbin 工具查看 zlib.lib 里的符号,静态库符号不带__imp_前缀,导入库一定带。
5.4 C4996:fopen、strcpy 全被当成错误
现象:编译 podofo 0.9.7 时出现大量error C4996: 'fopen': This function or variable may be unsafe,打开源码看明明是标准 C 函数,VS2005 却直接报错而不是警告。
原因:VS2005 默认启用了“安全检查”机制,fopen、strcpy、sprintf 这类旧 C 函数都被标记为 deprecated,提示用法 unsafe。如果工程设置里把警告级别调高并且“将警告视为错误”,C4996 就升级成了 error。podofo 是老库,源码里用这些函数属于正常现象,问题出在编译环境默认策略。
解决:在工程预处理定义里加_CRT_SECURE_NO_DEPRECATE,或者在 podofo 工程的 C/C++ 命令行里加/D_CRT_SECURE_NO_DEPRECATE,把这类安全警告整体压掉。另外,不需要也不建议签字符串为安全版本去改动源码,那会把 podofo 和你的老工程里所有调用都牵动一遍,干扰定位真正的问题。
5.5 生成的 PDF 中文乱码:字体和编码错配
现象:用 podofo 生成的 PDF 打开后,中文变成方框、问号,或直接缺失,英文正常。
原因:这是使用阶段最常见的坑,不在编译期。绘制中文文本时,podofo 的字体管理需要从系统字体里找到包含中文的字体文件,并在 PDF 中嵌入或引用。如果直接用默认的 Helvetica,它不含中文字形,中文全部无法映射;如果源文件里的中文字符串不是 UTF-8 编码,podofo 按 UTF-8 解析也会得到乱码。
解决:创建 PdfFont 时指定中文字体名称,例如 “SimSun” 或 “SimHei”,并设置字体嵌入。确保传入 DrawText 的文本是 UTF-8 编码。VS2005 工程里的 cpp 文件保存为带 BOM 的 UTF-8 后,字符串字面量默认还是按源代码字符集解释,但 podofo 不会自动转换,稳妥做法是显式把 std::string 里面的字节流构造成 UTF-8,不依赖编译器隐式行为。还有一步:检查你的 VM 或系统是否安装了对应中文字体,没有字体文件时 podofo 嵌入失败,但这个情况在老 Windows 服务端上并不少见。
6. 落地验证:用 podofo 写一个最小 PDF demo 并检查运行依赖
前面配置和编译都完成后,最后一步是写一个最小验证程序,确认库不是“编得出来但用不了”。在新建的 VS2005 控制台工程里,配置好包含目录和库目录,然后写下面这个 demo,覆盖 PDF 创建、绘制文本、保存和再读取四个动作:
#include <podofo/podofo.h> #include <iostream> #pragma comment(lib, "podofo.lib") #pragma comment(lib, "zlib.lib") #pragma comment(lib, "libjpeg.lib") int main() { using namespace PoDoFo; PdfMemDocument doc; PdfPage* pPage = doc.CreatePage(PdfPage::CreateStandardPageSize(ePdfPageSize_A4)); if (!pPage) return -1; PdfPainter painter; painter.SetPage(pPage); PdfFont* pFont = NULL; PdfFontManager fontMgr; pFont = fontMgr.CreateFont("SimSun", NULL); painter.SetFont(pFont, 12.0); painter.DrawText(50, 50, "Hello PoDoFo"); // 这里可换成 UTF-8 中文 painter.FinishPage(); doc.Write("out.pdf"); PdfMemDocument reader("out.pdf"); std::cout << "pages: " << reader.GetPageCount() << std::endl; return 0; }这段代码里的 PdfMemDocument 同时承担创建和读取职责,Write 后立刻再打开同一个文件,能一次性确认写入管道和解析管道都正常。运行成功后,再用 dumpbin 确认 podofo.lib 的链接没有隐藏动态依赖。命令是:
dumpbin /dependents out.exe输出里如果只有 KERNEL32.dll 和 USER32.dll 这类系统库,说明静态链接干净,部署时不需要额外带 DLL。这一步必须做,我在实际项目里就遇到过表面编译通过、运行时却报缺 DLL 的情况,原因就是 zlib 或 libjpeg 被编成了动态库混进了链接。
验证通过之后,把 out.pdf 用其他 PDF 阅读器打开检查页面尺寸和文本内容,这是最后一道外部视角确认。整个方案到这一步就算落地了。基于多次重复这类老编译链的经验,我个人最大的教训是:永远先锁定 zlib 和 libjpeg 的静态库编译,再动 podofo 本体。依赖库一旦编译选项不统一,后面所有链接报错都会指向 podofo,排查半天结果发现是 zlib 的运行时库和 podofo 不一致,这种白费功夫的经历实在不值得重复。希望这篇笔记能帮你在 VS2005 上一次性把 podofo 0.9.7 编译到能用,避掉我已经替你踩过的那些坑。
本文还有配套的精品资源,点击获取