☰
VS2010下编译podofo 0.9.4 PDF库:从CMake到VS配置的完整避坑
2026/9/29 18:52:04 网站建设 项目流程

简介:PoDoFo 是一个用于解析、修改和创建 PDF 文档的 C++ 类库。这份 VS2010 工程来自作者在 Windows 10 x64 环境下的实际构建尝试,因 CMake 生成解决方案多次失败,转而手工创建工程并成功编译,仅收录核心 src 部分,不含 Samples 等附加内容,适合需要直接在 Visual Studio 2010 中编译使用 PoDoFo 的 C++ 开发者。压缩包内共有 370 个文件,以 263 个头文件与 93 个 C++ 源文件为主体,并包含预先编译好的 freetype2、zlib 静态库和工程配置项,整体大小约 2.43MB,目录精简,源码组织清晰。目前已有 749 人浏览学习,工程覆盖 PDF 解析、绘制、加密等核心模块,编译后可直接产出 DLL 与 LIB 库文件;同时作者将构建时生成的必要配置头文件完整保留,免去使用者自行配置的步骤,也避开了依赖库版本匹配的常见坑。对于希望在旧版 VS 中集成 PDF 功能、或想研读 PoDoFo 核心源码的开发者,这份现成工程可显著缩短环境准备时间,快速进入开发或学习状态。

1. 在VS2010里编译podofo-0.9.4前,先弄懂这三件事

做PDF解析和生成,在VS2010里搭podofo-0.9.4工程,是个典型的"老环境补课"问题。podofo 是一个开源 C++ PDF 库,0.9.4 这个版本被很多老系统固化了,新需求往往只能在它上面补功能,而不是换库重来;而 VS2010(VC10)虽然老,但不少工业上位机、嵌入式工具链到现在还用它能编译出来承认。这篇文章想把从源码编译、CMake 生成、VS 工程配置到链接运行的完整过程捋一遍,特别是那些一看就头大的 C1189、LNK2038 和运行时崩溃。适合手里有老工程要维护、或被迫把 PDF 功能塞进 2010 环境的工程师。

2. 准备编译环境:依赖库、工具链和目录结构

2.1 下载podofo-0.9.4源码,先核对两个关键文件

我拿到 podofo-0.9.4 源码后,第一步不是急着点 CMake,而是先解压看两个东西:根目录的CMakeLists.txt和cmake/Modules目录里的Find*.cmake。因为这个版本没有现成的 VS2010 工程,所有.sln都是 CMake 现场生成的,你后面用哪些选项、会触发哪些依赖检查,全看这两个文件。

打开CMakeLists.txt,重点找option(PODOFO_HAVE_JPEG "Use LibJPEG" ...)类似的行。这些option()就是后面 CMake 命令行里-D参数的开关。默认情况下,podofo 会把能开的依赖都开着:libjpeg、libpng、libtiff、OpenSSL、FreeType、Lua。如果你照默认直接编,VS2010 会疯狂找新版第三方库,而这些库多半是用 VS2015 或 VS2019 编出来的,链接阶段必然报LNK2038运行时库不匹配。所以我的习惯是:第一次先全部关掉,把核心 PDF 库编出来,确认“无依赖也能跑”,然后再按需一个个开。这也是老工程最稳妥的推进方式,别想一口吃成胖子。

另一个关键文件是src/podofo.h或podofoConfig.h.cmake。它决定了你用静态库还是动态库时要不要定义PODOFO_STATIC。这一步踩坑的人特别多,后面第 4 章会专门说。现在你只需要确认这个版本支持静态编译,并且知道自己要编的是.lib还是.dll,因为 VS2010 的 Debug 和 Release 运行库配置会影响整个工程的属性继承。

2.2 VS2010的工程生成方式:用CMake还是自带工程

podofo 官方从不提供“podofo-0.9.4-vs2010.sln”这种现成工程,你必须用 CMake 生成。这里最大的坑是 CMake 版本。VS2010 的 generator 在 CMake 里叫Visual Studio 10 2010,CMake 3.20 之前还保留着,之后才慢慢从cmake --help里消失。所以我机器上常年留着 CMake 3.18 和一个 CMake 2.8.12 备用。如果你只有新版 CMake,可以试试,但生成失败时别死磕,换老版本是常态。

生成方式有两种:命令行和 CMake GUI。GUI 的好处是能用勾选直观看到所有PODOFO_HAVE_*选项,坏处是第一次设置CMAKE_PREFIX_PATH很麻烦,特别是依赖库分散在不同目录时。我一般用命令行,把选项写成一个 bat 脚本,这样以后重新生成工程时不用去 GUI 里翻配置。

还要决定 32 位还是 64 位。VS2010 的 64 位编译器并不是默认安装的,很多精简安装都只有 x86。如果你的上位机是 32 位进程,直接生成 Win32 即可;如果必须 64 位,得提前确认 VS2010 已装 x64 编译组件,否则 CMake 检查 generator 会失败。

2.3 手动准备依赖:zlib、libjpeg、openssl等,以及32位/64位选择

这是最容易让新手放弃的一步。podofo 0.9.4 的可选依赖和用途如下:

依赖库作用关闭时的功能损失
zlibPDF 流过滤 (FlateDecode)几乎必须,但 podofo 源码内置了 mini zlib 吗?实际上 0.9.4 仍要求外部 zlib,除非改配置。
libjpegJPEG 图片解码/编码无法处理 JPEG 图像
libpngPNG 图像同上
libtiffTIFF 图像无法嵌入 TIFF
FreeType字体渲染/度量无法做文本布局测量
OpenSSLAES/RSA 加密无法创建加密 PDF
Lua脚本功能一般用不到

我的建议:如果只是读写不加密的 PDF 文本、合并页面、加水印,那么全关也没关系。FlateDecode 压缩在 0.9.4 里其实依赖 zlib,绕不开,所以 zlib 还是得准备。好消息是 zlib 很小,用 VS2010 从源码编一个很轻松。你可以在 zlib 目录里打开projects/vc10工程,直接生成zlibstat.lib,注意配置成“Release /MD”或“Debug /MD”,和后面的 podofo 工程保持一致。

具体做法:先建一个干净的第三方目录,比如D:\thirdparty。分别把 zlib 源码解压到D:\thirdparty\zlib,把 podofo 源码解压到D:\podofo-0.9.4。不要放中文路径,也不要有空格。VS2010 对路径里的空格处理虽然没有太大问题,但有些旧版 CMake 模块会把路径拼错,所以洁癖一点。

3. 用CMake生成VS2010工程并配置静态库/动态库

3.1 编写CMake命令行,生成.sln

打开 VS2010 自带的“Visual Studio 命令提示符”,或者普通 cmd 后确认cmake在 PATH 里。我习惯在源码目录外建独立构建目录,这样源码不会被污染。先执行:

mkdir build-vs2010 cd build-vs2010 cmake -G "Visual Studio 10 2010" -A Win32 ^ -DPODOFO_BUILD_SHARED=OFF ^ -DPODOFO_HAVE_JPEG=OFF ^ -DPODOFO_HAVE_LIBPNG=OFF ^ -DPODOFO_HAVE_TIFF=OFF ^ -DPODOFO_HAVE_OPENSSL=OFF ^ -DPODOFO_HAVE_FREETYPE=OFF ^ -DPODOFO_HAVE_LUA=OFF ^ -DPODOFO_HAVE_ZLIB=ON ^ -DZLIB_LIBRARY="D:/thirdparty/zlib/build/zlibstat.lib" ^ -DZLIB_INCLUDE_DIR="D:/thirdparty/zlib" ^ ..

参数说明:

  • -A Win32指定平台架构,只有 CMake 3.1 以上才支持-A参数。如果你用老 CMake 2.8,就得去掉-A,生成之后再在 VS 里改“目标平台”。如果生成失败,检查 CMake 版本。
  • -DPODOFO_BUILD_SHARED=OFF表示编静态库。静态库链接简单,单一.lib文件,不需要部署 DLL。但注意要用时定义PODOFO_STATIC。
  • -DPODOFO_HAVE_ZLIB=ON保留 zlib,同时手动指定 zlib 的库和头文件路径,免得 CMake 去系统路径里瞎找。
  • -DZLIB_LIBRARY要指到你刚才用 VS2010 编译出来的 zlib 静态库。如果 zlib 是 32 位,podofo 也得是 32 位。

如果这一步提示Could NOT find ZLIB,多半是路径不对,或 zlib 没有先编好。可以先把-DZLIB_LIBRARY和-DZLIB_INCLUDE_DIR去掉,让 CMake 自己找系统路径,但那样容易找到 VS2015 版 zlib,后面照样 LNK2038。

3.2 打开后调整运行库和字符集

CMake 生成完毕后,进入build-vs2010目录,双击打开podofo.sln。不要急着按 F7 编译,先做两件预防动作。

第一,检查 CMake 默认生成的运行时库设置。对于Visual Studio 10 2010生成器,CMake 默认会根据 Debug/Release 选择/MDd或/MD。如果你 zlib 编的是/MD(动态 CRT),但 podofo 工程里某些项目被 CMake 设成了/MT,链接时一样报 LNK2038。我一般会在项目属性 -> C/C++ -> 代码生成 -> 运行库里,把所有要用的项目都统一成“多线程调试 DLL /MDd”或“多线程 DLL /MD”。这个统一必须包含所有依赖库,不然就是给自己挖坑。

第二,字符集改成“使用多字节字符集”。VS2010 默认新工程使用 Unicode 字符集,而 podofo 内部大量使用char*和 ANSI 字符串。虽然 CMake 生成的工程不一定会强设 Unicode,但你自己再建的调用工程必须注意,否则后面调用 API 时参数不匹配,出现PdfError全是乱码。这个坑留到第 4 章讲。

3.3 编译顺序和输出文件位置

在解决方案管理器里能看到多个项目,核心的是podofo主项目,还有其他测试项目。我可以只选中podofo项目,右键“生成”。CMake 会自动处理项目依赖,但要是你改了开关,建议先生成一次整个解决方案,确保所有由 CMake 生成的辅助项目先编好。

编译成功后,静态库输出通常在build-vs2010\src\Release\podofo.lib,Debug 则输出在build-vs2010\src\Debug\podofo.lib。头文件在源码根目录的src文件夹里,但注意podofoConfig.h是由 CMake 在build-vs2010\src下生成的,不是源码自带的。这个文件定义了当前配置(比如是否启用 OpenSSL、是否共享库)。你后面在自己的 VS2010 工程里加包含目录时,必须同时包含:

  • D:\podofo-0.9.4\src
  • D:\build-vs2010\src

少了第二个,编译会出现podofoConfig.h not found。这是最容易被漏掉的生成头文件。

4. 把podofo接进你的VS2010工程:链接、头文件与最小示例

4.1 新建VS2010控制台工程,设置包含目录和库目录

现在源库已经编好,我们来建一个实际使用的 VS2010 工程。打开 VS2010,新建一个 Win32 控制台应用程序,工程名随便,但最好放在和 thirdparty 同级目录,避免将来路径迁移。

在解决方案资源管理器中,右键工程选“属性”。在VC++目录下,把“包含目录”加上两个路径:D:\podofo-0.9.4\src和D:\build-vs2010\src。“库目录”加上D:\build-vs2010\src\Release(或者 Debug 目录)。

然后,在链接器 -> 输入 -> 附加依赖项里加上:

podofo.lib zlibstat.lib

zlibstat.lib是 zlib 静态库的文件名,如果你用zlib.lib或zdll.lib就是动态版,需要把对应 DLL 放到运行目录。对于 VS2010 老环境,我强烈建议全部静态链接,最后拷走一个 exe 就行。

这里的关键是“运行库”必须和 podofo 编译时一致。如果 podofo 是/MDRelease,你的调用工程也必须是/MDRelease,否则第 5 章 LNK2038 会来找你。你可以把调用工程设置为“多线程 DLL”或“多线程调试 DLL”,然后在“预处理器定义”里加上_CRT_SECURE_NO_WARNINGS,这能屏蔽掉fopen等函数的安全错误提示。

4.2 一个读PDF页面的最小C++代码

在上一节配置基础上,我写过一个最简示例:打开一个 PDF,打印页数。因为 podofo 0.9.4 的 API 命名和现代版不太一样,我用下面的代码验证基本链路通不通。

#include <podofo/podofo.h> #include <iostream> using namespace PoDoFo; int main() { PdfMemDocument doc; try { doc.Load("D:\\test.pdf"); std::cout << "PDF pages: " << doc.GetPageCount() << std::endl; } catch (PdfError& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; }

逻辑说明:

  • PdfMemDocument是 podofo 最常用的内存文档对象,0.9.4 里加载文件用Load()函数。
  • doc.Load如果失败会抛异常,所以必须用try/catch包住。不包也行,但错误提示极难定位。
  • 这里由于using namespace PoDoFo;,PdfError直接可用。

你可能想直接把连接矩阵放在这里,但注意一个细节:如果 podofo 是静态库,必须确认是否定义了PODOFO_STATIC。在 4.3 节说。

编译时如果报error C1189: #error : "PODOFO_STATIC must be defined ...",说明你没定义宏。进入项目属性——C/C++ -> 预处理器 -> 预处理器定义,加上PODOFO_STATIC。这个宏会让头文件里的导入导出dllimport变成普通声明,否则链接时会找不到一堆外部符号。

另一个问题是 VS2010 默认的“Unicode 字符集”会让doc.Load字符串参数变成宽字符类型。PdfMemDocument::Load接收的是const char*,你用 L"..." 就编译报错。所以正确做法是:把工程字符集设成“使用多字节字符集”,或者把宽字符串转成 UTF-8。我建议前者,省事且没有运行时转换开销。

4.3 参数说明:链接库名、宏定义、预处理

我把这组配置列成一张表,方便你对照检查:

项目设置值说明
包含目录D:\podofo-0.9.4\src;D:\build-vs2010\src第二个是 CMake 生成头文件所在目录
库目录D:\build-vs2010\src\Release取决于你编译的配置
附加依赖项podofo.lib;zlibstat.lib静态库方式
运行库/MD或/MDd与 podofo 全局统一
预处理器PODOFO_STATIC;_CRT_SECURE_NO_WARNINGS前者必须,后者抑制 fopen 安全告警
字符集使用多字节字符集匹配 podofo 的 ANSI API

如果你只做读取,到这一步就够了。但如果你想生成 PDF,会发现PdfPainter相关 API 里隐藏更多的坑,我们放到最后一章再写。

这里额外提一个“黑匣子”问题:podofo 的异常信息有时非常简略,只返回e.what()像Error 0x...。我见过有人加载失败后完全摸不着头脑。建议在catch里多打一个e.GetErrorCode()转成字符串,比如(int)e.GetErrorCode(),然后对照podofo/src/PdfError.cpp里的错误码表。这条血泪经验能省你大量排查时间。

5. 避坑:podofo 0.9.4在VS2010下常见的5个编译/链接问题

5.1 现象:fatal error C1083: Cannot open include file: 'zlib.h'

现象:编译 podofo 或你的调用工程时,头文件阶段直接报 zlib.h 找不到。

原因:CMake 生成时确实指定了ZLIB_INCLUDE_DIR,但该路径下没有 zlib.h,或者你把 zlib 和 zlib 头文件放在了不同目录。还有一种情况是用了#include <zlib.h>,但D:\thirdparty\zlib里只有zlib.h.in,没有zlib.h——因为 zlib 源码需要先运行configure或 CMake 生成头文件。

解决:如果是 zlib 源码目录,通常zlib.h就在根目录。如果找不到,去zlib 源码\build目录找。确认你的ZLIB_INCLUDE_DIR指向了包含zlib.h的那一层,而不是src。另外,VS2010 的“包含目录”是全局继承的,不要在“源文件目录”里写相对路径“../thirdparty/zlib”,否则项目迁移后必然挂。

5.2 现象:LNK2038: mismatch detected for 'RuntimeLibrary': value 'MD_DynamicRelease' doesn't match value 'MT_StaticRelease'

现象:链接时出现 LNK2038,通常发生在 podofo.lib 和你调用工程或 zlib 之间。

原因:podofo 编译时运行库是/MD(动态 CRT),而你的调用工程默认可能是/MT(静态 CRT)。在 VS2010 中这两者不能混用,因为内存分配、静态变量所属的 CRT 实例不同,链接器直接拒绝。

解决:打开所有相关工程——zlib 工程、podofo 工程、你的调用工程,统一“运行库”选项。最省心的是都用/MD。如果你坚持用/MT,那么 zlib 和 podofo 都必须用/MT重新编一遍。别想着用/MT的 podofo.lib 配/MD的调用工程,那是浪费时间。

5.3 现象:LNK2019: unresolved external symbol "class PoDoFo::PdfError __cdecl PoDoFo::PodofoSetError(...)"或大量_imp_符号找不到

现象:编译通过,但链接时报一堆外部符号未解析,全是_imp_前缀或 podofo 内部类。

原因:最常见于把动态库(DLL)的导入库当成静态库用。如果 CMake 里PODOFO_BUILD_SHARED=ON生成的是podofo.dll,它的.lib只是导入符号。你链接了它,但没有把podofo.dll放到 PATH 或 exe 目录,或你在代码里定义了PODOFO_STATIC但实际链接的是 DLL 导入库,头文件里的dllimport和dllexport对不上号。

解决:确定你到底想用静态还是动态。静态库方式:重新编译时PODOFO_BUILD_SHARED=OFF,并定义PODOFO_STATIC。动态方式:去掉PODOFO_STATIC宏,链接podofo.lib(指导入库),并把podofo.dll复制到 exe 所在目录。如果必须混用,报这个错不用奇怪,这是 VS2010 下最常见的“库形态”混淆。

5.4 现象:error C4996: 'fopen': This function or variable may be unsafe. Consider using _fsopen instead.

现象:编译你的代码时,只要用到标准文件函数,VS2010 就把 C4996 视为错误。

原因:VS2010 的 CRT 默认启用安全警告,把fopen、strcpy、sscanf等列为 deprecated。podofo 内部大量用这些函数,所以它的源码头文件也会触发这个警告。如果你在“预处理器定义”里没有加_CRT_SECURE_NO_WARNINGS,警告会升级为错误。

解决:在你的调用工程里加入_CRT_SECURE_NO_WARNINGS。如果你在编译 podofo 自带的工程也遇到这个错误,可以在 podofo 工程的“预处理器定义”里也加上它。这个宏没有副作用,只是告诉 CRT 不报这些安全警告。如果你追求安全,可以替换成_s版本,但在 podofo 源码里到处改不现实。我的血泪经验是:老工程直接用宏,别折腾源码替换。

5.5 现象:运行时崩溃Unhandled exception at 0x...,或PdfError内部断言的SetError(0x0001... )

现象:读某些 PDF 文件时崩溃,生成 PDF 时也会在Write阶段突然抛异常。关闭所有可选依赖后,调试器在PdfFilter或PdfTokenizer里中断。

原因:大概率是 zlib 版本和 podofo 0.9.4 不兼容。podofo 走 FlateDecode 时依赖 zlib 的inflateInit和inflate,如果你用的是 zlib 1.2.12 以后的高版本,某些结构体大小和旧版不同,而 podofo 0.9.4 是按旧 zlib 编译的,运行时在 DLL 边界上换结构体会导致内存越界。或者你用了 VS2015 编的 zlib,内部 CRT 调用的栈和 VS2010 不一致。

解决:用 VS2010 从源码重编 zlib,并且固定一个和 podofo 0.9.4 同年代的主流版本,比如 zlib 1.2.8。不要贪新。同时,PODOFO_HAVE_OPENSSL关闭后,如果你调用了加密相关 API,也会因为加密后端为空而崩溃。检查自己是否使用了PdfEncrypt,如果用了必须把 OpenSSL 打开,否则这种崩溃是无解的。

6. 让podofo为你生成PDF:迷你写入示例与后续扩展

读通了之后,生成 PDF 就是顺理成章的事。这里给一个我验证过的写入示例,它只输出一行文字到 A4 页面,没有花哨功能,但能帮你确认整条编译链没问题。

#include <podofo/podofo.h> #include <iostream> using namespace PoDoFo; int main() { try { PdfMemDocument doc; PdfPage* pPage = doc.CreatePage(PdfPage::CreateStandardPageSize(PdfPageSize::A4)); if (!pPage) { std::cerr << "Failed to create page" << std::endl; return 1; } PdfPainter painter; painter.SetPage(pPage); PdfFont* pFont = doc.CreateFont("Helvetica"); if (!pFont) { std::cerr << "Failed to create font" << std::endl; return 1; } painter.SetFont(pFont); painter.DrawMultiLineText("Hello from PoDoFo 0.9.4 VS2010", 50, 50); painter.FinishPage(); doc.Write("output.pdf"); std::cout << "PDF written successfully" << std::endl; } catch (PdfError& e) { std::cerr << "PdfError: " << e.what() << " code=" << static_cast<int>(e.GetErrorCode()) << std::endl; return 1; } return 0; }

代码说明:

  • PdfPage::CreateStandardPageSize(PdfPageSize::A4)生成标准 A4 尺寸。如果想自定义页面,可以用PdfPage的SetSize,但 0.9.4 的这组 API 要求传入PdfRect对象,参数顺序极容易记错,建议第一次就照示例写。
  • painter.DrawMultiLineText是绘制文本的函数,坐标原点在页面左下角,50, 50表示距左下角 50 个单位。VS2010 的double和 podofo 的double没有差异,但注意如果页面旋转过,坐标映射会变。
  • doc.Write("output.pdf")会覆盖已存在文件。如果文件正在被另一个程序占用,会抛异常,这个异常需要捕获。

编译这个示例时,记得把PODOFO_STATIC和字符集设置照 4.3 节核对一遍。运行后,检查生成的output.pdf用 Adobe Reader 或浏览器能正常打开。如果打不开,多半是 zlib 版本问题,回到 5.5 条。

往后扩展时,我最常用的几个方向是:

功能podofo 0.9.4 的入口备注
嵌入图片PdfImage+PdfPainter::DrawImage需要打开 JPEG/PNG 依赖
加密PdfEncrypt需要打开 OpenSSL 并设置密钥
读取表单字段PdfAcroForm老 API 和新版本差异较大
合并/拆分页面PdfMemDocument::Append0.9.4 对大型文件内存开销大

顺带说一个习惯:我每次拿到老库,都会先留一个“干净工程”,不含业务代码,只保留编译配置和最小示例。等哪天换电脑或交接时,不用重新折腾 CMake 参数,下午茶时间就能重新跑起来。这和用什么高深的技巧无关,纯粹是吃过翻车的亏,想留一颗后悔药。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询