데모는 32개 주제로, Delphi 32개, C++Builder 31개, Lazarus 32개의 총 95개 네이티브 프로젝트입니다. 각 플랫폼의 메인 디렉터리에는 완전한 워크플로 데모 22개가 있습니다. 10개의 집중 주제는 Miscellaneous에 있으며 세 플랫폼 합계 29개 프로젝트입니다. XfaLab은 Delphi와 Lazarus에서 제공합니다
메인 디렉토리의 워크플로부터 먼저 열어 보세요. 기능 투어는 PDFViewer, 라이브 작성은 CreatePDF, 즉시 인벤토리는 ContentExtractionLab, 편집 가능한 폼은 FormFill, 검증과 변환은 StandardsLab, 자동화 intake는 IntakeWorkbenchCli가 맡습니다. 이 여섯 진입점은 곧바로 동작하는 결과를 만들어 내며, 아래의 집중 샘플은 API 하나를 자세히 들여다봐야 할 때 유용합니다
| 데모 | 용도 | 관련 데모와 다른 점 |
|---|---|---|
PDFViewer | 일반적인 보기와 기능 평가 | 메인 뷰어가 탐색, 검색, 썸네일, 표시 모드, 인쇄, 기능 쇼케이스를 하나로 묶습니다 |
MultiPageViewer | 연속 페이지, 펼침, 표지 인식 레이아웃 | 내장 뷰어 선택 API를 쓰는 집중 레이아웃 예제입니다. 더 폭넓은 뷰어 경험은 PDFViewer를 사용하세요 |
SplitView | 별도 창에서 페이지나 문서 비교 | 하나의 뷰에 두 페이지를 배치하는 방식이 아니라 독립적인 뷰 위치와 활성 창으로의 명령 전달을 보여줍니다 |
SearchAndSelect | 페이지 간 검색 결과와 선택 | 결과 목록과 검색 옵션이 뷰어 도구 모음보다 검색 워크플로를 더 직접적으로 드러냅니다 |
PrintPDF | 미리보기와 일반 인쇄 작업 | 페이지 범위, 크기 조정, 진행률, 취소가 주요 인쇄 워크플로를 제공합니다 |
ContentExtractionLab | 여러 콘텐츠 카테고리를 함께 조사 | TXT/JSON 인벤토리는 객체를 설명하고 텍스트를 미리 보여줄 뿐 완전한 텍스트나 이미지 파일 내보내기를 대체하지 않습니다 |
ExtractImages | 임베디드 이미지 객체 내보내기 | 페이지 전체 스크린샷이 아니라 이미지 비트맵을 추출하며, 내장 픽스처의 두 페이지에는 각각 실제 이미지 객체 두 개가 들어 있습니다 |
TableExtractionLab | 표 감지와 CSV/JSON 내보내기 | 괘선 표와 공백 정렬 표, 병합 셀, 연속 그룹, 신뢰도 제어는 일반 텍스트 추출과 독립적으로 동작합니다 |
Attachment | 임베디드 첨부 파일 읽기, 추가, 내보내기 | 첨부 파일 페이로드 조작은 ContentExtractionLab의 이름·크기 나열을 넘어섭니다 |
BatchExport | 여러 문서에서 페이지 이미지 내보내기 | 스트리밍 출력, 파일별 상태, 실패 격리로 단일 변환이 아닌 배치 처리를 보여줍니다 |
CreatePDF | 라이브 미리보기와 함께 페이지 콘텐츠 만들기 | 세 가지 네이티브 변형 모두 검색 가능한 텍스트, 벡터 도형, 실제 래스터 이미지, 표, 검토 주석을 만들며, 옵션은 명시적으로 저장하기 전에 미리보기를 다시 구성합니다 |
ImageToPDF | 입력 이미지 파일로 페이지 구성 | 이미지 배치, 맞춤, 순서, JPEG 처리가 문서 변환 워크플로를 제공합니다 |
MergePDF | 여러 PDF에서 페이지 조립 | 문서 가져오기와 순서 있는 구성을 보여주며, 분할은 반대 방향의 워크플로를 해결합니다 |
SplitPDF | 선택한 페이지 그룹을 개별 PDF로 기록 | 출력 이름 지정과 페이지 그룹 검증은 병합이나 페이지 이미지 렌더링과 다릅니다 |
WatermarkStamp | 기존 페이지에 반복 오버레이 적용 | 위치 지정과 페이지 일괄 스탬핑이 기본 작성 API를 보완합니다 |
FormFill | 대화형 AcroForm 입력과 flatten | 필드 입력과 저장된 appearance는 XFDF 교환이나 XFA 고유 동작과 다릅니다 |
AccessibilityReader | 대화형 읽기와 음성 | GUI 리더는 준수 감사가 아니라 읽기 탐색과 강조 표시를 보여줍니다 |
AccessibilityWorkbenchCli | 접근성 일괄 검토와 리포트 생성 | 발견 항목, 요약, 검토 게이트가 자동화를 지원하며, 발견 항목은 수동 접근성 검토를 대체하지 않습니다 |
StandardsLab | 표준별 검증과 변환 | 대화형 랩은 집계 리포트만 만드는 것이 아니라 작업과 옵션을 직접 드러냅니다 |
PreflightReport | 대화형 표준 리포트 | GUI로 수동 조사와 리포트 내보내기를 지원합니다 |
PreflightReportCli | 자동화 표준 리포트 | 배치 입력, 리포트 형식, 종료 코드 게이트가 PreflightReport의 자동화 짝을 제공합니다 |
IntakeWorkbenchCli | 일괄 intake, 분류, 라우팅 | 집계 리포트, 실패 게이트, 라우팅 결정으로 SecurityAudit GUI를 넘어서는 처리 워크플로입니다 |
이 프로젝트들은 Demo\Delphi\Miscellaneous, Demo\CBuilder\Miscellaneous, Demo\Lazarus\Miscellaneous에서 여세요. 메인 디렉터리는 완전한 작업 경험에 집중하고 여기는 유용한 API 사용법과 한정된 내보내기 작업을 유지합니다. 기본 Unicode 검사와 고정된 주석 작성/목록 안내는 문서 작성, 추출, XFDF 교환, API 테스트가 다루므로 별도의 데모 프로젝트는 두지 않습니다
| 데모 | 용도 | 관련 데모와 다른 점 |
|---|---|---|
SecurityAudit | 대화형 보안 상태 조사 | GUI는 파일 라우팅보다 권한, 스크립트, 서명, 보안 세부 사항에 중점을 둡니다 |
XfdfLab | XFDF 데이터 가져오기와 내보내기 | 교환과 가져온 PDF의 명시적 저장으로 지속성을 보여주며, XFDF만 내보내면 수정된 PDF는 저장되지 않습니다 |
AccessibilityLab | 콘솔에서 리더 API 둘러보기 | 간결한 실행형 안내로 읽기 단위, 단어 상자, 구조, 음성 스크립트 출력을 드러냅니다 |
ExtractText | 선택한 페이지의 완전한 텍스트 내보내기 | 텍스트 레이어 출력, 페이지 구분자, 취소는 인벤토리 미리보기나 표 감지와 구별되며 OCR은 수행하지 않습니다 |
FontProperties | 글꼴과 문자 메트릭 조사 | 첫 텍스트 객체의 실제 글꼴과 문자 메트릭이 표시된 상태로 시작하며, 문서 페이로드를 내보내지 않고도 다른 줄을 선택해 글꼴을 비교할 수 있습니다 |
NavigationLab | 북마크, destination, 탐색 액션 | 간결한 API 안내가 PDFViewer의 눈에 보이는 탐색 컨트롤을 보완합니다 |
PDFtoJpg | 문서 하나를 페이지 이미지로 렌더링 | 간결한 단일 문서 렌더링 예제입니다. Delphi는 개별 페이지 범위를 노출하고 BatchExport는 여러 입력 문서에 집중합니다 |
PrintPreferences | N-up 시트와 PDF 작성자 인쇄 환경설정 | 매수, 홀수/짝수 필터링, 작성자 제공 환경설정이 학습 초점이며 PrintPDF의 짝이 되는 예제입니다 |
ViewerInteractionLab | 마우스, 키보드, 좌표 상호작용 진단 | 상호작용 로그와 페이지/장치 좌표 변환으로 API 진단 도구 구실을 합니다 |
XfaLab | 정적 XFA 패킷과 런타임 기능 | 일반 런타임으로 template과 datasets 패킷을 엽니다. 대화형 XFA 렌더링에는 XFA 지원 런타임이 필요하며 Delphi와 Lazarus 변형이 제공됩니다 |
PDFViewer는 번들된 PDFiumDemoShowcase.pdf를 엽니다. 이 문서는 검색 가능한 텍스트, 벡터 차트, 임베디드 이미지 두 개, 표, AcroForm 필드 네 개, 챕터 북마크, 링크, 검토 노트, 파일 첨부 두 개를 담은 6페이지 문서입니다. 체험 바는 Restart showcase, Find live text, 2-page spread, Night reading, Inspect this PDF, Explore forms를 제공하며, 각 동작은 현재 문서에 적용됩니다
CreatePDF는 2페이지 리포트를 라이브 미리보기로 엽니다. 제목과 작성자를 편집하거나 도형, 표, 워터마크를 켜고 끄면 실제 페이지 콘텐츠가 다시 구성되고, Unicode 옵션은 세 번째 페이지를 추가합니다. 저장하기 전까지는 아무것도 기록되지 않으며, 저장한 PDF는 검색 가능한 상태로 남습니다
MultiPageViewer, SearchAndSelect, ContentExtractionLab, FormFill은 각자의 워크플로를 위해 같은 문서를 열며, 검색 결과, 추출 인벤토리, 편집 가능한 필드가 즉시 준비되어 있습니다. Miscellaneous\NavigationLab은 북마크와 destination API 안내를 위해 자체 로컬 사본을 사용합니다
Attachment는 실제 임베디드 파일 두 개로 시작합니다. PrintPreferences는 4페이지 문서와 함께 시작하며, 2부 인쇄, 선택한 페이지 범위, 배율, 양면 인쇄에 대한 작성자 인쇄 환경설정이 들어 있습니다. 현재 인쇄 워크플로는 부수와 페이지 범위를 적용하고, 배율과 양면 인쇄는 검사 후 보고됩니다. XfaLab은 정적 XFA 검사용 실제 template과 datasets 패킷으로 시작합니다. 대화형 XFA 렌더링은 적절한 런타임이 필요한 별개의 기능입니다
보기, 폼, 추출, 탐색 프로젝트는 각자 자체적인 PDFiumDemoShowcase.pdf를 포함합니다. 프로젝트 디렉토리를 복사할 때는 이 번들 입력을 배포할 실행 파일 옆에 함께 두세요. Lazarus 빌드는 이를 자동으로 staging합니다. 투어는 오프라인으로 동작하고, 로컬 PDF를 사용할 수 없을 때는 일반 뷰어와 추출 샘플이 생성된 폴백 콘텐츠를 제공합니다
모든 네이티브 데모는 자체 샘플 구현을 포함합니다. AccessibilityLab, AccessibilityWorkbenchCli, IntakeWorkbenchCli, PreflightReportCli의 Delphi와 Lazarus 진입점은 컴파일러별 초기화와 JPEG 작성을 포함한 전체 워크플로를 프로젝트 디렉토리 안에 담고 있습니다. Attachment, 인쇄 환경설정, XFA 예제도 기능별 입력을 로컬에서 직접 만듭니다. 복사한 프로젝트 밖에서 필요한 것은 라이브러리, 일치하는 네이티브 런타임, 일반적인 컴파일러 프레임워크 의존성뿐입니다
임베디드 비트맵이 필요하면 이미지 객체 추출을, 보이는 페이지가 필요하면 페이지 렌더링을, PDF에 이미 텍스트 레이어가 있다면 텍스트 추출을 선택하세요. 이런 카테고리를 나열하는 리포트는 전체 페이로드를 내보내지 않습니다. 이러한 경계 덕분에 모든 데모를 또 하나의 범용 뷰어나 워크벤치로 키우지 않고도 집중 예제가 유용하게 유지됩니다
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\Miscellaneous\FontProperties, Demo\CBuilder\Miscellaneous\FontProperties, Demo\Lazarus\Miscellaneous\FontProperties 프로젝트는 TPdf와 TPdfView 양쪽으로 동일한 메타데이터를 보여줍니다
Demo\Delphi\ContentExtractionLab, Demo\CBuilder\ContentExtractionLab, Demo\Lazarus\ContentExtractionLab 프로젝트는 콘텐츠 추출을 단일 워크플로로 전환합니다. 이 프로젝트는 PDF를 열거나 만들고, 사용자가 메타데이터, 페이지 텍스트, 페이지 객체, 이미지, 첨부 파일, 링크, 주석, 북마크, 글꼴 / 문자 메트릭을 선택하도록 한 다음 요약과 자세한 TXT 또는 JSON 리포트를 생성합니다. 또한 모두 / 없음 카테고리 선택, 리포트 클립보드 복사 및 소스 PDF 저장 액션을 포함하여 사용자가 생성된 샘플 PDF를 추출 리포트와 함께 보관할 수 있도록 합니다. ExtractImages와 Attachment는 페이로드 내보내기 워크플로를 제공하고, Miscellaneous\ExtractText와 Miscellaneous\FontProperties는 집중 API 예제로 남습니다. ExtractText는 Delphi, C++Builder, Lazarus 전체에서 페이지당 텍스트 출력을 생성하는 페이지 범위 및 페이지 구분자 옵션을 지원하고, ContentExtractionLab은 교차 기능 인벤토리 샘플입니다
Demo\Delphi\TableExtractionLab, Demo\CBuilder\TableExtractionLab, Demo\Lazarus\TableExtractionLab 프로젝트는 표 감지를 하나의 워크플로로 만듭니다. 랩은 병합된 헤더 셀이 있는 선 그리기 표, 선 없이 공백으로 정렬된 표, 페이지 경계를 넘어 갈라지는 긴 표를 포함한 샘플 문서를 만든 뒤 현재 페이지에는 ExtractTables를, 문서 전체에는 ExtractDocumentTables를 실행하고 행, 열, RowSpan과 ColumnSpan 값, 헤더 플래그, 감지 모드, 신뢰도, 연쇄 그룹을 보고합니다. DetectRuledTables, DetectWhitespaceTables, DetectContinuations, DetectFilledRulings, MinConfidence 옵션은 UI에서 편집할 수 있고 선택한 표는 TPdfTable.ToCsv와 TPdfTable.ToJson으로 내보냅니다. 랩은 폴더 전체를 일괄 처리하기도 합니다. Batch folder는 모든 PDF에 ExtractDocumentTables를 실행해 문서별 표 개수를 보고하고, 텍스트 레이어가 없는 스캔에 플래그를 붙이며, 빈 문서는 컴팩트 열 프리셋으로 재시도합니다. Export all CSV는 감지된 모든 표를 원본 파일 옆에 기록합니다. 각 문서는 자체 TPdf 인스턴스에서 열리므로 읽을 수 없는 한 파일이 배치 나머지에 영향을 줄 수 없습니다
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;
// Without flattening, FormField[i] := value writes only the /V entry —
// the visible appearance comes from the /AP stream and may not refresh
// GenerateFormAppearances regenerates /AP for every widget, then
// FlattenAllPages bakes every page's annotations and widgets into
// permanent, non-editable content
Pdf1.FileName := 'C:\Application.pdf';
Pdf1.Active := True;
Pdf1.FormField[0] := 'John Smith';
Pdf1.FormField[1] := '[email protected]';
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);
// Create a compressed PDF document
Pdf1.CreateDocument;
Pdf1.Compressed := True; // ensure all streams are FlateDecode compressed (default)
Pdf1.Active := True;
Pdf1.AddPage(0, 595, 842); // A4 size
Pdf1.AddText('Hello World!', 'Arial', 24, 100, 700, clBlack, $FF, 0.0);
Pdf1.SaveAs('C:\Compressed.pdf');
Pdf1.Active := False;
// Save with options
Pdf1.SaveAs('C:\Output.pdf', saIncremental); // incremental save
Pdf1.SaveAs('C:\NoSecurity.pdf', saRemoveSecurity); // remove security
// Save to a specific PDF version target. The PDF version validator rejects
// any feature introduced after the chosen target (JBIG2Decode, MarkInfo,
// AES encryption, Polygon / Caret / Watermark / Redact annotations, ...)
if not Pdf1.SaveAs('C:\Legacy15.pdf', saNone, pv15) then
ShowMessage('Save rejected: document contains features newer than PDF 1.5');
// SaveAsPdfA post-processes the base save with an incremental update that
// injects an XMP metadata stream, an sRGB ICC OutputIntent, and an updated
// document catalog so the result is PDF/A-1b conformant. The bundled
// sRGB IEC61966-2.1 profile is used automatically when no custom profile
// is supplied
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 with a custom ICC profile (CMYK printing pipelines for example)
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;
// In-memory PDF/A output
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;
// Cheap header-style probe — reads the conformance without iterating
// the full validator. Result: pacNone, pac1a, pac1b, pac2b, pac3b, or
// 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;
// Full validator with per-issue diagnostics
Validation := Pdf1.ValidatePdfA;
Memo1.Lines.Add('Conformance: ' + GetEnumName(TypeInfo(TPdfAConformance),
Ord(Validation.Conformance)));
for Issue in Validation.Issues do
Memo1.Lines.Add('- ' + Issue.Description);
end;
Demo\Delphi\StandardsLab, Demo\CBuilder\StandardsLab, Demo\Lazarus\StandardsLab 프로젝트는 PDF를 로드하거나 만든 후 ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, ValidatePdfVT를 실행한 다음, 감지된 각 준수 수준과 문제 수를 하나의 그리드에 표시합니다. 저장 버튼은 SaveAsPdfA, SaveAsPdfUa, SaveAsPdfE, SaveAsPdfX, SaveAsPdfR, SaveAsPdfVT를 호출하여 사용자 지정 하니스를 작성하지 않고도 마커 출력 워크플로를 볼 수 있습니다
FPdfPreflightReport 유닛은 ValidatePdfA, ValidatePdfUa, ValidatePdfE, ValidatePdfX, ValidatePdfR, ValidatePdfVT를 Info / Warning / Error 심각도, 상태 및 문제 집계, 조회, 검색, 필터링, 안정적인 SHA-256 지문을 갖춘 하나의 조회 가능한 리포트 객체로 래핑합니다. 리포트는 텍스트, HTML, Markdown, JSON, CSV, SARIF 2.1.0, JUnit XML, NDJSON 또는 네이티브 XML로 내보낼 수 있습니다. Demo\Delphi\PreflightReport, Demo\Lazarus\PreflightReport, Demo\CBuilder\PreflightReport는 대화형 선택 및 미리보기 워크플로를 제공하고, Demo\Delphi\PreflightReportCli는 스크립트 가능한 입력 / 출력 인수, 표준 필터링, 선택적 리포트 첨부 임베딩, CI 종료 코드 게이팅, 배치 목록 및 디렉토리 처리, 충돌 안전 라우팅, 집계 요약, CSV 매니페스트를 추가합니다. 생성된 리포트는 내장 검증기가 마커 수준과 선택된 파일 수준 검사를 다루므로, 전체 콘텐츠 수준 프리플라이트는 여전히 전용 검증 엔진에 위임할 수 있다고 명시합니다
// LoadCustomDocument lets PDFium pull blocks from any seekable TStream on
// demand. The stream is NOT copied into memory, so multi-GB PDFs, remote
// HTTP body streams, and database-backed sources are all viable without
// an up-front buffer
var
FileStream: TFileStream;
begin
// Read-from-file streaming (no in-memory copy)
FileStream := TFileStream.Create('C:\Huge.pdf',
fmOpenRead or fmShareDenyWrite);
Pdf1.LoadCustomDocument(FileStream, True{AOwnsStream});
// ^^ ownership transferred — Pdf1 frees FileStream on UnloadDocument
Pdf1.Active := True;
ShowMessage('Pages: ' + IntToStr(Pdf1.PageCount));
end;
// Externally-owned stream (caller frees it after 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 exposes IPdfCancellationToken /
// IPdfCancellationTokenSource for cancel-from-UI semantics that work
// across the long-running export and render paths
uses FPdfAsync;
var
CancelSource: IPdfCancellationTokenSource;
CancelToken: IPdfCancellationToken;
begin
CancelSource := PdfCancellationTokenSource;
CancelToken := CancelSource.Token;
// user clicks Cancel
CancelSource.Cancel;
// The worker periodically checks CancelToken.IsCancelled, or the render
// path polls it through PDFium's IFSDK_PAUSE callback
if CancelToken.IsCancelled then
Memo1.Lines.Add('Cancelled by user');
end;
// RenderPageProgressive lets a long high-DPI render abort mid-page when
// the user clicks Cancel, instead of blocking the caller until done
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> spins up a worker on a background thread and posts a
// TPdfFutureResult<T> envelope back to the main thread
//
// Important: TPdf serializes render calls per instance, but the worker still
// owns dispatch and document lifetime. Do not mutate a TPdf while a render is
// in flight; batch export usually keeps one TPdf per file
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 wraps PDFium's FS_MATRIX so callers can build a transform
// declaratively and hand it to FPDFPageObj_SetMatrix
uses FPdfMatrix, FPdfView;
var
M: TPdfMatrix;
RawMatrix: FS_MATRIX;
PageObj: FPDF_PAGEOBJECT;
begin
M := TPdfMatrix.Create;
try
M.Translate(72, 200); // move 1in right, ~2.8in up
M.Scale(0.5, 0.5); // half-size stamp
M.Rotate(15); // 15-degree CCW rotation
// M.HorizontalFlip;
// M.VerticalFlip;
// M.CentralFlip;
// M.Skew(10, 5);
// M.Multiply(Other); // post-multiply with another matrix
// Apply to any PDFium page object (text / path / image / form XObject)
RawMatrix := M.Handle;
FPDFPageObj_SetMatrix(PageObj, RawMatrix);
finally
M.Free;
end;
end;
// ImportPagesByIndex copies an explicit zero-based index array from a
// source PDF into this document. InsertAt = 0 inserts before page 1;
// PageCount appends
var
Source: TPdf;
begin
Source := TPdf.Create(nil);
try
Source.FileName := 'C:\Report.pdf';
Source.Active := True;
// Import pages 0, 2, 4 (one-based: pages 1, 3, 5) from Source
// and append them at the end of Pdf1
Pdf1.ImportPagesByIndex(Source, [0, 2, 4], Pdf1.PageCount);
// Empty array imports every source page
// Pdf1.ImportPagesByIndex(Source, [], 0);
finally
Source.Free;
end;
end;
// ImportNPagesToOne returns a brand-new TPdf whose pages are NumX*NumY
// composites of THIS document. OutputWidth/Height are PDF user units
// (1 unit = 1/72 in)
var
Composite: TPdf;
begin
Pdf1.FileName := 'C:\Slides.pdf';
Pdf1.Active := True;
// 4-up A4 landscape: 842 x 595 pt, 2 columns x 2 rows
Composite := Pdf1.ImportNPagesToOne(842, 595, 2, 2);
try
if Composite <> nil then
Composite.SaveAs('C:\Slides_4up.pdf');
finally
Composite.Free;
end;
end;
// MovePages takes the zero-based indices of the pages to move plus the
// destination index for the first moved page after the move completes
// Other pages shift around the moved block so the document length stays
// the same
//
// Example: in a 5-page document with pages numbered [0,1,2,3,4],
// MovePages([2, 3], 0) reorders to [2, 3, 0, 1, 4]
if not Pdf1.MovePages([2, 3], 0) then
ShowMessage('Invalid indices passed to MovePages');
// CreateXObjectFromPage manufactures a reusable Form XObject handle from
// any page of any other TPdf. Stamp that handle onto as many pages of
// THIS document as you want — each stamp is a single PDFium page object
// that can be positioned, scaled, and rotated through the 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;
// Wrap page 0 of WatermarkSource as a reusable 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;
// Centre the watermark on the page
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; // closes the FPDF_XOBJECT handle
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) accepts any format registered with the VCL or LCL
// graphics units (BMP, PNG via PngImage, JPG, ...)
Pdf1.AddImage('C:\Photo.png', 100, 500, 200, 150);
// AddImage(TBitmap) skips the TPicture intermediary, ideal when the
// bitmap already comes from rendering or batch generation work
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
// Paint a clYellow mask over every match on the view's current page
// Switching to a different page clears the highlight automatically
PdfView1.HighlightColor := clYellow;
MatchCount := PdfView1.HighlightSearchText('invoice', False{Case}, True{Word});
Status.Caption := Format('%d match(es) on this page', [MatchCount]);
// Programmatically remove the overlay before the page changes
PdfView1.ClearHighlight;
end;
// FitMode keeps long documents framed during Resize or page changes
// Setting Zoom directly cancels FitMode (it reverts to pfmNone)
PdfView1.FitMode := pfmFitPage; // whole page in viewport
// PdfView1.FitMode := pfmFitWidth; // page width matches viewport width
// PdfView1.FitMode := pfmActualSize; // 100%
// PageColor decouples the rendered PDF page from the host control's Color
// — typical dark-mode viewer setup keeps the scroll area dark while the
// PDF page stays paper-white
PdfView1.Color := clBlack; // scroll area
PdfView1.PageColor := clWhite; // PDF page background
// Optional drop shadow and page border to lift the page off a dark bg
PdfView1.PageShadowSize := 6;
PdfView1.PageShadowColor := $00404040;
PdfView1.PageBorderColor := clGray;
Demo\Delphi\Miscellaneous\ViewerInteractionLab, Demo\CBuilder\Miscellaneous\ViewerInteractionLab, Demo\Lazarus\Miscellaneous\ViewerInteractionLab 프로젝트는 현대적인 TPdfView 상호작용 표면을 하나의 컴팩트한 폼에 유지합니다. 샘플 PDF를 열거나 만들고, DisplayMode를 전환하고, FitMode를 적용하며, PageColor를 변경하고, 페이지를 회전하며, HighlightSearchText로 현재 페이지 검색 결과를 강조 표시하고, 사용자 텍스트 선택을 활성화 또는 비활성화하며, SelectedText를 검사하고, SelectAll, CopySelectionToClipboard, ClearSelection을 호출하며, 마우스가 페이지 위로 이동할 때 상태 표시줄에서 DeviceToPage 좌표가 업데이트되는 것을 관찰할 수 있습니다
Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect, Demo\Lazarus\SearchAndSelect 프로젝트는 검색 및 선택 뷰어 워크플로를 독립형 샘플로 전환합니다. PDF를 열거나 만들고, 페이지 번호, 문자 인덱스 및 미리보기 텍스트가 포함된 모든 페이지 검색 결과를 나열하며, 이전 / 다음 탐색 및 더블 클릭 결과 이동을 지원하고, HighlightSearchText로 현재 페이지 일치 항목을 강조 표시하며, 대소문자 구분 및 단어 단위 옵션을 노출하고, AllowUserTextSelection을 전환하며, SelectedText를 미리 보고, 버튼에서 SelectAll, CopySelectionToClipboard, ClearSelection을 구동하며, 사용자가 단일 및 펼침 표시 모드 간에 전환할 수 있도록 합니다
Demo\Delphi\Miscellaneous\PrintPreferences, Demo\CBuilder\Miscellaneous\PrintPreferences, Demo\Lazarus\Miscellaneous\PrintPreferences 프로젝트는 인쇄 전에 작성자 인쇄 환경설정을 읽습니다. 이들은 PrintCopies, PrintPageRanges, PrintScaling, PrintPaperHandling을 표시하고, 작성자가 제공한 매수와 범위를 인쇄 입력에 복사할 수 있으며, SetPdfPrintPaperHandlingDevMode가 PDF 양면 인쇄 의도를 Windows DEVMODE에 어떻게 매핑하는지 보여줍니다
Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF, Demo\Lazarus\PrintPDF 프로젝트는 표준 인쇄 워크플로를 보여줍니다. PDF 열기, 페이지 미리보기, 플랫폼 인쇄 대화상자에서 페이지 범위 및 매수 선택, 한 부씩 인쇄 모드 준수, TPdf.RenderPage를 통한 각 페이지 렌더링, 취소가 있는 인쇄 진행률 노출. Delphi 샘플은 또한 빠른 로딩을 위해 드롭된 PDF 파일을 받습니다
Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer, Demo\Lazarus\MultiPageViewer 프로젝트는 일반 페이지 탐색 및 줌 워크플로로 연속, 펼침, 표지 인식 보기를 실행합니다. 매우 큰 PDF에서 첫 페이지 열기 속도와 렌더링 응답성을 테스트하는 데에도 유용합니다. 대용량 파일 테스트에서 Acrobat이나 Foxit이 첫 번째 뷰까지 20-30초가 걸릴 수 있는 2.x GB 문서가 PDFium Delphi Component 뷰어 경로를 통해 거의 즉시 사용 가능해질 수 있습니다
Demo\Delphi\SplitView, Demo\CBuilder\SplitView, Demo\Lazarus\SplitView 프로젝트는 나란히 PDF 검토를 보여줍니다. C++Builder 및 Lazarus 샘플은 두 개 또는 세 개 창 비교, 활성 뷰 선택, 선택적 동기화된 탐색 명령, 공유 줌 사전 설정, 선택된 뷰 또는 로드된 모든 뷰의 회전에 중점을 둡니다. 첫 번째 사용 가능한 뷰와 후속 페이지 렌더링이 응답성을 유지하도록 하면서 큰 문서를 비교하려면 이러한 샘플을 사용하세요. Acrobat이나 Foxit이 열기까지 20-30초가 걸릴 수 있는 동일한 2.x GB PDF 파일이 PDFium Delphi Component 뷰어 경로에서는 거의 즉시 사용 가능해질 수 있습니다
Demo\Delphi\Miscellaneous\SecurityAudit, Demo\CBuilder\Miscellaneous\SecurityAudit, Demo\Lazarus\Miscellaneous\SecurityAudit는 PDF를 읽기 전용으로 열고 권한 상태, 첨부 파일, 문서 JavaScript action, URI/Launch 링크 주석, 웹 링크, 서명 요약, XFA 상태, V8 헬퍼 가용성, 지원하지 않는 기능의 Callback을 나열합니다. 같은 요약과 발견 항목을 TXT 감사 보고서로 저장하거나 복사하고 구조화된 JSON으로도 저장할 수 있습니다. 호스트 애플리케이션의 위험 패널로 쓰는 도구이며 맬웨어 탐지기는 아닙니다
// LinkOptions controls automatic handling of the four PDF link action
// types. Defaults enable goto and URI only; launch (run a program) and
// embedded GotoR (jump to another file) stay OFF so a stray click cannot
// run arbitrary code
PdfView1.LinkOptions :=
[loAutoGoto, loAutoOpenURI]; // safe defaults
// OnAnnotationLinkClick fires for in-document destinations (page jumps,
// named destinations, action chains). Mark Handled = True to prevent the
// view from auto-following the link
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; // suppress auto-handling
end;
end;
// OnWebLinkClick fires for URI strings discovered by PDFium's web-link
// scanner (auto-detected URLs in the text stream, even without an
// /A <</S/URI...>> annotation)
procedure TForm1.PdfView1WebLinkClick(Sender: TObject;
WebLinkIndex: Integer; const Url: WString;
var Handled: Boolean);
begin
Log.Add('Web link clicked: ' + string(Url));
end;
// On the focused AcroForm widget, six new methods drive PDFium's form
// edit history. All six short-circuit safely if no widget is focused or
// the document carries no 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;
// All three default to True so existing apps upgrade with no behaviour
// change. Flip them False for kiosks, preview panes, or read-only
// embeddings without subclassing TPdfView
PdfView1.AllowUserPageChange := False; // PgUp/PgDn/Ctrl+Home/End
PdfView1.ChangePageOnMouseScrolling := False; // wheel up/down jumps
PdfView1.AllowUserTextSelection := False; // mouse/keyboard text selection
// Ctrl+wheel is reserved for zoom and is skipped by the navigation
// handler regardless of ChangePageOnMouseScrolling.
// TPdfThumbnailView (unit FPdfThumbnail) renders one page thumbnail per
// row in a scrollable side panel
uses FPdfThumbnail;
procedure TForm1.FormCreate(Sender: TObject);
begin
ThumbView.Pdf := Pdf1;
ThumbView.ThumbnailWidth := 120;
ThumbView.ThumbnailHeight := 160;
ThumbView.SelectionColor := $00CFA85F; // warm highlight
ThumbView.OnPageClick := ThumbViewPageClick;
end;
procedure TForm1.ThumbViewPageClick(Sender: TObject; PageIndex: Integer);
begin
PdfView1.PageNumber := PageIndex + 1; // PageIndex is zero-based
end;
procedure TForm1.PdfView1PageNumberChanged(Sender: TObject);
begin
ThumbView.CurrentPageIndex := PdfView1.PageNumber - 1;
end;
// Pattern used by Demo\Delphi\BatchExport, Demo\CBuilder\BatchExport,
// and Demo\Lazarus\BatchExport. One TPdf per file, IPdfCancellationToken
// for cancel-from-UI, TPdfStreamAdapter for buffered file load
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 is a cheap catalog probe — True when /StructTreeRoot exists
// StructureElements materialises the tree into a flat array of
// TPdfStructureElement records with type, title, alternate text, actual
// text, expansion text, language, level, parent index, and child / marked
// content / attribute counts
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 is a global function that maps to PDFium's
// FPDF_SetPrintMode. Use the matching TPdfPrintMode value for the
// printer driver in use
SetPdfPrintMode(pmEmf); // GDI EMF (default)
// SetPdfPrintMode(pmTextOnly); // text-only rendering
// SetPdfPrintMode(pmPostScript2); // PostScript Level 2
// SetPdfPrintMode(pmPostScript3); // PostScript Level 3
// SetPdfPrintMode(pmPostScript2PassThrough);
// SetPdfPrintMode(pmPostScript3PassThrough);
// SetPdfPrintMode(pmEmfImageMasks); // EMF + image masks
// SetPdfPrintMode(pmPostScript3Type42); // PostScript 3 + Type 42 fonts
// PrintCopies, PrintPageRanges, PrintScaling, and PrintPaperHandling are
// the viewer-preference dictionary entries — they encode what the
// document author wanted PDF readers to default to
var
Ranges: TPrintPageRanges;
Range: TPrintPageRange;
begin
ShowMessage('Suggested copies: ' + IntToStr(Pdf1.PrintCopies));
ShowMessage('Print scaling: ' + GetEnumName(TypeInfo(TPrintScaling),
Ord(Pdf1.PrintScaling)));
Ranges := Pdf1.PrintPageRanges; // one-based ranges
for Range in Ranges do
Log.Add(Format('Pages %d..%d', [Range.First, Range.Last]));
if SetPdfPrintPaperHandlingDevMode(PrinterDevMode, Pdf1.PrintPaperHandling) then
ApplyPrinterDevMode(PrinterDevMode);
end;
// Document-level JavaScript is stored in the /Names /JavaScript name
// tree. JavaScriptAction[i] returns one entry; JavaScriptActions returns
// the full array
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;
// Zero-copy render path (default since v1.21.0): RenderPage writes
// directly into the destination TBitmap's DIB buffer when callers pass a
// pre-allocated bitmap, avoiding one width * height * 4 bytes copy
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;