Технічна стаття

Технічна стаття: Build a PDF Intake Review Workbench in Delphi with PDFium українською

Ця локалізована версія зосереджується на Build a PDF Intake Review Workbench in Delphi with PDFium Component і використовує оновлену англійську статтю як технічну основу для команд Delphi, PDF і документального ПЗ

Сторінка перетворює оновлену базову статтю на конкретні контрольні точки для проєктування, реалізації та перевірки

Що синхронізовано з англійської статті

Англійський базовий матеріал розширено практичним контекстом, технічними рішеннями та конкретними прикладами, тому цю сторінку слід читати як робочий посібник, а не короткий огляд

Важливі розділи оновленої базової статті:

  • Спочатку використовуйте невеликі відтворювані вхідні файли
  • Залишайте назви продуктів, API, файлів і literal-значення без змін
  • Зберігайте результат валідатора й відомості про версії разом зі створеним зразком

Практичні рішення для реалізації

Починайте з типу файлу, очікуваного результату та стану помилки, який має побачити користувач. Далі прив'яжіть кожен виклик API до перевірного результату, щоб валідація, журнали та підтримка могли відтворити сценарій клієнта

  • Спочатку використовуйте невеликі відтворювані вхідні файли
  • Залишайте назви продуктів, API, файлів і literal-значення без змін
  • Зберігайте результат валідатора й відомості про версії разом зі створеним зразком

Код і точки API

Приклади коду залишено без змін, щоб розробники могли напряму порівняти їх із проєктами Delphi, C++Builder і Lazarus/FPC

procedure InspectIncoming(const IncomingPath: string; var Rec: TIntakeRecord);
var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := IncomingPath;
    Pdf.FormFill := False;     // no form environment, no JavaScript init
    Pdf.Active := True;        // failure is silent: Active simply stays False

    if not Pdf.Active then
    begin
      Rec.OpenFailed := True;  // damaged file or user-password lock
      Exit;                    // the finally block still runs
    end;

    Rec.PageCount := Pdf.PageCount;
    CollectIdentity(Pdf, IncomingPath, Rec);
    CollectRiskSignals(Pdf, Rec);
  finally
    Pdf.Active := False;
    Pdf.Free;                  // never leak the instance on a malformed file
  end;
end;
procedure CollectIdentity(Pdf: TPdf; const FilePath: string;
  var Rec: TIntakeRecord);
begin
  Rec.Title := Pdf.Title;             // Info dictionary value
  Rec.Author := Pdf.Author;
  Rec.CreatedAt := Pdf.CreationDate;  // raw PDF date string ("D:2026...")

  // An empty Info title does not mean the document is untitled. The
  // component does not expose the XMP packet, so probe the raw file
  // bytes for the dc:title element before trusting the blank.
  if (Rec.Title = '') and FileContainsText(FilePath, 'dc:title') then
    Include(Rec.Flags, ifTitleInXmpOnly);
end;
procedure CollectRiskSignals(Pdf: TPdf; var Rec: TIntakeRecord);
var
  i, PageNo: Integer;
  Ext: string;
begin
  Rec.IsEncrypted := Assigned(FPDF_GetSecurityHandlerRevision) and
    (FPDF_GetSecurityHandlerRevision(Pdf.Document) <> -1);
  Rec.HasForms := Pdf.FormType <> ftNone;
  Rec.IsXfa := Pdf.FormType = ftXfaFull;
  Rec.HasJavaScript := Pdf.JavaScriptActionCount > 0;

  // AnnotationCount is a per-page property; walk the pages to total
  // it. Loading a page object renders nothing, so this stays cheap.
  Rec.Annotations := 0;
  for PageNo := 1 to Pdf.PageCount do
  begin
    Pdf.PageNumber := PageNo;
    Inc(Rec.Annotations, Pdf.AnnotationCount);
  end;

  Rec.Attachments := Pdf.AttachmentCount;

  for i := 0 to Rec.Attachments - 1 do
  begin
    Ext := LowerCase(ExtractFileExt(string(Pdf.AttachmentName[i])));
    if (Ext = '.exe') or (Ext = '.js') or (Ext = '.vbs') or (Ext = '.dll') then
      Include(Rec.Flags, ifDangerousAttachment);
  end;
end;

Перевірка перед випуском

Перевіряйте вихідний файл тими самими інструментами, якими користуватиметься клієнт або архів. Записуйте версію компонента, тестові дані, версію валідатора та спостережений результат, щоб пізнішу регресію можна було точно відстежити

Додаткові нотатки

Це доповнення перетворює коротку версію на корисніший робочий матеріал і водночас залишається узгодженим із Build a PDF Intake Review Workbench in Delphi with PDFium Component та технічною базою англійської статті. Текст має чітко показувати, з якого типу вхідних даних починається тема, який результат очікується і в якій точці поведінку потрібно підтвердити через validation.

Під час переписування важливий порядок рішень: спочатку форма даних, потім межа зміни, далі залежності API, і лише після цього кінцева поведінка. Якщо стаття згадує кілька варіантів, варто пояснити, який шлях краще захищений для maintenance, support і відтворення проблеми.

Будь-який code block, назва файлу, назва API та literal-значення мають залишатися без змін. Навколишнє пояснення може бути ширшим, але приклад коду повинен бути точною опорою, щоб читач міг напряму порівняти його зі своїм Delphi-, C++Builder- або Lazarus/FPC-проєктом.

У розділі validation слід згадати малий файл-зразок, порівняння результату та фіксацію версії component або validator. Якщо сторінка описує bug fix чи migration, маршрут відтворення, початковий стан і точку підтвердження потрібно описати явно, щоб будь-який regression можна було відстежити без здогадок.

Таке розширення робить сторінку корисною не лише після першого прочитання: для reviewer це пояснення рішення, для support це діагностичний контекст, а для команди супроводу це помітка, на яку можна спертися перед наступними змінами.

  • Не змінюй назви продуктів, API, файлів і literal-значення
  • Code block, якщо є, лишай без змін
  • Пояснюй validation через зразок і порівнюваний результат
  • Показуй порядок рішень, а не лише дуже короткий підсумок