尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Delphi 12.3集成PDFium:从tgz包到PDF渲染与文本提取实战

Delphi 12.3集成PDFium:从tgz包到PDF渲染与文本提取实战 简介本资源是面向Delphi开发者尤其使用Delphi 12.3版本的PDFium Windows x64原生库集成包用于在Delphi项目中实现高性能PDF渲染、文本提取、表单填充、注释处理及文档结构解析等核心功能。压缩包共30个文件包含24个C/C头文件如fpdf_view.h、fpdf_formfill.h、fpdf_edit.h等、1个静态链接库pdfium.dll.lib、1个动态链接库pdfium.dll、1份版本说明VERSION、1份CMake配置PDFiumConfig.cmake、1份构建脚本args.gn及1份许可证文件LICENSE全面支撑Delphi调用PDFium原生API的开发闭环。资源大小仅2.88MB轻量紧凑适配Windows平台x64架构。目前已有30人学习下载开发者可直接将头文件与lib/dll纳入Delphi工程结合封装示例快速实现跨平台PDF处理能力显著降低PDF功能开发门槛。 上次帮朋友调一个老系统的PDF打印模块机器是Win10 64位IDE换成了最新的Delphi 12.3。他不知从哪个社区FTP里翻到一个叫pdfium-win-x64.tgz的控件包非官方的说是一个开发者把PDFium的x64动态库连同Pascal接口翻译一起打包了。名字听着挺普通但真要在Delphi 12.3里把这套东西跑起来从解压、引用、翻译接口到踩内存坑一步一个雷。今天我把这次完整集成过程梳理一遍为什么选PDFium、tgz包怎么处理、接口怎么封装、渲染和文本提取怎么写以及我实际踩过的几个典型问题。给正在折腾Delphi加PDF的同行做个参考。1. 项目起因一个tgz包引发的PDF集成需求1.1 PDFium到底是何方神圣PDFium是Google主导维护的开源PDF渲染引擎Chrome浏览器内置的PDF阅读器就是用它做的。它本身是C写的对外暴露一套纯C接口所以跨语言绑定非常方便。Delphi开发者拿到PDFium基本等于白捡了一个免费、可商用BSD协议、跨平台的PDF解析和渲染内核。很多老Delphi项目处理PDF第一反应是装Gnostice、QuickPDF、powerPDF这类商业控件。功能确实全但问题也很明显版本绑定IDE、授权费不低、部署文件大。PDFium就不一样它只有一个pdfium.dll体积通常在几MB到十几MB之间接口简单适合做轻量集成。1.2 为什么我不选商业PDF控件商业控件在Delphi里其实是双刃剑。功能全但经常遇到IDE版本升级后控件安装不上、组件面板加载失败这种破事。热搜词里有个很典型的场景delphi 控件版本问题 导致 每次进入ide都丢失控件需要重新放置保存后还是那样这就是第三方控件注册表和IDE版本兼容性翻车了。PDFium走的是动态库加手写接口的路子不进IDE组件面板不装设计期包完全绕开这类问题。我这次用PDFium核心目的有三个一是给老系统补一个PDF预览能力二是批量把PDF页面导出成图片做存档三是从PDF里抽取文本做关键词检索。商业控件做这些当然没问题但为一个预览功能引入一套重型组件库维护成本不划算。相比之下PDFium的纯函数式接口反而更符合Delphi工程里功能模块独立的风格。1.3 Delphi 12.3下的PDF处理现状Delphi 12.3在Win64桌面开发上已经非常成熟TBitmap、TMemoryStream这些基础类处理位图数据没问题。但Delphi自己不带PDF渲染能力TPrinter也只能处理打印不能解析PDF内容。所以想读取PDF页面尺寸、渲染页面、提取文字必须借助第三方库。FireMonkey框架虽然跨平台但PDFium在Windows下的x64动态库集成最省事其他平台需要分别编译对应库工作量会大不少。这次项目只跑Windows 64位桌面所以我直接锁定了win-x64这个目标这也是这个tgz包名字里最关键的标识。提示如果以后要支持Linux或macOSPDFium官方也提供对应目标平台的编译产物但接口翻译单元基本可以复用只有DLL路径和加载方式需要调整。2. pdfium-win-x64.tgz包拆解与集成准备2.1 拿到tgz包后先做MD5校验再解压从网上下载的tgz包第一件事不是解压而是校验完整性。我习惯先算MD5和发布者给的校验值比对。这也是个很常规的自我保护手段防止下载中途损坏或者内容被改过。Windows下用PowerShell算MD5非常方便Get-FileHash .\pdfium-win-x64.tgz -Algorithm MD5算出来的值如果和发布者提供的一致再进入解压步骤。这一步花不了十秒钟但能省掉后面排查DLL损坏的麻烦。2.2 解压后的目录结构与作用分析tgz本质是tar.gz打包格式。Windows 10 1803之后的系统自带tar.exe直接命令行解压就行tar -xzf pdfium-win-x64.tgz如果你机器上没有tar命令用7-Zip也能解它会自动识别两层压缩格式。解压之后典型的结构是这样的路径作用bin/x64/pdfium.dllPDFium官方x64动态库核心渲染引擎src/PdfiumTypes.pasDelphi类型定义比如FPDF_DOCUMENT、FPDF_PAGE等指针类型src/PdfiumApi.pasDelphi接口翻译单元把C函数翻译成Pascal可调用的函数或过程src/PdfiumLib.pas二次封装单元通常是作者封装好的更友好的类demo/示例工程一般包含加载、渲染、文本提取等demo代码docs/使用说明、API索引、免责声明等我拿到这个结构后通常不会直接用PdfiumLib.pas里的二次封装类因为第三方封装有时会把接口改成作者自己的习惯不如直接基于PdfiumTypes.pas和PdfiumApi.pas写自己的封装层。这样遇到问题我能精确定位到具体API。2.3 把你的Delphi 12.3工程指向这些文件集成步骤不复杂但路径处理容易出错把pdfium.dll拷贝到工程输出目录。多数情况下直接放在Win64\Debug或Win64\Release下和exe同目录。在Delphi 12.3里通过Project Options Delphi Compiler Search Path把src目录加进去这样PdfiumTypes.pas这些单元就能被引用。如果demo工程要编译先确认Project Options Target Platforms选的是64-bit Windows否则编译出来是32位程序加载x64的dll会直接失败。这里有个容易忽略的细节Delphi 12.3默认的工程平台是32位新建工程后要手动把目标平台切到64位。很多初学者解压完包写了几行代码一运行报BadImageFormatException或者无法加载DLL十有八九是目标平台没切。3. PDFium接口封装的关键实现3.1 用动态加载还是静态链接我选了动态PDFium官方库有两种接入方式静态链接编译时引用pdfium.lib和动态加载运行时LoadLibrary。在Delphi里我强烈建议动态加载。原因有三静态链接要在Delphi里处理.lib文件虽然可以借助工具把pdfium.lib转成Delphi能用的导入库但步骤繁琐。动态加载可以用LoadLibrary判断dll是否存在、版本是否合适集成失败时能给用户友好的提示而不是程序启动就崩。动态加载更方便以后替换PDFium版本只要接口不变换一个dll文件就行。动态加载的核心思路是把PDFium的C接口函数逐个用GetProcAddress取出来赋值给Delphi函数指针变量。3.2 初始化与文档加载FPDF_InitLibrary与FPDF_LoadDocumentPDFium的接口翻译难点主要在类型对应。C语言里的FPDF_DOCUMENT其实就是一个指向内部结构体的指针在Delphi里统一声明成Pointer即可。先定义函数指针类型type FPDF_DOCUMENT Pointer; FPDF_PAGE Pointer; FPDF_BITMAP Pointer; FPDF_TEXTPAGE Pointer; TFPDF_InitLibrary procedure; cdecl; TFPDF_LoadDocument function(file_path: PAnsiChar; password: PAnsiChar): FPDF_DOCUMENT; cdecl; TFPDF_GetPageCount function(document: FPDF_DOCUMENT): Integer; cdecl; TFPDF_LoadPage function(document: FPDF_DOCUMENT; page_index: Integer): FPDF_PAGE; cdecl; TFPDF_GetPageWidthF function(page: FPDF_PAGE): Single; cdecl; TFPDF_GetPageHeightF function(page: FPDF_PAGE): Single; cdecl; TFPDF_CloseDocument procedure(document: FPDF_DOCUMENT); cdecl; TFPDF_ClosePage procedure(page: FPDF_PAGE); cdecl; TFPDFBitmap_Create function(width, height, alpha: Integer): FPDF_BITMAP; cdecl; TFPDFBitmap_FillRect procedure(bitmap: FPDF_BITMAP; left, top, width, height: Integer; color: Cardinal); cdecl; TFPDFBitmap_Destroy procedure(bitmap: FPDF_BITMAP); cdecl; TFPDF_RenderPageBitmap procedure(bitmap: FPDF_BITMAP; page: FPDF_PAGE; start_x, start_y, size_x, size_y, rotate, flags: Integer); cdecl; TFPDFBitmap_GetBuffer function(bitmap: FPDF_BITMAP): Pointer; cdecl; TFPDFBitmap_GetStride function(bitmap: FPDF_BITMAP): Integer; cdecl;然后写一个加载dll并取函数地址的初始化过程var PdfiumDllHandle: THandle; FPDF_InitLibrary: TFPDF_InitLibrary; FPDF_LoadDocument: TFPDF_LoadDocument; FPDF_GetPageCount: TFPDF_GetPageCount; FPDF_LoadPage: TFPDF_LoadPage; FPDF_GetPageWidthF: TFPDF_GetPageWidthF; FPDF_GetPageHeightF: TFPDF_GetPageHeightF; FPDF_CloseDocument: TFPDF_CloseDocument; FPDF_ClosePage: TFPDF_ClosePage; FPDFBitmap_Create: TFPDFBitmap_Create; FPDFBitmap_FillRect: TFPDFBitmap_FillRect; FPDFBitmap_Destroy: TFPDFBitmap_Destroy; FPDF_RenderPageBitmap: TFPDF_RenderPageBitmap; FPDFBitmap_GetBuffer: TFPDFBitmap_GetBuffer; FPDFBitmap_GetStride: TFPDFBitmap_GetStride; procedure LoadPdfium(const DllPath: string); begin PdfiumDllHandle : LoadLibrary(PChar(DllPath)); if PdfiumDllHandle 0 then raise Exception.Create(加载pdfium.dll失败: DllPath); FPDF_InitLibrary : GetProcAddress(PdfiumDllHandle, FPDF_InitLibrary); FPDF_LoadDocument : GetProcAddress(PdfiumDllHandle, FPDF_LoadDocument); FPDF_GetPageCount : GetProcAddress(PdfiumDllHandle, FPDF_GetPageCount); FPDF_LoadPage : GetProcAddress(PdfiumDllHandle, FPDF_LoadPage); FPDF_GetPageWidthF : GetProcAddress(PdfiumDllHandle, FPDF_GetPageWidthF); FPDF_GetPageHeightF : GetProcAddress(PdfiumDllHandle, FPDF_GetPageHeightF); FPDF_CloseDocument : GetProcAddress(PdfiumDllHandle, FPDF_CloseDocument); FPDF_ClosePage : GetProcAddress(PdfiumDllHandle, FPDF_ClosePage); FPDFBitmap_Create : GetProcAddress(PdfiumDllHandle, FPDFBitmap_Create); FPDFBitmap_FillRect : GetProcAddress(PdfiumDllHandle, FPDFBitmap_FillRect); FPDFBitmap_Destroy : GetProcAddress(PdfiumDllHandle, FPDFBitmap_Destroy); FPDF_RenderPageBitmap : GetProcAddress(PdfiumDllHandle, FPDF_RenderPageBitmap); FPDFBitmap_GetBuffer : GetProcAddress(PdfiumDllHandle, FPDFBitmap_GetBuffer); FPDFBitmap_GetStride : GetProcAddress(PdfiumDllHandle, FPDFBitmap_GetStride); end;注意PDFium所有导出函数都是cdecl调用约定这块不能写错。写成了stdcall轻则参数错乱重则直接访问违例。3.3 页面渲染到BitmapFPDF_RenderPageBitmap参数解释渲染页面到Bitmap是PDFium最常用的功能。核心流程分四步初始化库、加载文档、取页面、创建Bitmap并渲染。完整示例function RenderPdfPageToBitmap(const AFileName: string; APageIndex: Integer; ADpi: Integer): TBitmap; var doc: FPDF_DOCUMENT; page: FPDF_PAGE; bitmap: FPDF_BITMAP; w, h, stride: Integer; buffer: PByte; scale: Single; begin Result : nil; FPDF_InitLibrary; try doc : FPDF_LoadDocument(PAnsiChar(AnsiString(AFileName)), nil); if doc nil then raise Exception.Create(无法加载PDF文件); try page : FPDF_LoadPage(doc, APageIndex); if page nil then raise Exception.Create(无法加载PDF页面); try // 页面尺寸单位是点point1点 1/72英寸 scale : ADpi / 72.0; w : Round(FPDF_GetPageWidthF(page) * scale); h : Round(FPDF_GetPageHeightF(page) * scale); bitmap : FPDFBitmap_Create(w, h, 0); try FPDFBitmap_FillRect(bitmap, 0, 0, w, h, $FFFFFFFF); FPDF_RenderPageBitmap(bitmap, page, 0, 0, w, h, 0, 0); stride : FPDFBitmap_GetStride(bitmap); buffer : FPDFBitmap_GetBuffer(bitmap); Result : TBitmap.Create; Result.PixelFormat : pf32bit; Result.Width : w; Result.Height : h; // PDFium的缓冲区是BGRX格式需要按行拷贝到TBitmap // 注意Y方向可能上下颠倒如果渲染结果颠倒把Y改成h-1-Y for Y : 0 to h - 1 do begin DestLine : Result.ScanLine[Y]; SrcLine : PByte(NativeUInt(buffer) NativeUInt((h - 1 - Y) * stride)); Move(SrcLine^, DestLine^, w * 4); end; finally FPDFBitmap_Destroy(bitmap); end; finally FPDF_ClosePage(page); end; finally FPDF_CloseDocument(doc); end; finally FPDF_DestroyLibrary; end; end;这段代码有几点要解释FPDFBitmap_Create第三个参数传0表示不透明格式BGRx也就是每个像素4字节但没有Alpha通道。这样填充白色背景后PDF空白区域显示为纯白色。FPDFBitmap_GetBuffer返回的缓冲区每行可能有对齐填充不能想当然用w*4去跳行要用GetStride返回的步长。PDFium的Bitmap内部默认是自底向上存储而Delphi的TBitmap.ScanLine[0]是顶部第一行所以拷贝时做了一次上下翻转。注意如果你发现渲染出来的图片上下颠倒原因就是Y轴方向处理反了把h-1-Y改成Y试一下二选一总有一个是对的。3.4 文本提取FPDFText系列接口除了渲染成图片PDF很多场景还需要提取文字。PDFium的文本提取接口不在主接口里而是通过FPDFText_LoadPage把一个页面加载成文本页对象再逐字符读取。接口翻译type TFPDFText_LoadPage function(page: FPDF_PAGE): FPDF_TEXTPAGE; cdecl; TFPDFText_CountChars function(text_page: FPDF_TEXTPAGE): Integer; cdecl; TFPDFText_GetText function(text_page: FPDF_TEXTPAGE; start_index, count: Integer; result_buffer: PByte; buffer_size: Integer): Integer; cdecl; TFPDFText_ClosePage procedure(text_page: FPDF_TEXTPAGE); cdecl;提取整页文本的函数function GetPdfPageText(const AFileName: string; APageIndex: Integer): string; var doc: FPDF_DOCUMENT; page: FPDF_PAGE; tp: FPDF_TEXTPAGE; count: Integer; buf: TBytes; len: Integer; begin Result : ; FPDF_InitLibrary; try doc : FPDF_LoadDocument(PAnsiChar(AnsiString(AFileName)), nil); if doc nil then Exit; try page : FPDF_LoadPage(doc, APageIndex); if page nil then Exit; try tp : FPDFText_LoadPage(page); if tp nil then Exit; try count : FPDFText_CountChars(tp); if count 0 then Exit; // FPDFText_GetText返回UTF-16LE缓冲区末尾带双字节0结束符 SetLength(buf, (count 1) * 2); len : FPDFText_GetText(tp, 0, count, buf[0], Length(buf)); if len 0 then Result : PWideChar(buf[0]); finally FPDFText_ClosePage(tp); end; finally FPDF_ClosePage(page); end; finally FPDF_CloseDocument(doc); end; finally FPDF_DestroyLibrary; end; end;这里有个比较隐蔽的点FPDFText_GetText返回的result_buffer即使传入的是PByte实际内容是UTF-16LE的字符串。所以在Delphi里可以直接用PWideChar去读。缓冲区长度的计算要留出结束符的位置否则可能触发越界。另外PDF文件中没有文本图层的情况很常见。扫描版的PDF本质上是图片套了个PDF壳FPDFText_CountChars返回0这是正常的不代表API调用错误。遇到这种文件只能走OCR路线或者在UI层提示当前PDF无可提取文本。4. 实用功能落地缩略图、批量导出PDF页面的实战代码4.1 做一个PDF转图片的控制台示例渲染单页的代码有了接下来就能扩展成批量导出。我的做法是做一个控制台程序输入一个PDF文件路径和输出目录程序自动把每一页都导出成PNG。PDFium本身不编码PNG/JPG所以把渲染后的位图保存成图片还是得靠VCL的TBitmap配合TPngImage或者干脆用TBitmap.SaveToFile输出BMP。我这里推荐用TPngImage保存成PNG体积可控适合Web端展示。批量示例逻辑procedure ExportPdfToImages(const APdfPath, AOutputDir: string; ADpi: Integer); var doc: FPDF_DOCUMENT; pageCount, I, w, h, stride: Integer; page: FPDF_PAGE; bitmap: FPDF_BITMAP; buffer, srcLine, destLine: PByte; scale: Single; bmp: TBitmap; png: TPNGImage; Y: Integer; begin FPDF_InitLibrary; try doc : FPDF_LoadDocument(PAnsiChar(AnsiString(APdfPath)), nil); if doc nil then raise Exception.Create(无法加载PDF); try pageCount : FPDF_GetPageCount(doc); scale : ADpi / 72.0; for I : 0 to pageCount - 1 do begin page : FPDF_LoadPage(doc, I); if page nil then Continue; try w : Round(FPDF_GetPageWidthF(page) * scale); h : Round(FPDF_GetPageHeightF(page) * scale); bitmap : FPDFBitmap_Create(w, h, 0); try FPDFBitmap_FillRect(bitmap, 0, 0, w, h, $FFFFFFFF); FPDF_RenderPageBitmap(bitmap, page, 0, 0, w, h, 0, 0); stride : FPDFBitmap_GetStride(bitmap); buffer : FPDFBitmap_GetBuffer(bitmap); bmp : TBitmap.Create; try bmp.PixelFormat : pf32bit; bmp.Width : w; bmp.Height : h; for Y : 0 to h - 1 do begin destLine : PByte(bmp.ScanLine[Y]); srcLine : PByte(NativeUInt(buffer) NativeUInt((h - 1 - Y) * stride)); Move(srcLine^, destLine^, w * 4); end; png : TPNGImage.Create; try png.Assign(bmp); png.SaveToFile(Format(%s\page_%.3d.png, [AOutputDir, I 1])); finally png.Free; end; finally bmp.Free; end; finally FPDFBitmap_Destroy(bitmap); end; finally FPDF_ClosePage(page); end; end; finally FPDF_CloseDocument(doc); end; finally FPDF_DestroyLibrary; end; end;从代码可以看出批量导出实际上就是单页渲染外面套一个for循环。但要注意很多PDF第一页是封面第二页是版权页真正有内容的页面从中间才开始如果有筛选需求可以在循环里加判断条件。4.2 渲染分辨率的选择与DPI计算PDF页面尺寸的单位是点point1点等于1/72英寸。所以要得到指定DPI的位图尺寸计算公式是像素宽 页面点数宽 × (目标DPI / 72)比如一个A4页面595点×842点按144 DPI渲染就是595 * (144/72) 1190像素宽842 * 2 1684像素高。这个公式很关键别硬编码缩放系数否则换一个PDF就失真了。实际开发中缩略图场景一般用72 DPI到96 DPI就够屏幕预览用144 DPI如果要打印或清晰存档用200 DPI到300 DPI。DPI越高渲染耗时和内存占用是平方级增长的一个300 DPI的A4页面位图是2480×3508像素占内存约28MB批量处理时要注意内存回收。我在这个项目里给导出功能做了两个预设Screen144 DPI和Print300 DPI用户通过命令行参数选择。4.3 批量任务中的资源管理与进度反馈批量处理PDF最忌讳的是每页都重新LoadLibrary初始化PDFium。PDFium的FPDF_InitLibrary和FPDF_DestroyLibrary应该只在程序启动和结束时调用一次别放在单页函数里反复调用。我第一次写的时候图省事直接把初始化放进循环里结果600页的PDF跑了一分多钟后来改成一次性初始化速度提升明显。进度反馈方面因为PDFium的渲染接口是同步阻塞的无法在渲染过程中取消或暂停。如果任务时间很长界面层只能靠TThread把渲染丢到后台线程然后通过Synchronize或者Queue进度事件回传当前页码。控制台程序简单些直接WriteLn输出进度即可。另外批量导出PNG时文件名建议用补零方式比如page_001.png到page_600.png。否则按文件名排序会变成page_1、page_10、page_100顺序全乱。5. 常见问题与排查实录5.1 为什么总是无法定位程序输入点这是我在集成过程中遇到最频繁的问题。报错通常是无法定位程序输入点 FPDF_InitLibrary 于动态链接库 pdfium.dll 上。出现这个报错99%的原因是pdfium.dll版本和接口翻译单元不匹配。PDFium官方接口一直在演进有些旧接口在新版本中改名或者被移除了。比如旧版本的FPDF_GetPageWidth返回Double新版本增加了FPDF_GetPageWidthF返回Single如果你翻译单元里调用的函数在新dll里不存在就会出现无法定位程序输入点。排查思路先确认tgz包里的pdfium.dll版本查看压缩包内的docs或者dll文件属性。再确认接口翻译单元对应的PDFium版本。如果是自己翻译接口使用GetProcAddress后判断返回是否为nil为nil就说明函数不存在。正确的集成方法是接口翻译单元要和dll版本配套尽量用作者已经验证过的组合不要混搭不同版本。5.2 64位指针在Delphi中的类型处理PDFium接口里大量使用指针比如FPDF_DOCUMENT、FPDF_PAGE。在32位时代Pointer是4字节64位时代Pointer是8字节。Delphi的Pointer类型能自动适配但如果你声明成了Cardinal或者Integer来存指针高32位会被截断调用时访问违例。我见过一个经典错误用var Handle: Cardinal存LoadLibrary的返回值。在Win64下HMODULE是64位Cardinal根本放不下。正确做法是用THandle或者NativeUInt。var PdfiumDllHandle: THandle; // 在Win64下是64位正确同理FPDFBitmap_GetBuffer返回的指针在做指针运算时要转成NativeUInt再做加减。直接buffer stride * Y这样的写法会触发编译错误因为Delphi不允许对无类型指针做算术运算。5.3 渲染结果全黑或空白的原因渲染出来全黑通常是因为FPDFBitmap_Create的格式参数传错了。如果传1表示Alpha格式缓冲区里每个像素的Alpha通道参与计算而填充白色背景时用的$FFFFFFFF在BGRx格式下没问题但在Alpha格式下$FFFFFFFF是白色且不透明理论上也不会全黑。更常见的情况是忘了填充背景色然后直接渲染。PDF页面如果本身没有绘制背景默认就是透明或黑色缓冲区未初始化就有可能出现黑色。所以我在所有渲染逻辑里都先FPDFBitmap_FillRect(bitmap, 0, 0, w, h, $FFFFFFFF)强制把背景刷成白色再渲染PDF内容。渲染成空白页还有一个容易忽略的原因DPI设置太大位图尺寸超过系统限制或者内存不足导致TBitmap.Create失败。300 DPI的页面位图已经很大了可以适当降低DPI测试。5.4 内存与句柄泄漏的检查方法PDFium的接口成对出现加载和释放必须一一对应FPDF_LoadDocument对应FPDF_CloseDocumentFPDF_LoadPage对应FPDF_ClosePageFPDFBitmap_Create对应FPDFBitmap_DestroyFPDFText_LoadPage对应FPDFText_ClosePage如果只加载不释放长时间跑批量任务内存会持续增长最后OOM崩溃。我在封装时用了一个土办法加保护所有对象创建后立即挂在try/finally里保证异常路径也能释放。这不需要动态检测工具代码写规范了泄漏概率就大幅降低。6. 一些不吐不快的实战心得这次集成PDFium最深的体会是第三方开源库的价值不在于功能多强而在于它能不能安静地融进现有工程。PDFium只有一个dll不污染IDE不依赖控件注册对老项目友好程度远超商业组件。如果要给PDFium集成排个优先级第一优先是渲染页面到图像这个应用面最广第二是文本提取很多检索功能依赖它第三才是元数据读取和高级特性。初学的时候不要一上来就追求封装一个万能类先把单页渲染跑通再逐步扩展这个路径最稳。最后分享一个小技巧如果你需要动态加载PDFium但又不确定用户机器上dll放在哪可以在初始化时依次查找三个位置exe同目录、程序工作目录、系统PATH环境变量。找到第一个能成功LoadLibrary的路径就用它同时在日志里打出最终加载的dll全路径。这个习惯帮我避开了很多线上环境本地好好的用户机器上跑不起来的怪问题。本文还有配套的精品资源点击获取
返回列表