从 Quick PDF Library 和 PdfiumViewer 迁移到 sgcPDF

Foxit 已于 2021 年 12 月 13 日停止 Quick PDF Library,PdfiumViewer 仓库自 2019 年 8 月 2 日起为只读。sgcPDF 可以同时取代两者:编译进 Delphi exe 的纯 Pascal,以及面向 WinForms 的托管 C#,无需部署 PDF DLL。本套件把 Quick PDF Library 的函数和 PdfiumViewer 的类对应到 sgcPDF,代码来自两个能够编译并通过自测的示例。

建立在已停止更新的 PDF 库之上的应用

两种常见情况:用 Quick PDF Library 创建、合并、填写和渲染 PDF 文件的 Delphi 应用,以及用 PdfiumViewer 显示 PDF 文件的 WinForms 应用。

从 Quick PDF Library(Delphi)迁移

Quick PDF Library(Debenu Quick PDF Library,后来的 Foxit Quick PDF Library)以 DLL、ActiveX 或 LIB 形式提供,带解锁密钥,并围绕一个拥有"当前选中"文档、页面、字体和图像的对象工作。sgcPDF 是一组带对象模型的 Pascal 单元:TsgcPDFDocument、TsgcPDFPage、TsgcPDFEditor 和 TsgcPDFAcroForm,支持 Delphi 7 到 Delphi 13。

从 PdfiumViewer(WinForms)迁移

PdfiumViewer 把原生 PDFium 库封装为 PdfViewer 控件、PdfRenderer 和 PdfDocument。它可以显示、搜索、渲染和打印 PDF 文件。sgcPDF 用一个托管控件 TsgcPDFViewerControl 取代它们,并补上 PdfiumViewer 从未提供的功能:创建 PDF 文件、填写表单、添加批注和保存更改。

Quick PDF Library。Foxit 公告(2026-10-20 阅读)写道:"自 2021 年 12 月 13 日起,Foxit 将停止以下产品",列表中的第一个产品就是 Foxit Quick PDF Library (All Platforms)。拥有有效维护与支持合同的客户可获得支持至 2022 年 12 月 13 日:"在该日期之后,本产品将进入终止支持状态,您将不再获得支持。"Foxit 推荐的替代品是其面向 Windows、Mac、Linux、Android 和 iOS 的 PDF SDK、其 ActiveX SDK 和其 .NET SDK 套件。

PdfiumViewer。GitHub 仓库(2026-10-20 阅读)显示:"此仓库已于 2019 年 8 月 2 日被所有者归档,现在为只读。"其 README 解释道:"我已经很长时间没有真正花时间在这个项目上了",以及"我无法妥善地支持这个项目。"它还说明:"PdfiumViewer 控件需要原生 PDFium 库,这些库不包含在 PdfiumViewer NuGet 包中。"该 NuGet 包最后更新于 2017 年 11 月 6 日(版本 2.13.0),目标框架为 net20。

一个 PDF 库,附带源代码

sgcPDF 是 sgcUI 的 PDF 产品。它可在 Delphi 和 .NET 中创建、编辑、填写、签名、加密、渲染和显示 PDF 文件。

无需部署 PDF DLL

在 Delphi 中,sgcPDF 单元直接编译进 exe:没有需要部署和注册的 DLL、ActiveX 或 LIB,也没有解锁密钥调用。在 .NET 中,sgcPDF 是托管 C#,因此没有 pdfium.dll,也无需选择 x86 或 x64 原生包:示例以 AnyCPU 运行。

对象模型

每个调用都接收页面对象或索引,因此 Quick PDF Library 的"当前选中"状态不复存在。拼版使用矢量表单 XObject(AddPageAsForm、DrawForm、DrawFormInRect),表单使用 AcroForm,加密最高支持 AES-256(emAES_256)。

可以编辑的查看器

TsgcPDFViewerControl 可按宽度、整页或高度适应,旋转视图,搜索,并通过对话框打印;在 .NET 中它像 PdfiumViewer 一样提供 PrintDocument。它还能填写表单字段(FormFilling)、用鼠标添加批注(AnnotationTool)并选择文本。侧边视图:TsgcPDFThumbnailView、TsgcPDFOutlineView、TsgcPDFCommentsView。

sgcPDF 单独销售,Delphi 和 C++ Builder 单个开发者版本起价 €249,.NET 版本起价 €249,含完整源代码和 1 年更新,同时包含在 sgcUI Enterprise 和 All-Access 套装中。.NET 包还以免费 Community 包的形式发布在 NuGet 上,即 esegece.sgcPDF.Community 和 esegece.sgcPDF.WinForms.Community,功能相同。请参阅 sgcPDF 产品页面。

sgcPDF 中的 Quick PDF Library 函数

相关单元是 sgcPDF_Types、sgcPDF_Fonts 和 sgcPDF_Document,需要时再加上 sgcPDF_Editor、sgcPDF_Encryption、sgcPDF_Renderer_GDI 和 sgcPDF_Export。sgcPDF 的页面索引从 0 开始。

Quick PDF Library sgcPDF
UnlockKey代码中无需调用
NewDocumentTsgcPDFDocument.Create,然后 AddPage(新文档在添加之前没有页面)
NewPage, SetPageDimensions, SetPageSizeTsgcPDFDocument.AddPage(AWidth, AHeight): TsgcPDFPage, InsertPage(AIndex, AWidth, AHeight)
LoadFromFile, LoadFromStringTsgcPDFDocument.LoadFromFile, LoadFromStream, LoadFromBytes
带密码的 LoadFromFile在 LoadFromFile 之前调用 TsgcPDFDocument.SetPassword(APassword)
SaveToFile, SaveToStringTsgcPDFDocument.SaveToFile, SaveToStream, SaveIncremental
PageCountTsgcPDFDocument.PageCount;仅针对文件时用 sgcPDFGetPageCount(AFileName)
SelectPageTsgcPDFDocument.Pages[AIndex](从 0 开始,默认属性),GetPage(AIndex)
SetInformationTsgcPDFDocument.Info.Title, .Author, .Subject, .Keywords, .Creator, .Producer
AddStandardFont + SelectFont + SetTextSize + DrawTextTsgcPDFPage.DrawText(AText, X, Y, AFont, AFontSize),搭配 TsgcPDFStandardFont(sfHelvetica、sfTimesRoman、sfCourier 及其粗体和斜体变体)
SetTextColor + DrawTextTsgcPDFPage.DrawTextColor(AText, X, Y, AFont, AFontSize, R, G, B)(分量取值 0 到 1)
AddTrueTypeFont + DrawTextTsgcPDFDocument.AddTrueTypeFont(AFontName, ABold, AItalic): Integer(或 AddTrueTypeFontFromFile),然后 TsgcPDFPage.DrawTextTT
DrawLine, DrawBoxTsgcPDFPage.DrawLine(X1, Y1, X2, Y2, ALineWidth), DrawRectangle(X, Y, W, H, ALineWidth), DrawRectangleFill
AddImageFromFile + SelectImage + DrawImageTsgcPDFDocument.AddImage(AFileName): Integer + TsgcPDFPage.DrawImageFromIndex(AIndex, X, Y, W, H),或 DrawImageFile(AFileName, X, Y, W, H)
GetPageTextTsgcPDFDocument.ExtractTextW(APageIndex), ExtractText, FindText; TsgcPDFEditor.ExtractAllText, SearchText
AddToFileList + MergeFileListsgcPDFMergeFiles([...], AOutputFile)
MergeDocumentTsgcPDFDocument.AppendDocument(ADoc), TsgcPDFEditor.MergeFrom(AFileName)
ExtractPageRanges, ExtractFilePagessgcPDFExtractPages(AInputFile, AOutputFile, ARange)(从 1 开始的范围字符串,如 '1,3,5-9'),TsgcPDFEditor.ExtractPages(ARange, AOutputFileName)
DeletePagesTsgcPDFDocument.DeletePages(ARange), DeletePage(AIndex)
RotatePageTsgcPDFEditor.RotatePage(AIndex, ADegrees)
SetTransparency + DrawText(水印)TsgcPDFEditor.AddWatermarkAllPages(TsgcPDFWatermarkOptions), AddWatermark, AddTransparentText
在每一页上 DrawText(印章、页眉、页脚)TsgcPDFEditor.AddTextColor, AddHeaderText, AddFooterText, AddPageNumbers
RenderPageToDCTsgcPDFRendererGDI.RenderPage(ADC, ADocument.Parser, APage.PageDict, AWidth, AHeight);在 VCL 查看器中用 TsgcPDFViewerControl.RenderPageToBitmap
RenderPageToFileTsgcPDFExporter.ExportPage(ADocument, APageIndex, AFileName)(BMP、PNG、JPEG),sgcPDFPageToImage(APDFFile, AImageFile, APageIndex, ADPI)
FormFieldCount, FindFormFieldByTitleTsgcPDFDocument.AcroForm.FieldCount, AcroForm.FindField(AName)
SetFormFieldValue, SetFormFieldValueByTitleAcroForm.FillField(AName, AValue), AcroForm.SetCheckBox(AName, AChecked), TsgcPDFEditor.FillFormField
GetFormFieldValueAcroForm.GetFieldValue(AName)
NewFormFieldAcroForm.AddTextField, AddCheckBox, AddComboBox, AddListBox, AddRadioGroup + AddRadioButton, AddButton, AddSignatureField
FlattenFormFieldAcroForm.FlattenFields, TsgcPDFEditor.FlattenForm
EncryptTsgcPDFDocument.SetEncryption(AMethod, AUserPassword, AOwnerPassword),取值 emAES_256、emAES_128、emRC4_128 或 emRC4_40
CapturePageTsgcPDFDocument.AddPageAsForm(ASource, APageIndex): Integer(带页面框、矩形或批注的重载)
DrawCapturedPageTsgcPDFPage.DrawForm(AFormIndex, X, Y, AScaleX, AScaleY, ARotation), DrawFormInRect(AFormIndex, ATarget, AKeepAspect, ARotation), DrawFormMatrix
GetPageBoxTsgcPDFPage.GetPageBox(ABox) (ppbTrimBox, ppbBleedBox, ...), TsgcPDFDocument.GetFormBBox
NewOutlineTsgcPDFEditor.AddBookmark(ATitle, APageIndex), AddBookmarkChild
AddLinkToPageTsgcPDFEditor.AddLinkAnnotation(APageIndex, ALeft, ABottom, ARight, ATop, ADestPage)
AddNoteAnnotationTsgcPDFEditor.AddTextAnnotation(APageIndex, ALeft, ABottom, ARight, ATop, AText)
SignFileTsgcPDFEditor.SignDocument(AOutputFile, ACertSubjectName, AReason, ALocation)
PrintDocumentVCL 查看器 TsgcPDFViewerControl.Print(AFromPage, AToPage, ATitle) 不弹对话框直接打印,PrintDialog(ATitle) 显示打印机对话框(页面范围、份数)后打印。FireMonkey 查看器 TsgcPDFViewerFMX 在 Windows 和 macOS 上有同样的两个方法。
SetMeasurementUnits(0), SetOrigin(0)默认且唯一的坐标系:以点为单位,原点在左下角;辅助函数 sgcPDFMMToPoints、sgcPDFRectMM

Delphi 查看器中还有:适应高度缩放(TsgcPDFViewerControl 的 zmFitHeight、TsgcPDFViewerFMX 的 fzmFitHeight、TsgcPDFViewer 的 ZoomFitHeight)和视图旋转(ViewRotation、RotateLeft、RotateRight),它只旋转视图,不改变文档。

sgcPDF 中常见的 Quick PDF Library 任务

每段摘录都复制自 MigrateFromQuickPDF 演示,该演示可用 Delphi 7(Win32)和 Delphi 13(Win32 与 Win64)编译,并通过自测。注释写明了每一行所取代的 Quick PDF Library 调用。

uses 子句

Quick PDF Library 的导入单元和 DLL 都不再需要。这些单元涵盖演示的所有任务:

frmMain.pas
uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, ShellAPI,
  // sgc
  sgcPDF_Types, sgcPDF_Fonts, sgcPDF_Document, sgcPDF_Encryption,
  sgcPDF_Editor, sgcPDF_Renderer_GDI;

创建包含文本、方框和图像的文档

取代 NewDocument、SetInformation、AddStandardFont、SelectFont、SetTextSize、SetTextColor、DrawText、DrawBox、AddImageFromFile、SelectImage、DrawImage 和 SaveToFile。

frmMain.pas
oDoc := TsgcPDFDocument.Create;
try
  // SetInformation(1, ...) / SetInformation(2, ...)
  oDoc.Info.Title := 'Created with sgcPDF';
  oDoc.Info.Author := 'Migration sample';
  // NewDocument creates page 1, sgcPDF adds every page explicitly
  oPage := oDoc.AddPage(CS_A4_WIDTH, CS_A4_HEIGHT);
  // AddStandardFont(4) + SelectFont + SetTextSize(28) + DrawText
  oPage.DrawText('Hello from sgcPDF', 72, 760, sfHelveticaBold, 28);
  // SetTextColor(r, g, b) + DrawText
  oPage.DrawTextColor('No DLL, pure Pascal.', 72, 725, sfHelvetica, 14,
    0.1, 0.3, 0.7);
  // SetLineColor + SetLineWidth + DrawBox(x, y, w, h, 0)
  oPage.DrawRectangle(72, 520, 260, 120, 1.5);
  // AddImageFromFile + SelectImage + DrawImage(x, y, w, h)
  vImage := oDoc.AddImage(OutFile('logo.bmp'));
  oPage.DrawImageFromIndex(vImage, 102, 540, 200, 80);
  oDoc.SaveToFile(OutFile('created.pdf'));
finally
  oDoc.Free;
end;

提取页面文本

取代 LoadFromFile、PageCount、SelectPage 和 GetPageText。请注意页面索引从 0 开始。

frmMain.pas
oDoc.LoadFromFile(OutFile('input_a.pdf'));
Log('Extract text: input_a.pdf has ' + IntToStr(oDoc.PageCount) +
  ' pages');
// SelectPage(2) + GetPageText(0): sgcPDF page indexes are 0-based
vText := string(oDoc.ExtractTextW(1));
Log(vText);

合并与拆分

取代 AddToFileList 加 MergeFileList,以及 ExtractPageRanges 或 ExtractFilePages。范围字符串仍从 1 开始。

frmMain.pas
sgcPDFMergeFiles([OutFile('input_a.pdf'), OutFile('input_b.pdf')],
  OutFile('merged.pdf'));
frmMain.pas
// 1-based range string, the same syntax as Quick PDF Library
sgcPDFExtractPages(OutFile('merged.pdf'), OutFile('pages_2-3.pdf'), '2-3');
// one file per page: split
sgcPDFExtractPages(OutFile('merged.pdf'), OutFile('page_4.pdf'), '4');

为每一页添加水印和印章

取代在页面循环中使用的 SelectPage、SetTransparency、SetTextColor 和 DrawText。

frmMain.pas
oEditor.LoadFromFile(OutFile('input_a.pdf'));
vOptions.Text := 'CONFIDENTIAL';
vOptions.Font := sfHelveticaBold;
vOptions.FontSize := 72;
vOptions.Rotation := 45;
vOptions.Opacity := 0.3;
vOptions.R := 0.8;
vOptions.G := 0.1;
vOptions.B := 0.1;
oEditor.AddWatermarkAllPages(vOptions);
// a stamp in the top right corner of every page
for i := 0 to oEditor.Document.PageCount - 1 do
  oEditor.AddTextColor(i, 'APPROVED', 440, 800, sfHelveticaBold, 16,
    0.0, 0.5, 0.0);
oEditor.SaveToFile(OutFile('watermarked.pdf'));

将页面渲染到设备上下文

取代 RenderPageToDC。渲染器接收已加载文档的 Parser 和页面的 PageDict。

frmMain.pas
oDoc.LoadFromFile(OutFile('created.pdf'));
oPage := oDoc.Pages[0];
// 96 DPI: points are 1/72 inch
vScale := 96 / 72;
vW := Round(oPage.Width * vScale);
vH := Round(oPage.Height * vScale);
oBitmap.PixelFormat := pf24bit;
oBitmap.Width := vW;
oBitmap.Height := vH;
oBitmap.Canvas.Brush.Color := clWhite;
oBitmap.Canvas.FillRect(Rect(0, 0, vW, vH));
oRenderer.RenderPage(oBitmap.Canvas.Handle, oDoc.Parser, oPage.PageDict,
  vW, vH);
GdiFlush;
oBitmap.SaveToFile(OutFile('created_page1.bmp'));

填写表单

取代 FormFieldCount、FindFormFieldByTitle 和 SetFormFieldValue。

frmMain.pas
oDoc.LoadFromFile(OutFile('form.pdf'));
Log('Fill form: form.pdf has ' + IntToStr(oDoc.AcroForm.FieldCount) +
  ' fields');
oDoc.AcroForm.FillField('Customer', 'ACME Corporation');
oDoc.AcroForm.FillField('Amount', '1250.00');
oDoc.AcroForm.SetCheckBox('Paid', True);
oDoc.SaveToFile(OutFile('form_filled.pdf'));

用 AES-256 加密并用密码打开

取代 Encrypt,以及带密码的 LoadFromFile。

frmMain.pas
oDoc.LoadFromFile(OutFile('input_a.pdf'));
oDoc.SetEncryption(emAES_256, CS_USER_PASSWORD, CS_OWNER_PASSWORD);
oDoc.SaveToFile(OutFile('encrypted.pdf'));
frmMain.pas
oDoc.SetPassword(CS_USER_PASSWORD);
oDoc.LoadFromFile(OutFile('encrypted.pdf'));
vText := string(oDoc.ExtractTextW(0));

2-up 拼版

取代 CapturePage、NewPage、SetPageDimensions 和 DrawCapturedPage。AddPageAsForm 把每个源页面保留为表单 XObject,因此文本保持矢量且可选择。

frmMain.pas
oSource.LoadFromFile(OutFile('merged.pdf'));
vHalf := CS_A4_HEIGHT / 2;
oSheet := nil;
for i := 0 to oSource.PageCount - 1 do
begin
  // a landscape A4 sheet for every 2 source pages
  if i mod 2 = 0 then
    oSheet := oTarget.AddPage(CS_A4_HEIGHT, CS_A4_WIDTH);
  // CapturePage
  vForm := oTarget.AddPageAsForm(oSource, i);
  // DrawCapturedPage, scaled to fit the left or the right half
  oSheet.DrawFormInRect(vForm, sgcPDFRect((i mod 2) * vHalf, 0,
    (i mod 2) * vHalf + vHalf, CS_A4_WIDTH), True);
end;
oTarget.SaveToFile(OutFile('imposed_2up.pdf'));

sgcPDF 中的 PdfiumViewer 类

命名空间 esegece.sgcPDF,包 esegece.sgcPDF.WinForms。两个库的页面索引都从 0 开始。

PdfiumViewer sgcPDF .NET
PdfViewer(带工具栏和书签窗格的 UserControl)TsgcPDFViewerControl;自行构建工具栏,书签使用 TsgcPDFOutlineView
PdfRenderer (PdfViewer.Renderer)同一个 TsgcPDFViewerControl
PdfViewer.Document = PdfDocument.Load(path)TsgcPDFViewerControl.LoadFromFile(path)(查看器拥有文档),LoadFromStream,或 Document = vDoc(由您释放)
PdfDocument.Load(path, password)在 LoadFromFile 之前调用 TsgcPDFDocument.SetPassword,然后 TsgcPDFViewerControl.Document = vDoc
PdfDocument.PageCountTsgcPDFViewerControl.PageCount(属性),TsgcPDFDocument.PageCount()(方法)
PdfDocument.PageSizesTsgcPDFDocument.GetPage(i).Width 和 .Height(点)
PdfRenderer.PageTsgcPDFViewerControl.PageIndex、GotoPage、NextPage、PrevPage、FirstPage、LastPage,事件 OnPageChange
PdfViewerZoomMode.FitWidthZoomMode = TsgcPDFViewerZoomMode.zmFitWidth
PdfViewerZoomMode.FitHeightTsgcPDFViewerZoomMode.zmFitHeight
PdfViewerZoomMode.FitBestTsgcPDFViewerZoomMode.zmFitPage
Zoom(倍数,1.0 = 100%)TsgcPDFViewerControl.Zoom(百分比,100 = 100%,会设置 zmCustom),ZoomIn、ZoomOut、zmActualSize、ZoomToRect
PdfRenderer.RotateLeft(), RotateRight()TsgcPDFViewerControl.RotateLeft(), RotateRight()
PdfRenderer.RotationTsgcPDFViewerControl.ViewRotation(TsgcPDFViewerRotation:pvr0、pvr90、pvr180、pvr270);PageRotation(i) 返回当前显示的旋转角度
PdfDocument.Search(text, matchCase, wholeWord) + PdfRenderer.MarkersTsgcPDFViewerControl.FindText(AText, ACaseSensitive, AWholeWord)(高亮并滚动),FindNext、FindPrevious、SearchResults、GetSearchMatch(i)、ClearSearch
PdfDocument.CreatePrintDocument() + PrintDialog 或 PrintPreviewDialogTsgcPDFViewerControl.CreatePrintDocument() 返回 System.Drawing.Printing.PrintDocument,可用于同样的 PrintDialog 和 PrintPreviewDialog 代码。一次调用完成:PrintDialog(ATitle);不弹对话框:Print(AFromPage, AToPage, ATitle)
PdfDocument.Render(page, dpiX, dpiY, forPrinting)TsgcPDFViewerControl.RenderPageToBitmap(APageIndex, ref Bitmap, AZoomPercent, ADPI);没有查看器时用 sgcPDF_Export.sgcPDFPageToImage 或 TsgcPDFExporter.ExportPage
PdfDocument.GetPdfText(page)TsgcPDFDocument.ExtractTextW(APageIndex);带字符位置用 TsgcPDFViewerControl.PageText(i);鼠标选择用 SelectedText、CopyToClipboard
PdfDocument.BookmarksTsgcPDFOutlineView
PdfViewer.LinkClickTsgcPDFViewerControl.OnLinkClick, OpenLinksInBrowser
PdfDocument.Save(path)TsgcPDFDocument.SaveToFile、SaveIncremental(AFileName)(保留原始字节并追加更改)
无对应功能:表单填写TsgcPDFViewerControl.FormFilling = true、OnFieldChange;代码中用 AcroForm.FillField、SetCheckBox、GetFieldValue、FindField
无对应功能:批注TsgcPDFDocument.Annotations.AddText、AddFreeText、AddHighlight、AddStamp、AddLine、AddSquare、AddCircle,然后 AnnotationsChanged();用鼠标则是 AnnotationTool 和 AnnotationEditing
无对应功能:创建 PDFTsgcPDFDocument.AddPage, TsgcPDFPage.DrawText, DrawRectangle, AcroForm.AddTextField, AddCheckBox, SaveToFile

使用 sgcPDF 查看器的 PdfiumViewer 窗体

每段摘录都复制自 MigrateFromPdfiumViewer 演示,该演示可在 Release 下编译,并通过自测。注释引用了每个代码块所取代的 PdfiumViewer 代码。

用一个控件取代 PdfViewer 和 PdfRenderer

MainForm.cs
// PdfiumViewer: PdfViewer (UserControl with toolbar) and its PdfRenderer (the page
// surface, PdfViewer.Renderer) -> one control, TsgcPDFViewerControl
private TsgcPDFViewerControl FViewer;
MainForm.cs
FViewer = new TsgcPDFViewerControl();
FViewer.Parent = pnlViewer;
FViewer.Dock = DockStyle.Fill;
// PdfiumViewer: PdfRenderer.DisplayRectangleChanged / polling PdfRenderer.Page -> OnPageChange
FViewer.OnPageChange = DoPageChange;
// PdfiumViewer: none, PdfiumViewer renders form fields but cannot edit them.
// sgcPDF: the user types into text fields and toggles check boxes in the viewer
FViewer.FormFilling = true;
FViewer.OnFieldChange = DoFieldChange;
// PdfiumViewer: none. sgcPDF: AnnotationTool adds notes, highlights, shapes, ink
// and stamps with the mouse; AnnotationEditing moves, resizes and deletes them
FViewer.AnnotationAuthor = "sgcPDF demo";

打开文件

MainForm.cs
// PdfiumViewer: pdfViewer1.Document = PdfDocument.Load(AFileName)
// sgcPDF: the viewer loads and owns the document; to share a document you load
// yourself, assign it: FViewer.Document = vDoc (you dispose it)
FViewer.LoadFromFile(AFileName);
// PdfiumViewer: PdfViewer.ZoomMode = PdfViewerZoomMode.FitWidth
FViewer.ZoomMode = TsgcPDFViewerZoomMode.zmFitWidth;
// PdfiumViewer: PdfDocument.PageCount -> TsgcPDFViewerControl.PageCount
// (or TsgcPDFDocument.PageCount(), a method)
Log("Opened " + Path.GetFileName(AFileName) + ", " + sgcPDF_RTL.IntToStr(FViewer.PageCount) + " pages");

缩放模式和视图旋转

适应宽度、适应整页和适应高度一一对应。缩放是百分比,而不是倍数。

MainForm.cs
internal void FitWidth()
{
    // PdfiumViewer: PdfViewerZoomMode.FitWidth -> TsgcPDFViewerZoomMode.zmFitWidth
    FViewer.ZoomMode = TsgcPDFViewerZoomMode.zmFitWidth;
}

internal void FitPage()
{
    // PdfiumViewer: PdfViewerZoomMode.FitBest -> TsgcPDFViewerZoomMode.zmFitPage
    FViewer.ZoomMode = TsgcPDFViewerZoomMode.zmFitPage;
}

internal void FitHeight()
{
    // PdfiumViewer: PdfViewerZoomMode.FitHeight -> TsgcPDFViewerZoomMode.zmFitHeight
    FViewer.ZoomMode = TsgcPDFViewerZoomMode.zmFitHeight;
}

internal void RotateLeft()
{
    // PdfiumViewer: PdfRenderer.RotateLeft() -> TsgcPDFViewerControl.RotateLeft()
    // (ViewRotation turns the view only, the document is not changed)
    FViewer.RotateLeft();
}

internal void RotateRight()
{
    // PdfiumViewer: PdfRenderer.RotateRight() -> TsgcPDFViewerControl.RotateRight()
    FViewer.RotateRight();
}

internal void SetZoomPercent(double APercent)
{
    // PdfiumViewer: PdfRenderer.Zoom is a factor (1.0 = 100%);
    // TsgcPDFViewerControl.Zoom is a percentage (100 = 100%) and sets ZoomMode to zmCustom
    FViewer.Zoom = APercent;
}

搜索

MainForm.cs
// PdfiumViewer: PdfDocument.Search(text, matchCase, wholeWord) returns PdfMatches
// and the app adds PdfMarker objects to PdfRenderer.Markers to highlight them.
// sgcPDF: FindText highlights every match and scrolls to the first one,
// FindNext / FindPrevious move between them, GetSearchMatch(i) gives page and chars
if (!FViewer.FindText(AText, false, false))
{
    Log("Not found: " + AText);
    return 0;
}
for (int i = 0; i < FViewer.SearchResults; i++)
{
    TsgcPDFViewerSearchMatch vMatch = FViewer.GetSearchMatch(i);
    Log("Match " + sgcPDF_RTL.IntToStr(i + 1) + ": page " + sgcPDF_RTL.IntToStr(vMatch.PageIndex + 1) +
      ", char " + sgcPDF_RTL.IntToStr(vMatch.FirstChar));
}
return FViewer.SearchResults;

用 CreatePrintDocument 打印

PdfiumViewer 的打印代码保持原样:CreatePrintDocument() 返回一个 PrintDocument,可用于 PrintDialog 或 PrintPreviewDialog。

MainForm.cs
// PdfiumViewer: using (PrintDocument d = PdfDocument.CreatePrintDocument())
//   { PrintDialog dlg = new PrintDialog(); dlg.Document = d; if (dlg.ShowDialog() == OK) d.Print(); }
// sgcPDF: TsgcPDFViewerControl.CreatePrintDocument() returns the same kind of
// System.Drawing.Printing.PrintDocument, so the PdfiumViewer code stays as it is. It honours
// the page range (all, some pages, current page), copies and collate of its PrinterSettings.
// A shorter way: FViewer.PrintDialog(title) shows the dialog and prints in one call, and
// FViewer.Print(from, to, title) prints without a dialog (0-based pages, -1 = last page).
if (FViewer.PageCount == 0)
    return;
using (PrintDocument vDoc = FViewer.CreatePrintDocument())
using (PrintDialog vDialog = new PrintDialog())
{
    vDoc.DocumentName = "PdfiumViewer migration sample";
    vDialog.Document = vDoc;
    vDialog.AllowSomePages = true;
    vDialog.AllowCurrentPage = true;
    vDialog.UseEXDialog = true;
    vDoc.PrinterSettings.MinimumPage = 1;
    vDoc.PrinterSettings.MaximumPage = FViewer.PageCount;
    vDoc.PrinterSettings.FromPage = 1;
    vDoc.PrinterSettings.ToPage = FViewer.PageCount;
    if (vDialog.ShowDialog(this) == DialogResult.OK)
        vDoc.Print();
}
MainForm.cs
// PdfiumViewer: PrintPreviewDialog with PdfDocument.CreatePrintDocument()
// sgcPDF: the same with TsgcPDFViewerControl.CreatePrintDocument()
if (FViewer.PageCount == 0)
    return;
using (PrintDocument vDoc = FViewer.CreatePrintDocument())
using (PrintPreviewDialog vPreview = new PrintPreviewDialog())
{
    vPreview.Document = vDoc;
    vPreview.Width = 900;
    vPreview.Height = 700;
    vPreview.ShowDialog(this);
}

将页面渲染为位图

MainForm.cs
// PdfiumViewer: Image img = PdfDocument.Render(page, dpiX, dpiY, forPrinting)
// sgcPDF: RenderPageToBitmap(page, ref bitmap, zoomPercent, dpi); the bitmap passed
// in is disposed and replaced
Bitmap vBitmap = null;
FViewer.RenderPageToBitmap(APageIndex, ref vBitmap, 100, 150);
try
{
    vBitmap.Save(AFileName, ImageFormat.Png);
    Log("Page " + sgcPDF_RTL.IntToStr(APageIndex + 1) + " rendered at 150 dpi: " +
      sgcPDF_RTL.IntToStr(vBitmap.Width) + " x " + sgcPDF_RTL.IntToStr(vBitmap.Height) + " px, " +
      AFileName);
}
finally
{
    vBitmap.Dispose();
}
// without a viewer (server, batch): one call from file to image
sgcPDF_Export.sgcPDFPageToImage(FViewer.FileName, Path.ChangeExtension(AFileName, null) + "_file.png",
  APageIndex, 72);

页面文本

MainForm.cs
// PdfiumViewer: PdfDocument.GetPdfText(page) returns the plain text of a page.
// sgcPDF: TsgcPDFDocument.ExtractTextW(page) is the same plain text, and
// TsgcPDFViewerControl.PageText(page) returns a TsgcPDFTextExtractor with the
// characters and their positions (GetTextWithPositions); the user can also select
// text with the mouse and copy it (SelectedText, CopyToClipboard)
string vText = FViewer.Document.ExtractTextW(APageIndex);
Log("Text of page " + sgcPDF_RTL.IntToStr(APageIndex + 1) + ": " + vText.Trim());
TsgcPDFTextExtractor vExtractor = FViewer.PageText(APageIndex);
if (vExtractor != null)
    Log("Characters with positions: " + sgcPDF_RTL.IntToStr(vExtractor.GetCharCount()));
return vText;

超越 PdfiumViewer:填写表单并添加注释

MainForm.cs
// PdfiumViewer: none (no AcroForm API). sgcPDF: AcroForm.FillField / SetCheckBox,
// then an incremental save keeps the original bytes and appends the change
TsgcPDFDocument vDoc = FViewer.Document;
if (vDoc.AcroForm.FindField("CustomerName") == null)
{
    Log("This document has no CustomerName field, open the generated sample");
    return;
}
vDoc.AcroForm.FillField("CustomerName", "ACME Ltd.");
vDoc.AcroForm.SetCheckBox("Approved", true);
FViewer.ClearCache();
FViewer.Invalidate();
vDoc.SaveIncremental(AFileName);
Log("Form filled and saved: " + AFileName);
MainForm.cs
// PdfiumViewer: none (annotations are only rendered). sgcPDF: the document's
// Annotations manager adds notes, free text, highlights, stamps, lines, shapes and
// ink; the viewer shows them after AnnotationsChanged
TsgcPDFDocument vDoc = FViewer.Document;
int vPage = FViewer.PageIndex;
TsgcPDFTextAnnotation vNote = vDoc.Annotations.AddText(vPage, 420, 760, 444, 784,
  "Migrated from PdfiumViewer to sgcPDF.");
vNote.Author = "sgcPDF demo";
vNote.SetColor(1.0, 0.9, 0.2);
vDoc.Annotations.AddHighlight(vPage, 72, 728, 330, 728, 72, 742, 330, 742);
FViewer.AnnotationsChanged();
vDoc.SaveIncremental(AFileName);
Log("Note and highlight added on page " + sgcPDF_RTL.IntToStr(vPage + 1) + ", saved: " + AFileName);

分步指南

一次迁移一项任务或一个窗体,并对比迁移前后的输出文件。

Delphi:从 Quick PDF Library 迁移

1

添加单元

添加 sgcPDF 的 Source 文件夹或已安装的包,并把 Quick PDF Library 的导入换成 sgcPDF_Types、sgcPDF_Fonts 和 sgcPDF_Document,需要时再加上 sgcPDF_Editor、sgcPDF_Encryption 和 sgcPDF_Renderer_GDI。

2

去掉 DLL

从部署中移除 DLL 或 ActiveX,并从代码中移除 UnlockKey 调用。

3

每个文档一个对象

把 TQuickPDF 对象换成每个文档一个 TsgcPDFDocument,加载、编辑、保存类任务则用 TsgcPDFEditor。

4

页面是对象

把 SelectPage 和绘制调用换成对 AddPage 或 Pages[i] 得到的 TsgcPDFPage 的调用,并把每个页码减 1。

5

对应调用

使用上面的表格。标准字体是 DrawText 的参数;TrueType 字体用 AddTrueTypeFont 添加一次,再用 DrawTextTT 绘制。

6

拼版

把 CapturePage 和 DrawCapturedPage 换成 AddPageAsForm 以及 DrawForm 或 DrawFormInRect。

7

对比输出

运行转换后的任务并检查文件。演示的自测可以作为模板:它会重新打开每个输出,并检查页数、文本和表单值。

WinForms:从 PdfiumViewer 迁移

1

移除原生部分

移除 PdfiumViewer 包、所有 PdfiumViewer.Native.* 包以及手工复制的 pdfium.dll,包括 x86 和 x64 文件夹。项目可以改回 AnyCPU。

2

添加 sgcPDF

添加 esegece.sgcPDF.WinForms(或 esegece.sgcPDF.WinForms.Community),它会带入 esegece.sgcPDF,然后添加 using esegece.sgcPDF;。

3

替换控件

把 PdfViewer 或 PdfRenderer 换成 TsgcPDFViewerControl(Dock = Fill),并把用到的工具栏按钮重建为对该控件的调用。

4

加载文档

把 PdfDocument.Load 和 Document = 换成 LoadFromFile,或者自行加载 TsgcPDFDocument 后赋给 Document。

5

对应成员

Page 变为 PageIndex 或 GotoPage,缩放模式对应 TsgcPDFViewerZoomMode,缩放倍数乘以 100,Search 变为 FindText,Render 变为 RenderPageToBitmap。

6

保留打印代码

在查看器上而不是 PdfiumViewer 文档上调用 CreatePrintDocument();PrintDialog 和 PrintPreviewDialog 代码保持不变。或者使用 PrintDialog(ATitle)。

7

启用新功能

可选:FormFilling = true、AnnotationTool 和 SaveIncremental,这些是 PdfiumViewer 无法提供的。

无法一一对应的部分

规划迁移之前请先阅读。

Quick PDF Library

PdfiumViewer

MigrateFromQuickPDF 和 MigrateFromPdfiumViewer

两个演示都会自行生成输入文件,因此不需要外部 PDF,并且都带有自测。

Delphi:位置

sgcUI 安装程序把它放在 Demos\PDF\Delphi\MigrateFromQuickPDF:sgcPDF_Demo_MigrateFromQuickPDF.dpr、frmMain.pas、frmMain.dfm 和 readme.html。

Delphi:如何运行

在 7 到 13 的任意 Delphi 中打开 .dpr(VCL,Win32 或 Win64),点击 Generate sample inputs,然后点击任意按钮或 Run all。输出保存在 exe 旁边的 output 文件夹中。

Delphi:自测

sgcPDF_Demo_MigrateFromQuickPDF.exe /selftest [folder] 运行每项任务,检查结果,并写入以 SELFTEST OK 结尾的 selftest.log。

.NET:位置

在 sgcUI .NET 演示中,位于 sgcUI_Demos.sln 解决方案的 PDF\MigrateFromPdfiumViewer 文件夹:sgcPDF_Demo_MigrateFromPdfiumViewer.csproj、Program.cs 和 MainForm.cs。

.NET:如何运行

dotnet run --project demos\PDF\MigrateFromPdfiumViewer(Community 版本加上 -p:SgcCommunity=true),点击 Generate sample,它会写入并打开 output\sample.pdf,然后点击任意按钮。

.NET:自测

sgcPDF_Demo_MigrateFromPdfiumViewer.exe /selftest [folder] 会打开示例、导航、缩放、适应高度、旋转、搜索、渲染、统计打印预览的页数、提取文本、填写表单、添加注释,并重新打开保存的文件。它写入以 SELFTEST OK 结尾的 selftest.log。

公平地说,对这两个库而言:基于 Quick PDF Library 或 PdfiumViewer 的应用在今天部署的地方仍能继续运行,Foxit 也把自己的 PDF SDK 列为 Quick PDF Library 的后继产品。当您希望 PDF 代码是编译进应用的 Pascal 或 C# 源代码、无需部署原生 PDF 库,并且用同一套 API 创建、填写、批注、签名和加密查看器所显示的文件时,sgcPDF 就是合适的路径。组件和版本请参阅 sgcPDF 产品页面。

超值之选:All-AccesseSeGeCe 全部产品,含高级支持,每年 €1,059 起。
查看 All-Access 价格

今天就迁移您的第一个 PDF 任务

下载包含 sgcPDF 的 sgcUI,或从 NuGet 添加免费的 Community 包,并借助随附的 MigrateFromQuickPDF 和 MigrateFromPdfiumViewer 演示迁移您的第一个任务或窗体。