Pdf1.FileName := 'C:\MyDocument.pdf';
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
ShowMessage('Title: ' + Pdf1.Title);
ShowMessage('Author: ' + Pdf1.Author);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile('C:\MyDocument.pdf');
Pdf1.LoadDocument(Stream, True);
Pdf1.Active := True;
ShowMessage('PDF loaded from stream');
finally
Stream.Free;
end;
end;
Pdf1.CreateDocument;
Pdf1.Active := True;
Pdf1.AddPage(0, 612, 792);
Pdf1.PageNumber := 1;
Pdf1.AddText('Hello World!', 'Arial', 12, 100, 700, clBlack, $FF, 0.0);
Pdf1.UpdatePage;
Pdf1.SaveAs('C:\NewDocument.pdf');
Pdf1.Active := False;
var
PageText: WString;
RectText: WString;
begin
Pdf1.PageNumber := 1;
PageText := Pdf1.Text;
Memo1.Text := PageText;
RectText := Pdf1.TextInRectangle(100, 100, 500, 200);
ShowMessage('Text in rectangle: ' + RectText);
end;
var
Position: Integer;
begin
Position := Pdf1.FindFirst('Hello', [seCaseSensitive]);
while Position >= 0 do
begin
ShowMessage('Found at character: ' + IntToStr(Position));
Position := Pdf1.FindNext;
end;
end;
Pdf1.PageNumber := 1;
Pdf1.AddText('Document Title', 'Times New Roman', 16, 100, 750, clBlue);
Pdf1.AddText('This is the document content.', 'Arial', 12, 100, 700, clBlack);
Pdf1.UpdatePage;
var
I: Integer;
MetricSize: Single;
FontBytes: TBytes;
begin
Pdf1.PageNumber := 1;
for I := 0 to Pdf1.ObjectCount - 1 do
if Pdf1.ObjectType[I] = otText then
begin
MetricSize := 12.0;
FontBytes := Pdf1.FontData[I];
Memo1.Lines.Add('Object ' + IntToStr(I));
Memo1.Lines.Add('Base name: ' + Pdf1.FontBaseName[I]);
Memo1.Lines.Add('Family: ' + Pdf1.FontFamilyName[I]);
Memo1.Lines.Add('Embedded: ' + BoolToStr(Pdf1.FontIsEmbedded[I], True));
Memo1.Lines.Add('Weight: ' + IntToStr(Pdf1.FontWeight[I]));
Memo1.Lines.Add('Italic angle: ' + IntToStr(Pdf1.FontItalicAngle[I]));
Memo1.Lines.Add('Ascent: ' + FloatToStr(Pdf1.FontAscent[I, MetricSize]));
Memo1.Lines.Add('Descent: ' + FloatToStr(Pdf1.FontDescent[I, MetricSize]));
Memo1.Lines.Add('Font data bytes: ' + IntToStr(Length(FontBytes)));
end;
if Pdf1.CharacterCount > 0 then
begin
Memo1.Lines.Add('First character size: ' + FloatToStr(Pdf1.CharacterFontSize[0]));
Memo1.Lines.Add('First character weight: ' + IntToStr(Pdf1.CharacterFontWeight[0]));
Memo1.Lines.Add('First character angle: ' + FloatToStr(Pdf1.CharacterAngle[0]));
end;
end;
โปรเจ็กต์ Demo\Delphi\Fontคุณสมบัติ Demo\CBuilder\Fontคุณสมบัติ และ Demo\Lazarus\Fontคุณสมบัติ แสดงเมทาดาทาเดียวกันผ่านทั้ง TPdf และ TPdfView
โปรเจ็กต์ Demo\Delphi\ContentExtractionLab Demo\CBuilder\ContentExtractionLab และ Demo\Lazarus\ContentExtractionLab เปลี่ยนการดึงเนื้อหาให้เป็นเวิร์กโฟลว์เดียว พวกมันเปิดหรือสร้าง PDF ให้ผู้ใช้เลือก metadata ข้อความบนหน้า อ็อบเจ็กต์หน้า รูปภาพ ไฟล์แนบ ลิงก์ คำอธิบายประกอบ ที่คั่นหน้า และเมตริกฟอนต์ / อักขระ จากนั้นสร้างทั้งสรุปและรายงาน TXT หรือ JSON แบบละเอียด lab ยังมีการเลือกหมวดหมู่แบบ all / none การคัดลอกรายงานไปยังคลิปบอร์ด และการบันทึก source-PDF เพื่อให้ผู้ใช้เก็บ PDF ตัวอย่างที่สร้างขึ้นไว้ข้างรายงานการดึงเนื้อหาได้ เดโมรุ่นเก่า ExtractText, ExtractImages, Attachment, และ FontProperties ยังคงเป็นตัวอย่างที่มุ่งเน้นเฉพาะด้าน; ExtractText รองรับตัวเลือกช่วงหน้าและตัวแบ่งหน้าเพื่อสร้างผลลัพธ์ข้อความรายหน้าครอบคลุมทั้ง Delphi, C++Builder, และ Lazarus ในขณะที่ ContentExtractionLab เป็นตัวอย่างการสำรวจที่ครอบคลุมข้ามเรื่อง
var
I: Integer;
FieldInfo: TPdfFormFieldInfo;
begin
Pdf1.FormField[0] := 'John';
for I := 0 to Pdf1.FormFieldCount - 1 do
begin
FieldInfo := Pdf1.FormFieldInfo[I];
ShowMessage(FieldInfo.Name + ': ' + Pdf1.FormField[I]);
end;
end;
// ถ้าไม่ flatten, FormField[i] := value จะเขียนเฉพาะรายการ /V เท่านั้น —
// รูปลักษณ์ที่เห็นได้มาจากสตรีม /AP และอาจไม่รีเฟรช
// GenerateFormAppearances จะสร้าง /AP ใหม่ให้ทุก widget จากนั้น
// FlattenAllPages จะฝังคำอธิบายประกอบและ widget ของทุกหน้าให้กลายเป็น
// เนื้อหาถาวรที่แก้ไขไม่ได้
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := 'john@example.com';
Pdf1.GenerateFormAppearances;
if Pdf1.FlattenAllPages then
Pdf1.SaveAs('C:\Application.flat.pdf');
Pdf1.Active := False;
var
I: Integer;
Bitmap: TBitmap;
begin
for I := 0 to Pdf1.BitmapCount - 1 do
begin
Bitmap := Pdf1.Bitmap[I];
try
Bitmap.SaveToFile('C:\ExtractedImage' + IntToStr(I) + '.bmp');
finally
Bitmap.Free;
end;
end;
end;
var
JpegStream: TFileStream;
begin
JpegStream := TFileStream.Create('C:\MyImage.jpg', fmOpenRead or fmShareDenyWrite);
try
Pdf1.AddJpegImage(JpegStream, 100, 500, 200, 150);
finally
JpegStream.Free;
end;
Pdf1.AddPicture(Image1.Picture, 100, 300, 150, 100);
Pdf1.UpdatePage;
end;
var
I: Integer;
Annotation: TPdfAnnotation;
begin
for I := 0 to Pdf1.AnnotationCount - 1 do
begin
Annotation := Pdf1.Annotation[I];
ShowMessage('Annotation ' + IntToStr(I) + ': ' + Annotation.ContentsText);
end;
end;
Pdf1.CreatePath(100, 100, 200, 150, fmAlternate, clYellow, $FF, True, clBlack, $FF, 2.0);
Pdf1.AddPath;
Pdf1.CreatePath(100, 100, fmNone, clBlack, $FF, True, clRed, $FF, 3.0);
Pdf1.LineTo(200, 100);
Pdf1.LineTo(150, 200);
Pdf1.ClosePath;
Pdf1.AddPath;
Pdf1.UpdatePage;
var
I: Integer;
Bookmarks: TBookmarks;
begin
Bookmarks := Pdf1.Bookmarks;
for I := 0 to Length(Bookmarks) - 1 do
ShowMessage(Bookmarks[I].Title + ' -> Page ' + IntToStr(Bookmarks[I].PageNumber));
end;
var
I: Integer;
Destination: TDestination;
begin
for I := 0 to Pdf1.DestinationCount - 1 do
begin
Destination := Pdf1.Destination[I];
if Destination.Name = 'Chapter1' then
begin
Pdf1.PageNumber := Destination.PageNumber;
Break;
end;
end;
end;
var
I: Integer;
AttachmentData: TBytes;
AttachmentName: WString;
FileStream: TFileStream;
begin
for I := 0 to Pdf1.AttachmentCount - 1 do
begin
AttachmentName := Pdf1.AttachmentName[I];
AttachmentData := Pdf1.Attachment[I];
FileStream := TFileStream.Create('C:\Extracted_' + string(AttachmentName), fmCreate);
try
if Length(AttachmentData) > 0 then
FileStream.WriteBuffer(AttachmentData[0], Length(AttachmentData));
finally
FileStream.Free;
end;
end;
if Pdf1.CreateAttachment('MyFile.txt') then
Pdf1.Attachment[Pdf1.AttachmentCount - 1] := TFile.ReadAllBytes('C:\MyFile.txt');
end;
Pdf1.AddPage(Pdf1.PageCount + 1, 612, 792);
Pdf1.AddPage(Pdf1.PageCount + 1, 595, 842);
Pdf1.AddPage(Pdf1.PageCount + 1, 400, 600);
var
SourcePdf: TPdf;
begin
SourcePdf := TPdf.Create(nil);
try
SourcePdf.FileName := 'C:\SourceDocument.pdf';
SourcePdf.Active := True;
Pdf1.ImportPages(SourcePdf, '1', Pdf1.PageCount + 1);
finally
SourcePdf.Free;
end;
end;
if Pdf1.PageCount > 1 then
Pdf1.DeletePage(Pdf1.PageCount);
// สร้างเอกสาร PDF ที่บีบอัด
Pdf1.CreateDocument;
Pdf1.Compressed := True; // ตรวจให้แน่ใจว่าสตรีมทั้งหมดถูกบีบอัดด้วย FlateDecode (ค่าเริ่มต้น)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // ขนาด A4
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// บันทึกพร้อมตัวเลือก
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // บันทึกแบบเพิ่มทีละส่วน
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // ลบการรักษาความปลอดภัย
// บันทึกไปยังเป้าหมาย PDF เวอร์ชันที่ระบุ ตัวตรวจสอบเวอร์ชัน PDF จะปฏิเสธ
// ฟีเจอร์ใด ๆ ที่ถูกเพิ่มหลังจากเป้าหมายที่เลือกไว้ (JBIG2Decode, MarkInfo,
// การเข้ารหัส AES, คำอธิบายประกอบ Polygon / Caret / Watermark / Redact, ...)
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Save rejected: document contains features newer than PDF 1.5');
// SaveAsPdfA จะประมวลผลการบันทึกฐานซ้ำด้วย incremental update ที่
// ใส่สตรีม XMP metadata, sRGB ICC OutputIntent และ document catalog
// ที่อัปเดตแล้วเพื่อให้ผลลัพธ์สอดคล้องกับ PDF/A-1b โปรไฟล์
// sRGB IEC61966-2.1 ที่แนบมาจะถูกใช้โดยอัตโนมัติเมื่อไม่ได้ระบุโปรไฟล์เอง
Pdf1.FileName := 'C:\Report.pdf';
Pdf1.Active := True;
if Pdf1.SaveAsPdfA('C:\Report.pdfa.pdf', pac1b) then
ShowMessage('Saved PDF/A-1b archive');
// PDF/A พร้อมโปรไฟล์ ICC แบบกำหนดเอง (เช่น ไปป์ไลน์การพิมพ์ CMYK)
var
Opts: TPdfASaveOptions;
begin
Opts := Default(TPdfASaveOptions);
Opts.Conformance := pac1b;
Opts.IccProfileData := TFile.ReadAllBytes('C:\Profiles\Coated_GRACoL_2006.icc');
Pdf1.SaveAsPdfA('C:\Report.cmyk.pdfa.pdf', Opts);
end;
// เอาต์พุต PDF/A ในหน่วยความจำ
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Opts := Default(TPdfASaveOptions);
Opts.Conformance := pac1b;
if Pdf1.SaveAsPdfAToStream(Stream, Opts) then
UploadToArchive(Stream);
finally
Stream.Free;
end;
end;
var
Validation: TPdfAValidationResult;
Issue: TPdfAValidationIssue;
begin
Pdf1.FileName := 'C:\Incoming\report.pdf';
Pdf1.Active := True;
// ตรวจแบบเบา ๆ ในระดับส่วนหัว — อ่านความสอดคล้องโดยไม่ต้องวนผ่าน
// ตัวตรวจสอบเต็ม ผลลัพธ์: pacNone, pac1a, pac1b, pac2b, pac3b หรือ
// pacUnknown
case Pdf1.PdfAConformance of
pac1b: Status.Caption := 'PDF/A-1b';
pac2b: Status.Caption := 'PDF/A-2b';
pacNone: Status.Caption := 'Not PDF/A';
else
Status.Caption := 'Unknown conformance';
end;
// ตัวตรวจสอบเต็มพร้อมการวินิจฉัยรายปัญหา
Validation := Pdf1.ValidatePdfA;
Memo1.Lines.Add('Conformance: ' + GetEnumName(TypeInfo(TPdfAConformance),
Ord(Validation.Conformance)));
for Issue in Validation.Issues do
Memo1.Lines.Add('- ' + Issue.คำอธิบาย);
end;
โปรเจ็กต์ Demo\Delphi\StandardsLab Demo\CBuilder\StandardsLab และ Demo\Lazarus\StandardsLab โหลดหรือสร้าง PDF รัน ValidatePdfA ValidatePdfUa ValidatePdfE ValidatePdfX ValidatePdfR และ ValidatePdfVT จากนั้นแสดงระดับความสอดคล้องและจำนวนปัญหาที่ตรวจพบแต่ละรายการในกริดเดียว ปุ่มบันทึกของพวกมันเรียก SaveAsPdfA SaveAsPdfUa SaveAsPdfE SaveAsPdfX SaveAsPdfR และ SaveAsPdfVT เพื่อให้เวิร์กโฟลว์ marker-output มองเห็นได้โดยไม่ต้องเขียน harness เอง
หน่วย FPdfPreflightReport ห่อ ValidatePdfA ValidatePdfUa ValidatePdfE ValidatePdfX ValidatePdfR และ ValidatePdfVT ให้กลายเป็นอ็อบเจ็กต์รายงานเดียว Demo\Delphi\PreflightReport Demo\Lazarus\PreflightReport และ Demo\CBuilder\PreflightReport ให้ผู้ใช้เลือกมาตรฐาน สร้างสรุป ตรวจสอบสถานะ ลำดับความสำคัญ หมวดหมู่ปัญหา จำนวนตามหมวด การกระทำถัดไป รหัสปัญหา และการกระทำที่แนะนำ สลับพรีวิวระหว่าง text Markdown JSON และ CSV และส่งออกรายงาน TXT HTML Markdown JSON หรือ CSV ตัวอย่าง GUI ของ Delphi ยังเพิ่มพรีวิว Action Plan ที่ย่อมาตรฐานที่ไม่ผ่าน ลำดับความสำคัญ รหัสปัญหา และคำแนะนำให้เป็นเช็กลิสต์การแก้ไข Demo\Delphi\PreflightReportCli เปิดเผยเส้นทางรายงานเดียวกันเป็นเวิร์กโฟลว์คอนโซลพร้อมอาร์กิวเมนต์ input / output รหัสผ่านแบบเลือกได้ การเลือกรูปแบบผลลัพธ์รวมถึง plan / actionplan การกรองมาตรฐาน การฝังรายงาน attach=output.pdf แบบไฟล์เดียวแบบเลือกได้ รหัสออกสำหรับ CI ผ่าน failon= การประมวลผล batch=list.txt การสแกนไดเรกทอรี batchdir=folder พร้อมการ recurse แบบเลือกได้และการเรียงเส้นทางอย่างเสถียร การกำหนดเส้นทางรายงาน outdir=reports ชื่อรายงานแบบปลอดการชนกันสำหรับชื่อไฟล์อินพุตที่ซ้ำกัน สรุป TXT / JSON / HTML ที่มีผลรวมไฟล์และสถานะมาตรฐาน manifest แถว CSV ที่ทำซ้ำการตั้งค่าการรันในแต่ละรายการ และโหมดตัวอย่างแบบไม่มีอาร์กิวเมนต์สำหรับสคริปต์และงาน CI รายงานที่สร้างขึ้นระบุว่าตัวตรวจสอบในตัวครอบคลุมการตรวจระดับ marker และระดับไฟล์ที่เลือกไว้ จึงยังสามารถมอบ preflight ระดับเนื้อหาเต็มให้เอนจินตรวจสอบเฉพาะทางได้
// LoadCustomDocument ให้ PDFium ดึงบล็อกจาก TStream ที่ seek ได้ทุกชนิด
// ตามต้องการ สตรีมจะไม่ถูกคัดลอกเข้า memory ดังนั้น PDF ขนาดหลาย GB,
// สตรีม body ของ HTTP ระยะไกล และแหล่งข้อมูลจากฐานข้อมูลจึงใช้งานได้
// โดยไม่ต้องมีบัฟเฟอร์ล่วงหน้า
var
FileStream: TFileStream;
begin
// สตรีมอ่านจากไฟล์ (ไม่คัดลอกเข้า memory)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ โอนความเป็นเจ้าของแล้ว — Pdf1 จะปล่อย FileStream เมื่อ UnloadDocument
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;
// สตรีมที่ผู้เรียกเป็นเจ้าของ (ผู้เรียกเป็นผู้ปล่อยหลัง UnloadDocument)
var
Stream: TFileStream;
begin
Stream := TFileStream.Create('C:\Live.pdf',
fmOpenRead or fmShareDenyNone);
try
Pdf1.LoadCustomDocument(Stream, False);
Pdf1.Active := True;
DoWork;
Pdf1.Active := False;
finally
Stream.Free;
end;
end;
// FPdfAsync เปิดเผย IPdfCancellationToken /
// IPdfCancellationTokenSource สำหรับความหมายของการยกเลิกจาก UI
// ที่ใช้งานได้กับเส้นทาง export และ render ที่ทำงานนาน
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// ... ผู้ใช้คลิก Cancel ...
CancelSource.Cancel;
// worker จะตรวจ CancelToken.IsCancelled เป็นระยะ หรือเส้นทาง render
// จะโพลผ่าน callback IFSDK_PAUSE ของ PDFium
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive ทำให้การ render ความละเอียดสูงที่ยาวนานยกเลิก
// กลางหน้าได้เมื่อผู้ใช้คลิก Cancel แทนที่จะบล็อกผู้เรียกจนเสร็จ
var
Status: TPdfProgressiveStatus;
Bitmap: TBitmap;
begin
Bitmap := TBitmap.Create;
try
Bitmap.PixelFormat := pf32bit;
Bitmap.SetSize(2480, 3508); // A4 ที่ 300 DPI
Status := Pdf1.RenderPageProgressive(Bitmap, 0, 0, Bitmap.Width,
Bitmap.Height, CancelToken, ro0, [reAnnotations], clWhite);
case Status of
prsDone: SaveBitmap(Bitmap);
prsCancelled: Memo1.Lines.Add('Render cancelled — partial result available');
prsFailed: Memo1.Lines.Add('PDFium reported FPDF_RENDER_FAILED');
end;
finally
Bitmap.Free;
end;
end;
// TPdfFuture<T> สร้าง worker บนเธรดพื้นหลังและส่งชุดผลลัพธ์
// TPdfFutureResult<T> กลับไปยังเธรดหลัก
//
// สำคัญ: TPdf จะ serialize การเรียก render แยกตามแต่ละ instance แต่ worker
// ยังเป็นเจ้าของการส่งงานและอายุเอกสาร อย่าแก้ไข TPdf ระหว่างที่มี
// render อยู่ระหว่างทาง; การส่งออกแบบแบตช์มักเก็บ TPdf หนึ่งตัวต่อหนึ่งไฟล์
type
TRenderResult = record
Bitmap : TBitmap;
PageNo : Integer;
end;
var
Future: TPdfFuture<TRenderResult>;
begin
Future := TPdfFuture<TRenderResult>.Create(
function (const ACancelToken: IPdfCancellationToken): TRenderResult
var
LocalPdf: TPdf;
begin
LocalPdf := TPdf.Create(nil);
try
LocalPdf.FileName := 'C:\Huge.pdf';
LocalPdf.Active := True;
LocalPdf.PageNumber := 1;
Result.Bitmap := LocalPdf.RenderPage(0, 0, 2480, 3508);
Result.PageNo := 1;
finally
LocalPdf.Free;
end;
end,
procedure (const AResult: TPdfFutureResult<TRenderResult>)
begin
case AResult.State of
pfsSuccess: PaintBitmapToView(AResult.Value.Bitmap);
pfsCancelled: Log.Add('Cancelled');
pfsFailed: Log.Add('Worker failed: ' + AResult.ErrorMessage);
end;
end);
end;
// FPdfMatrix ห่อ FS_MATRIX ของ PDFium เพื่อให้ผู้เรียกสร้าง transform
// แบบประกาศเชิงโครงสร้างแล้วส่งต่อให้ FPDFPageObj_SetMatrix
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // เลื่อน 1 นิ้วไปทางขวา ขึ้นไปประมาณ 2.8 นิ้ว
M.Scale(0.5, 0.5); // ตราประทับขนาดครึ่งหนึ่ง
M.Rotate(15); // หมุนทวนเข็มนาฬิกา 15 องศา
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // คูณต่อท้ายด้วย matrix อีกตัว
// ใช้กับ page object ของ PDFium ได้ทุกชนิด (text / path / image / form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex คัดลอกอาร์เรย์ดัชนีแบบ zero-based ที่ระบุไว้อย่าง
// ชัดเจนจาก source PDF มายังเอกสารนี้ InsertAt = 0 จะแทรกก่อนหน้า 1;
// PageCount จะต่อท้าย
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// นำเข้าหน้า 0, 2, 4 (one-based: หน้า 1, 3, 5) จาก Source
// แล้วต่อท้ายไว้ที่ท้าย Pdf1
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// ถ้าใช้อาร์เรย์ว่างจะนำเข้าทุกหน้าของ source
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne คืน TPdf ใหม่เอี่ยมที่หน้าทั้งหมดเป็นงานประกอบ
// NumX*NumY จากเอกสารนี้ OutputWidth/Height เป็นหน่วยผู้ใช้ของ PDF
// (1 หน่วย = 1/72 นิ้ว)
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// A4 แนวนอนแบบ 4-up: 842 x 595 pt, 2 คอลัมน์ x 2 แถว
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages รับดัชนีแบบ zero-based ของหน้าที่จะย้ายรวมถึงดัชนีปลายทาง
// ของหน้าที่ถูกย้ายหน้าแรกหลังจากย้ายเสร็จ หน้าที่เหลือจะเลื่อนไปรอบ
// บล็อกที่ย้าย ทำให้ความยาวเอกสารเท่าเดิม
//
// ตัวอย่าง: ในเอกสาร 5 หน้า ที่หน้าถูกนับเป็น [0,1,2,3,4],
// MovePages([2, 3], 0) จะเรียงใหม่เป็น [2, 3, 0, 1, 4]
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage สร้าง handle Form XObject ที่นำกลับมาใช้ซ้ำได้จาก
// หน้าของ TPdf อื่นใดก็ได้ นำ handle นั้นไปประทับลงบนหน้าในเอกสารนี้ได้
// ตามต้องการ — ตราประทับแต่ละชิ้นเป็น page object ของ PDFium หนึ่งชิ้น
// ที่สามารถจัดตำแหน่ง ปรับสเกล และหมุนผ่าน matrix API
uses FPdfMatrix, FPdfView;
var
WatermarkSource: TPdf;
XObj: TPdfXObject;
PageObj: FPDF_PAGEOBJECT;
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
I: Integer;
begin
WatermarkSource := TPdf.Create(nil);
try
WatermarkSource.FileName := 'C:\Watermarks\Confidential.pdf';
WatermarkSource.Active := True;
// ห่อหน้า 0 ของ WatermarkSource เป็น XObject ที่นำกลับมาใช้ซ้ำได้
XObj := Pdf1.CreateXObjectFromPage(WatermarkSource, 0);
try
for I := 1 to Pdf1.PageCount do
begin
Pdf1.PageNumber := I;
PageObj := Pdf1.InsertFormObjectFromXObject(XObj);
if PageObj = nil then Continue;
// จัดวอเตอร์มาร์กให้อยู่กึ่งกลางหน้า
M := TPdfMatrix.Create;
try
M.Scale(0.5, 0.5);
M.Translate(0.25 * Pdf1.PageWidth, 0.5 * Pdf1.PageHeight);
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
Pdf1.UpdatePage;
end;
finally
XObj.Free; // ปิด handle FPDF_XOBJECT
end;
Pdf1.SaveAs('C:\Watermarked.pdf');
finally
WatermarkSource.Free;
end;
end;
โปรเจ็กต์ Demo\Delphi\WatermarkStamp Demo\Lazarus\WatermarkStamp และ Demo\CBuilder\WatermarkStamp เปลี่ยนรูปแบบนี้ให้เป็นเวิร์กโฟลว์ที่รันได้ พวกมันสร้าง PDF เป้าหมายและ PDF ตราประทับที่นำกลับมาใช้ได้ ห่อหน้าตราประทับด้วย CreateXObjectFromPage แทรก Form XObject เดียวกันลงบนทุกหน้าเป้าหมาย ใช้การแปลง TPdfMatrix สำหรับวอเตอร์มาร์กกลางหน้าและตราประทับมุมขวาบน และเพิ่มป้ายเลขหน้าตามต้องการก่อนบันทึกผลลัพธ์
// AddImage(FileName) รองรับทุกรูปแบบที่ลงทะเบียนไว้กับ graphics units
// ของ VCL หรือ LCL (BMP, PNG ผ่าน PngImage, JPG, ...)
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) ข้ามตัวกลาง TPicture เหมาะเมื่อ bitmap มาจากงาน
// render หรือการสร้างแบบแบตช์อยู่แล้ว
var
Bitmap: TBitmap;
begin
Bitmap := Pdf1.RenderPage(0, 0, 595, 842);
try
Pdf1.PageNumber := 2;
Pdf1.AddImage(Bitmap, 50, 50, 250, 350);
Pdf1.UpdatePage;
finally
Bitmap.Free;
end;
end;
โปรเจ็กต์ Demo\Delphi\ImageToPDF Demo\CBuilder\ImageToPDF และ Demo\Lazarus\ImageToPDF เปลี่ยนสิ่งนี้ให้เป็นเวิร์กโฟลว์แบตช์เต็มรูปแบบ โดยเลือกไฟล์รูปภาพหลายไฟล์ พรีวิวรายการที่เลือก สร้างหนึ่งหน้า PDF ต่อหนึ่งรูปภาพ ปรับสเกลรูปภาพแต่ละภาพให้พอดีกับหน้า A4 แนวตั้งหรือแนวนอน บันทึกผลลัพธ์ และเปิด PDF ที่สร้างขึ้น
var
MatchCount: Integer;
begin
// วาดมาสก์ clYellow ทับทุกผลลัพธ์ที่พบในหน้าปัจจุบันของมุมมอง
// การเปลี่ยนไปหน้าอื่นจะล้างไฮไลต์โดยอัตโนมัติ
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// ลบ overlay ด้วยโปรแกรมก่อนที่หน้าจะเปลี่ยน
PdfView1.ClearHighlight;
end;
// FitMode ช่วยให้เอกสารยาวยังคงอยู่ในกรอบระหว่าง Resize หรือการเปลี่ยนหน้า
// การตั้ง Zoom โดยตรงจะยกเลิก FitMode (กลับเป็น pfmNone)
PdfView1.FitMode := pfmFitPage; // ทั้งหน้าพอดีกับ viewport
// PdfView1.FitMode := pfmFitWidth; // ความกว้างหน้าตรงกับความกว้าง viewport
// PdfView1.FitMode := pfmActualSize; // 100%
// PageColor แยกสีหน้ PDF ที่ render ออกจาก Color ของโฮสต์คอนโทรล
// — การตั้งค่า viewer โหมดมืดทั่วไปจะคงพื้นที่เลื่อนไว้เป็นสีเข้ม
// ขณะที่หน้ PDF ยังเป็นสีขาวกระดาษ
PdfView1.Color := clBlack; // พื้นที่เลื่อน
PdfView1.PageColor := clWhite; // พื้นหลังหน้ PDF
// เงาตกกระทบและเส้นขอบหน้าที่เลือกใช้ เพื่อยกหน้าขึ้นจากพื้นหลังมืด
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
โปรเจ็กต์ Demo\Delphi\ViewerInteractionLab Demo\CBuilder\ViewerInteractionLab และ Demo\Lazarus\ViewerInteractionLab รวบรวมพื้นผิวการโต้ตอบของ TPdfView รุ่นใหม่ไว้ในรูปแบบกะทัดรัดเดียว: เปิดหรือสร้าง PDF ตัวอย่าง สลับ DisplayMode ใช้ FitMode เปลี่ยน PageColor หมุนหน้า ไฮไลต์ผลการค้นหาบนหน้าปัจจุบันด้วย HighlightSearchText เปิดหรือปิดการเลือกข้อความของผู้ใช้ ตรวจ SelectedText เรียก SelectAll CopySelectionToClipboard และ ClearSelection และดูพิกัด DeviceToPage อัปเดตในแถบสถานะขณะเมาส์เลื่อนไปบนหน้า
โปรเจ็กต์ Demo\Delphi\SearchAndSelect Demo\CBuilder\SearchAndSelect และ Demo\Lazarus\SearchAndSelect เปลี่ยนเวิร์กโฟลว์ viewer สำหรับการค้นหาและเลือกให้กลายเป็นตัวอย่างแบบสแตนด์อโลน พวกมันเปิดหรือสร้าง PDF แสดงผลการค้นหาทุกหน้าพร้อมหมายเลขหน้า ดัชนีอักขระ และข้อความพรีวิว รองรับการนำทาง Previous / Next และการดับเบิลคลิกเพื่อกระโดดไปยังผลลัพธ์ ไฮไลต์ผลลัพธ์บนหน้าปัจจุบันด้วย HighlightSearchText เปิดตัวเลือก match-case และ whole-word สลับ AllowUserTextSelection พรีวิว SelectedText ขับ SelectAll CopySelectionToClipboard และ ClearSelection จากปุ่ม และให้ผู้ใช้สลับระหว่างโหมดแสดงผลหน้าเดี่ยวกับแบบ spread
โปรเจ็กต์ Demo\Delphi\PrintPreferences Demo\CBuilder\PrintPreferences และ Demo\Lazarus\PrintPreferences อ่านค่าการพิมพ์ที่ผู้เขียนกำหนดก่อนพิมพ์ พวกมันแสดง PrintCopies PrintPageRanges PrintScaling และ PrintPaperHandling สามารถคัดลอกจำนวนสำเนาและช่วงหน้าที่ผู้เขียนให้ไว้ไปยังอินพุตการพิมพ์ และแสดงวิธีที่ SetPdfPrintPaperHandlingDevMode แมปเจตนาการพิมพ์สองหน้าของ PDF ไปยัง DEVMODE ของ Windows
โปรเจ็กต์ Demo\Delphi\PrintPDF Demo\CBuilder\PrintPDF และ Demo\Lazarus\PrintPDF แสดงเวิร์กโฟลว์การพิมพ์มาตรฐาน: เปิด PDF พรีวิวหน้า เลือกช่วงหน้าและจำนวนสำเนาในกล่องโต้ตอบการพิมพ์ของแพลตฟอร์ม เคารพโหมด collate เรนเดอร์แต่ละหน้าผ่าน TPdf.RenderPage และแสดงความคืบหน้าการพิมพ์พร้อมการยกเลิก ตัวอย่าง Delphi ยังรับ PDF ที่ลากมาวางเพื่อโหลดได้รวดเร็ว
โปรเจ็กต์ Demo\Delphi\MultiPageViewer Demo\CBuilder\MultiPageViewer และ Demo\Lazarus\MultiPageViewer ทดสอบการแสดงผลแบบต่อเนื่อง แบบ spread และแบบ aware-cover พร้อมเวิร์กโฟลว์การนำทางหน้าและการซูมตามปกติ พวกมันยังมีประโยชน์สำหรับทดสอบความเร็วจากการเปิดจนถึงหน้าแรกและการตอบสนองของการเรนเดอร์บน PDF ขนาดใหญ่มาก ในการทดสอบไฟล์ใหญ่ เอกสารขนาด 2.x GB ที่อาจทำให้ Acrobat หรือ Foxit ใช้เวลา 20-30 วินาทีก่อนแสดงมุมมองแรก สามารถใช้งานได้เกือบทันทีผ่านเส้นทาง viewer ของ PDFiumPas
โปรเจ็กต์ Demo\Delphi\SplitView Demo\CBuilder\SplitView และ Demo\Lazarus\SplitView แสดงการตรวจสอบ PDF แบบเคียงข้าง ตัวอย่างของ C++Builder และ Lazarus เน้นการเปรียบเทียบแบบสองหรือสามพาเนล การเลือกมุมมองที่ใช้งานอยู่ คำสั่งนำทางที่ซิงก์กันได้ ตัวตั้งซูมที่ใช้ร่วมกัน และการหมุนสำหรับมุมมองที่เลือกหรือทุกมุมมองที่โหลดไว้ ใช้ตัวอย่างเหล่านี้เพื่อเปรียบเทียบเอกสารขนาดใหญ่โดยยังคงให้มุมมองแรกที่ใช้งานได้และการเรนเดอร์หน้าถัด ๆ ไปตอบสนองได้เหมือนเดิม ไฟล์ PDF ขนาด 2.x GB ชุดเดียวกันที่อาจทำให้ Acrobat หรือ Foxit ใช้เวลา 20-30 วินาทีในการเปิดก็สามารถใช้งานได้เกือบทันทีในเส้นทาง viewer ของ PDFiumPas
โปรเจ็กต์ Demo\Delphi\SecurityAudit Demo\CBuilder\SecurityAudit และ Demo\Lazarus\SecurityAudit เปิด PDF แบบอ่านอย่างเดียวและแสดงสถานะสิทธิ์ ไฟล์แนบที่ฝังไว้ การกระทำ JavaScript ของเอกสาร คำอธิบายประกอบลิงก์ URI และ Launch ลิงก์เว็บ สรุปลายเซ็น สถานะ XFA ความพร้อมของตัวช่วย V8 และ callback ของฟีเจอร์ที่ไม่รองรับ แถวสรุปและแถว finding เดียวกันสามารถบันทึกหรือคัดลอกเป็นรายงาน audit แบบ TXT หรือบันทึกเป็น JSON แบบมีโครงสร้างได้ พวกมันมีไว้เป็นแผงความเสี่ยงของแอปพลิเคชันโฮสต์ ไม่ใช่ตัวตรวจจับมัลแวร์
// LinkOptions ควบคุมการจัดการอัตโนมัติของ action link PDF ทั้ง 4 แบบ
// ค่าเริ่มต้นเปิดใช้เฉพาะ goto และ URI; launch (เรียกโปรแกรม) และ
// embedded GotoR (กระโดดไปยังไฟล์อื่น) ยังปิดอยู่เพื่อไม่ให้การคลิกพลาด
// สั่งรันโค้ดโดยพลการได้
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // ค่าเริ่มต้นที่ปลอดภัย
// OnAnnotationLinkClick จะทำงานสำหรับปลายทางภายในเอกสาร (การกระโดดหน้า,
// ปลายทางที่ตั้งชื่อไว้, action chain) ตั้ง Handled = True เพื่อป้องกัน
// ไม่ให้ view ตามลิงก์อัตโนมัติ
procedure TForm1.PdfView1AnnotationLinkClick(Sender: TObject;
LinkIndex: Integer; const Action: TPdfAction;
var Handled: Boolean);
begin
if Action.Kind = paUri then
begin
if MessageDlg('Open ' + Action.Uri + '?', mtConfirmation, [mbYes, mbNo], 0)
<> mrYes then
Handled := True; // ระงับการจัดการอัตโนมัติ
end;
end;
// OnWebLinkClick จะทำงานสำหรับสตริง URI ที่ตัวสแกน web-link ของ PDFium
// ตรวจพบ (URL ที่ตรวจจับอัตโนมัติใน text stream แม้ไม่มีคำอธิบายประกอบ
// /A <</S/URI...>>)
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
WebLinkIndex: Integer; const Url: WString;
var Handled: Boolean);
begin
Log.Add('Web link clicked: ' + string(Url));
end;
// บน widget ของ AcroForm ที่โฟกัสอยู่ มีเมธอดใหม่ 6 ตัวสำหรับประวัติ
// การแก้ไขฟอร์มของ PDFium เมธอดทั้งหกจะหยุดทำงานอย่างปลอดภัยหากไม่มี
// widget ที่โฟกัสหรือเอกสารไม่มี AcroForm
procedure TForm1.btnSelectAllClick(Sender: TObject);
begin
PdfView1.SelectAllFormText;
EditMenuCopy.Caption := 'Copy: ' + string(PdfView1.GetSelectedFormText);
end;
procedure TForm1.btnUndoClick(Sender: TObject);
begin
btnUndo.Enabled := PdfView1.FormCanUndo;
btnRedo.Enabled := PdfView1.FormCanRedo;
if PdfView1.FormCanUndo then
PdfView1.FormUndo;
end;
procedure TForm1.btnRedoClick(Sender: TObject);
begin
if PdfView1.FormCanRedo then
PdfView1.FormRedo;
end;
// ทั้งสามค่าจะเป็น True โดยค่าเริ่มต้นเพื่อให้แอปเดิมอัปเกรดได้โดยไม่
// เปลี่ยนพฤติกรรม สลับเป็น False สำหรับ kiosks, preview panes หรือ
// การฝังแบบอ่านอย่างเดียวโดยไม่ต้องสืบทอด TPdfView
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // ล้อเลื่อนขึ้น/ลงจะข้ามหน้า
PdfView1.AllowUserTextSelection := False; // การเลือกข้อความด้วยเมาส์/แป้นพิมพ์
// Ctrl+wheel ถูกสงวนไว้สำหรับซูม และตัวจัดการการนำทางจะข้ามมันไม่ว่า
// ค่า ChangePageOnMouseScrolling จะเป็นอย่างไร
// TPdfThumbnailView (unit FPdfThumbnail) แสดงภาพย่อของแต่ละหน้าเป็น
// หนึ่งแถวในแผงด้านข้างแบบเลื่อน
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // ไฮไลต์โทนอุ่น
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex เป็นแบบ zero-based
end;
procedure TForm1.PdfView1PageNumberเปลี่ยนแปลง(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// แบบที่ใช้โดย Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport,
// และ Demo\Lazarus\BatchExport หนึ่ง TPdf ต่อหนึ่งไฟล์,
// IPdfCancellationToken สำหรับการยกเลิกจาก UI, TPdfStreamAdapter สำหรับ
// การโหลดไฟล์แบบมีบัฟเฟอร์
uses FPdfAsync;
procedure ExportBatch(const Files: TArray<string>;
const OutDir: string; DPI: Integer; Quality: Integer;
const ACancelToken: IPdfCancellationToken);
var
I, J: Integer;
Job: TPdf;
Bmp: TBitmap;
Jpeg: TJPEGImage;
begin
for I := 0 to High(Files) do
begin
if ACancelToken.IsCancelled then Break;
OverallProgress.Position := I;
Job := TPdf.Create(nil);
try
Job.FileName := Files[I];
Job.Active := True;
PageProgress.Max := Job.PageCount;
for J := 1 to Job.PageCount do
begin
if ACancelToken.IsCancelled then Break;
PageProgress.Position := J;
Job.PageNumber := J;
Bmp := Job.RenderPage(
0, 0,
Round(Job.PageWidth * DPI / 72),
Round(Job.PageHeight * DPI / 72));
try
Jpeg := TJPEGImage.Create;
try
Jpeg.CompressionQuality := Quality;
Jpeg.Assign(Bmp);
Jpeg.SaveToFile(Format('%s\%s.p%.3d.jpg',
[OutDir, ExtractFileName(Files[I]), J]));
finally
Jpeg.Free;
end;
finally
Bmp.Free;
end;
end;
finally
Job.Free;
end;
end;
end;
procedure FitCurrentPage;
begin
PdfView1.Zoom := PdfView1.PageZoom[PdfView1.PageNumber];
end;
procedure InspectViewerPoint(X, Y: Integer);
var
PageNo: Integer;
PageX, PageY: Double;
CharIndex: Integer;
Ch: WString;
begin
PageNo := PdfView1.PageNumber;
if not PdfView1.DeviceToPage(X, Y, PageNo, PageX, PageY) then
Exit;
CharIndex := PdfView1.CharacterIndexAtPos(X, Y, 8, 8);
if CharIndex >= 0 then
begin
Ch := PdfView1.Text(CharIndex, 1);
ShowMessage(Format('Page %d, X %.2f, Y %.2f, character %s',
[PageNo, PageX, PageY, string(Ch)]));
end;
end;
function LoadPdfSafely(const FileName: string): Boolean;
begin
Result := False;
if not FileExists(FileName) then
Exit;
try
Pdf1.FileName := FileName;
Pdf1.Active := True;
Result := Pdf1.Active;
except
Result := False;
end;
end;
function ExtractTextSafely(PageNo: Integer): WString;
begin
Result := '';
if not Pdf1.Active then
Exit;
if (PageNo < 1) or (PageNo > Pdf1.PageCount) then
Exit;
Pdf1.PageNumber := PageNo;
Result := Pdf1.Text;
end;
// IsTagged เป็นการตรวจ catalog แบบเบา ๆ — จะเป็น True เมื่อมี /StructTreeRoot
// StructureElements ทำให้ tree กลายเป็นอาร์เรย์แบนของระเบียน
// TPdfStructureElement ที่มี type, title, alternate text, actual text,
// expansion text, language, level, parent index และจำนวน child / marked
// content / attribute
var
Elements: TPdfStructureElements;
I: Integer;
begin
if not Pdf1.IsTagged then
begin
Log.Add('Document is NOT tagged — accessibility tools may struggle.');
Exit;
end;
if Pdf1.Language = '' then
Log.Add('Tagged PDF without /Lang — fails PDF/UA.');
Elements := Pdf1.StructureElements;
for I := 0 to High(Elements) do
Log.Add(StringOfChar(' ', Elements[I].Level * 2)
+ Elements[I].StructType + ' "' + Elements[I].Title + '"');
end;
// SetPdfPrintMode เป็นฟังก์ชัน global ที่แมปกับ FPDF_SetPrintMode ของ PDFium
// ใช้ค่า TPdfPrintMode ที่ตรงกับไดรเวอร์เครื่องพิมพ์ที่ใช้งาน
SetPdfPrintMode(pmEmf); // GDI EMF (ค่าเริ่มต้น)
// SetPdfPrintMode(pmTextOnly); // การเรนเดอร์แบบข้อความล้วน
// SetPdfPrintMode(pmPostScript2); // PostScript ระดับ 2
// SetPdfPrintMode(pmPostScript3); // PostScript ระดับ 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks); // EMF + image masks
// SetPdfPrintMode(pmPostScript3Type42); // PostScript 3 + ฟอนต์ Type 42
// PrintCopies, PrintPageRanges, PrintScaling และ PrintPaperHandling เป็น
// รายการใน viewer-preference dictionary — มันเข้ารหัสสิ่งที่ผู้เขียน
// เอกสารต้องการให้ PDF reader ใช้เป็นค่าเริ่มต้น
var
Ranges: TPrintPageRanges;
Range: TPrintPageRange;
begin
ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // ช่วงแบบเริ่มที่ 1
for Range in Ranges do
Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));
if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
ApplyPrinterDevMode(PrinterDevMode);
end;
// JavaScript ระดับเอกสารถูกเก็บไว้ใน name tree /Names /JavaScript
// JavaScriptAction[i] คืนหนึ่งรายการ; JavaScriptActions คืนอาร์เรย์ทั้งหมด
var
I: Integer;
Action: TPdfJavaScriptAction;
begin
for I := 0 to Pdf1.JavaScriptActionCount - 1 do
begin
Action := Pdf1.JavaScriptAction[I];
Memo1.Lines.Add('Name: ' + string(Action.Name));
Memo1.Lines.Add('Script: ' + string(Action.Script));
Memo1.Lines.Add('---');
end;
end;
procedure ProcessMultiplePages;
var
I: Integer;
PageText: WString;
begin
for I := 1 to Pdf1.PageCount do
begin
Pdf1.PageNumber := I;
PageText := Pdf1.Text;
ProcessPageText(PageText);
end;
end;
procedure SetOptimalRenderOptions;
begin
PdfView1.Options := [reAnnotations, reLcd];
end;
// เส้นทาง render แบบไม่คัดลอก (ค่าเริ่มต้นตั้งแต่ v1.21.0): RenderPage
// เขียนตรงลงไปยัง buffer DIB ของ TBitmap ปลายทางเมื่อผู้เรียกส่ง bitmap
// ที่จองไว้ล่วงหน้า ทำให้ไม่ต้องคัดลอกหนึ่งชุดขนาด width * height * 4 bytes
procedure RenderIntoCallerBitmap(Bitmap: TBitmap);
begin
Bitmap.PixelFormat := pf32bit;
Bitmap.SetSize(2480, 3508); // A4 ที่ 300 DPI
Pdf1.RenderPage(Bitmap, 0, 0, Bitmap.Width, Bitmap.Height);
end;