PDFium Delphi Component Docs

PDFium VCL 프로그래밍 예제

실행 가능한 데모 선택

데모는 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를 넘어서는 처리 워크플로입니다

Miscellaneous의 집중 샘플

이 프로젝트들은 Demo\Delphi\Miscellaneous, Demo\CBuilder\Miscellaneous, Demo\Lazarus\Miscellaneous에서 여세요. 메인 디렉터리는 완전한 작업 경험에 집중하고 여기는 유용한 API 사용법과 한정된 내보내기 작업을 유지합니다. 기본 Unicode 검사와 고정된 주석 작성/목록 안내는 문서 작성, 추출, XFDF 교환, API 테스트가 다루므로 별도의 데모 프로젝트는 두지 않습니다

데모용도관련 데모와 다른 점
SecurityAudit대화형 보안 상태 조사GUI는 파일 라우팅보다 권한, 스크립트, 서명, 보안 세부 사항에 중점을 둡니다
XfdfLabXFDF 데이터 가져오기와 내보내기교환과 가져온 PDF의 명시적 저장으로 지속성을 보여주며, XFDF만 내보내면 수정된 PDF는 저장되지 않습니다
AccessibilityLab콘솔에서 리더 API 둘러보기간결한 실행형 안내로 읽기 단위, 단어 상자, 구조, 음성 스크립트 출력을 드러냅니다
ExtractText선택한 페이지의 완전한 텍스트 내보내기텍스트 레이어 출력, 페이지 구분자, 취소는 인벤토리 미리보기나 표 감지와 구별되며 OCR은 수행하지 않습니다
FontProperties글꼴과 문자 메트릭 조사첫 텍스트 객체의 실제 글꼴과 문자 메트릭이 표시된 상태로 시작하며, 문서 페이로드를 내보내지 않고도 다른 줄을 선택해 글꼴을 비교할 수 있습니다
NavigationLab북마크, destination, 탐색 액션간결한 API 안내가 PDFViewer의 눈에 보이는 탐색 컨트롤을 보완합니다
PDFtoJpg문서 하나를 페이지 이미지로 렌더링간결한 단일 문서 렌더링 예제입니다. Delphi는 개별 페이지 범위를 노출하고 BatchExport는 여러 입력 문서에 집중합니다
PrintPreferencesN-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에 이미 텍스트 레이어가 있다면 텍스트 추출을 선택하세요. 이런 카테고리를 나열하는 리포트는 전체 페이로드를 내보내지 않습니다. 이러한 경계 덕분에 모든 데모를 또 하나의 범용 뷰어나 워크벤치로 키우지 않고도 집중 예제가 유용하게 유지됩니다

기본 문서 작업

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;

새 PDF 문서 만들기

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;

텍스트 작업

PDF에서 텍스트 추출

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');

PDF/A-1b 아카이브 출력 만들기

// 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;

PDF/A 준수성 검증

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;

StandardsLab에서 다중 PDF 표준 검사

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로 PDF를 온디맨드 로드

// 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로 백그라운드 스레드에서 렌더링 실행

// 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;

변환 행렬

Translate / Scale / Rotate / Skew 구성

// 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;

N-up 합성 출력

// 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');

Form XObject 페이지 재사용을 통한 워터마킹

// 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 직접 호출 오버로드)

등록된 이미지 파일 또는 TBitmap 추가

// 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;

ViewerInteractionLab에서 뷰어 상호작용 탐색

Demo\Delphi\Miscellaneous\ViewerInteractionLab, Demo\CBuilder\Miscellaneous\ViewerInteractionLab, Demo\Lazarus\Miscellaneous\ViewerInteractionLab 프로젝트는 현대적인 TPdfView 상호작용 표면을 하나의 컴팩트한 폼에 유지합니다. 샘플 PDF를 열거나 만들고, DisplayMode를 전환하고, FitMode를 적용하며, PageColor를 변경하고, 페이지를 회전하며, HighlightSearchText로 현재 페이지 검색 결과를 강조 표시하고, 사용자 텍스트 선택을 활성화 또는 비활성화하며, SelectedText를 검사하고, SelectAll, CopySelectionToClipboard, ClearSelection을 호출하며, 마우스가 페이지 위로 이동할 때 상태 표시줄에서 DeviceToPage 좌표가 업데이트되는 것을 관찰할 수 있습니다

SearchAndSelect에서 텍스트 검색 및 선택

Demo\Delphi\SearchAndSelect, Demo\CBuilder\SearchAndSelect, Demo\Lazarus\SearchAndSelect 프로젝트는 검색 및 선택 뷰어 워크플로를 독립형 샘플로 전환합니다. PDF를 열거나 만들고, 페이지 번호, 문자 인덱스 및 미리보기 텍스트가 포함된 모든 페이지 검색 결과를 나열하며, 이전 / 다음 탐색 및 더블 클릭 결과 이동을 지원하고, HighlightSearchText로 현재 페이지 일치 항목을 강조 표시하며, 대소문자 구분 및 단어 단위 옵션을 노출하고, AllowUserTextSelection을 전환하며, SelectedText를 미리 보고, 버튼에서 SelectAll, CopySelectionToClipboard, ClearSelection을 구동하며, 사용자가 단일 및 펼침 표시 모드 간에 전환할 수 있도록 합니다

PrintPreferences에서 PDF 인쇄 환경설정 읽기

Demo\Delphi\Miscellaneous\PrintPreferences, Demo\CBuilder\Miscellaneous\PrintPreferences, Demo\Lazarus\Miscellaneous\PrintPreferences 프로젝트는 인쇄 전에 작성자 인쇄 환경설정을 읽습니다. 이들은 PrintCopies, PrintPageRanges, PrintScaling, PrintPaperHandling을 표시하고, 작성자가 제공한 매수와 범위를 인쇄 입력에 복사할 수 있으며, SetPdfPrintPaperHandlingDevMode가 PDF 양면 인쇄 의도를 Windows DEVMODE에 어떻게 매핑하는지 보여줍니다

PrintPDF에서 표준 PDF 인쇄

Demo\Delphi\PrintPDF, Demo\CBuilder\PrintPDF, Demo\Lazarus\PrintPDF 프로젝트는 표준 인쇄 워크플로를 보여줍니다. PDF 열기, 페이지 미리보기, 플랫폼 인쇄 대화상자에서 페이지 범위 및 매수 선택, 한 부씩 인쇄 모드 준수, TPdf.RenderPage를 통한 각 페이지 렌더링, 취소가 있는 인쇄 진행률 노출. Delphi 샘플은 또한 빠른 로딩을 위해 드롭된 PDF 파일을 받습니다

MultiPageViewer에서 빠른 다중 페이지 보기

Demo\Delphi\MultiPageViewer, Demo\CBuilder\MultiPageViewer, Demo\Lazarus\MultiPageViewer 프로젝트는 일반 페이지 탐색 및 줌 워크플로로 연속, 펼침, 표지 인식 보기를 실행합니다. 매우 큰 PDF에서 첫 페이지 열기 속도와 렌더링 응답성을 테스트하는 데에도 유용합니다. 대용량 파일 테스트에서 Acrobat이나 Foxit이 첫 번째 뷰까지 20-30초가 걸릴 수 있는 2.x GB 문서가 PDFium Delphi Component 뷰어 경로를 통해 거의 즉시 사용 가능해질 수 있습니다

SplitView에서 나란히 검토

Demo\Delphi\SplitView, Demo\CBuilder\SplitView, Demo\Lazarus\SplitView 프로젝트는 나란히 PDF 검토를 보여줍니다. C++Builder 및 Lazarus 샘플은 두 개 또는 세 개 창 비교, 활성 뷰 선택, 선택적 동기화된 탐색 명령, 공유 줌 사전 설정, 선택된 뷰 또는 로드된 모든 뷰의 회전에 중점을 둡니다. 첫 번째 사용 가능한 뷰와 후속 페이지 렌더링이 응답성을 유지하도록 하면서 큰 문서를 비교하려면 이러한 샘플을 사용하세요. Acrobat이나 Foxit이 열기까지 20-30초가 걸릴 수 있는 동일한 2.x GB PDF 파일이 PDFium Delphi Component 뷰어 경로에서는 거의 즉시 사용 가능해질 수 있습니다

SecurityAudit에서 PDF 위험 표면 감사

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를 TPdfView에 연결

// 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;

협력적으로 취소 가능한 일괄 내보내기

파일별 및 페이지별 진행률이 있는 다중 파일 PDF에서 JPG로

// 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;

오류 처리

견고한 PDF 작업

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;

Tagged PDF 및 접근성

Tagged PDF 감지 및 구조 트리 탐색

// 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;

인쇄 모드 선택

EMF / PostScript / 이미지 마스크 인쇄 모드 설정

// 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;

문서 JavaScript 액션

이름 트리의 JavaScript 열거

// 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;

성능 팁

PDF 작업 최적화

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;