THotPDF Preflight Report Methods

Syntax

type
  THPDFPreflightFormat = (pfText, pfJSON, pfHTML, pfCSV);

function CreatePreflightReport(
  const SourceFile: TFileName;
  const Password  : AnsiString = ''): AnsiString; overload;
function CreatePreflightReport(
  const SourceFile: TFileName;
  const Password  : AnsiString;
  const Format    : THPDFPreflightFormat): AnsiString; overload;
procedure SavePreflightReport(
  const SourceFile: TFileName;
  const ReportFile: TFileName;
  const Password  : AnsiString = ''); overload;
procedure SavePreflightReport(
  const SourceFile: TFileName;
  const ReportFile: TFileName;
  const Password  :AnsiString;
  const Format    : THPDFPreflightFormat); overload;
function ValidatePDFVT(
  const SourceFile: TFileName;
  out Report      : AnsiString): Boolean;
function ValidatePDFVT(
  const SourceFile: TFileName;
  const Password  : AnsiString;
  out Report      : AnsiString): Boolean;
procedure EmbedPreflightReportInPDF(
  const SourceFile: TFileName;
  const TargetFile: TFileName);
procedure EmbedPreflightReportInPDF(
  const SourceFile: TFileName;
  const TargetFile: TFileName;
  const Password  : AnsiString);
function LoadAndValidatePreflightReport(
  const EmbeddedFile  : TFileName;
  out OriginalReport: AnsiString;
  out CurrentReport : AnsiString): Boolean;
function LoadAndValidatePreflightReport(
  const EmbeddedFile  : TFileName;
  const Password      : AnsiString;
  out OriginalReport: AnsiString;
  out CurrentReport : AnsiString): Boolean;
function ComparePreflightReports(
  const ReportA: AnsiString;
  const ReportB: AnsiString): AnsiString;
function RepairPDFFromPreflightReport(
  const SourceFile: TFileName;
  const TargetFile: TFileName;
  out RepairsApplied: AnsiString): Boolean;
function RepairPDFFromPreflightReport(
  const SourceFile: TFileName;
  const TargetFile: TFileName;
  const Password  : AnsiString;
  out RepairsApplied: AnsiString): Boolean;
function AggregatePreflightReports(
  const Reports: array of AnsiString): AnsiString;
type
  THPDFPreflightProfile = record
    DisableChecks  : AnsiString;
    DisableWarnings: AnsiString;
    DisableHints   : Boolean;
  end;
function LoadPreflightProfile(
  const ProfileFile: TFileName): THPDFPreflightProfile;
function ApplyPreflightProfile(
  const Report : AnsiString;
  const Profile: THPDFPreflightProfile): AnsiString;
function ValidatePreflightProfile(
  const Profile: THPDFPreflightProfile;
  out UnknownNames: AnsiString): Boolean;
function GetBuiltInPreflightProfile(
  const Name: AnsiString): THPDFPreflightProfile;
procedure SavePreflightProfile(
  const Profile    : THPDFPreflightProfile;
  const ProfileFile: TFileName);
function CreatePreflightReportWithProfile(
  const SourceFile : TFileName;
  const Password   : AnsiString;
  const ProfileFile: TFileName;
  const Format     : THPDFPreflightFormat): AnsiString;
function MergePreflightProfiles(
  const A, B: THPDFPreflightProfile): THPDFPreflightProfile;
function DiffPreflightProfiles(
  const A, B  : THPDFPreflightProfile;
  out OnlyInA: AnsiString;
  out OnlyInB: AnsiString): Boolean;
procedure EmbedPreflightReportAsXMP(
  const SourceFile: TFileName;
  const TargetFile: TFileName);
procedure EmbedPreflightReportAsXMP(
  const SourceFile: TFileName;
  const TargetFile: TFileName;
  const Password  : AnsiString);
function ConvertPreflightReportToVeraPDFStyle(
  const Report: AnsiString): AnsiString;

Description

CreatePreflightReport는 입력 PDF를 읽고 가벼운 구조 검사 결과를 텍스트 보고서로 반환합니다. 패스워드 오버로드는 지원되는 암호화 PDF에 대해 기존의 패스워드 인식 로드 경로를 사용합니다. SavePreflightReport는 같은 보고서를 대상 텍스트 파일에 기록합니다.

ValidatePDFVT는 PDF/VT에 초점을 맞춘 검증을 수행하며, 필요한 마커와 구조가 모두 있을 때만 True를 반환합니다. 반환되는 텍스트 보고서는 다음을 검사합니다: pdfvtid:GTS_PDFVTVersion XMP 선언, PDF/VT 메타데이터 네임스페이스, PDF/VT 수정 날짜와 xmp:ModifyDate의 일치 여부, PDF/X 기본 마커, 암호화 없음, PDF/X output intent, catalog의 DPartRoot, DPartRootNode, NodeNameList, document part 존재 여부, 로드 가능한 페이지 수, 페이지 단위 DPart 커버리지. PDF/VT와 XMP의 날짜 프로퍼티는 요소 텍스트로 쓰든 rdf:Description 요소의 RDF 속성으로 쓰든 상관없습니다.

보고서는 다음 항목을 검사합니다: PDF 헤더, EOF 마커, 마지막 %%EOF 마커가 파일 끝 근처에 있는지, 마지막 startxref 오프셋, 그 오프셋이 xref 테이블을 가리키는지 xref 스트림을 가리키는지, %%EOF와 startxref 마커 개수, 리비전 마커의 균형과 마지막 마커 순서, trailer 또는 XRef 스트림 마커, 로드 가능한 페이지 수, 암호화 상태, catalog, 페이지 트리, 페이지 객체 마커, MediaBox, information dictionary 참조, root 참조, indirect 객체 수, indirect 객체 정의 수, 가장 큰 객체 번호, 중복 객체 번호 수, 객체 번호의 고유성, catalog 객체 수, 페이지 트리 객체 수, 페이지 객체 수, 페이지 트리에 선언된 페이지 수, 페이지 트리 count와 로드 가능한 페이지 수의 일관성, 페이지 객체와 로드 가능한 페이지 수의 일관성, stream과 endstream의 균형, 스트림 /Length 항목 커버리지, xref 테이블 수, xref 서브섹션 수, xref 테이블 항목 수, xref free 및 in-use 항목 수, 손상된 xref 행 수, xref 행의 유효성, 객체 번호 대비 xref 커버리지, xref 스트림 수, object stream 수, trailer 수, trailer /Size, trailer /Size가 가장 큰 객체 번호를 포함하는지, trailer /Root와 /Info의 indirect 참조, 참조된 객체가 정의되어 있는지, trailer /Root가 catalog 객체를 가리키는지, trailer /ID 배열의 항목 수와 쌍 구조, trailer의 /Root, /Info, /ID, /Encrypt 존재 여부, 추정 증분 업데이트 수, 선형화 상태, 로드된 페이지 박스, 폼 필드 수, 리소스 사전 수, 폰트 리소스 수, 임베드된 폰트 프로그램 수(/FontFile, /FontFile2, /FontFile3), 이미지 XObject 수, 폼 XObject 수, ColorSpace 리소스 수, 주석 수와 위젯/링크 주석 하위 개수, FlateDecode, DCTDecode, CCITTFaxDecode, JBIG2Decode, LZWDecode, ASCIIHexDecode, ASCII85Decode, RunLengthDecode, JPXDecode의 필터 체인 사용 횟수, 폰트 임베딩 커버리지, 주석 수의 일관성, PDF/A / PDF/X / PDF/UA / Tagged PDF / 투명도 준수 힌트, 선택된 기능 플래그, JavaScript 또는 액션 경고(PDF 1.7 12.6.4절의 전체 액션 집합인 GoTo*, Thread, URI, Sound, Movie, Hide, Named, SubmitForm, ResetForm, ImportData, SetOCGState, Rendition, Trans, GoTo3DView는 물론, 기존의 JavaScript / Launch / OpenAction / AdditionalActions / EmbeddedFile / RichMedia 검사와 PDF/A 상호 배타 경고까지 포함), 경고 총계, 그리고 전체 통과/실패 요약 이 헬퍼들은 파일을 외부 표준 검증기에 넘기기 전에 프로세스 안에서 빠르게 확인하기 위한 것이며, 완전한 PDF/A, PDF/X, PDF/UA 또는 ISO 32000 preflight 검증 엔진을 대체하지는 않습니다.

Format 오버로드는 다음 형식을 받습니다: pfText(기본 일반 텍스트 경로로, 기존 오버로드와 바이트 단위로 동일), pfJSON(CI에 적합한 JSON 문서), pfHTML(대시보드에 바로 쓸 수 있는, 인라인 CSS와 심각도별 행 색상을 갖춘 독립형 HTML 보고서), pfCSV(메타데이터, 검사, 힌트, 경고, 요약 값을 담은 스프레드시트용 행). JSON 출력은 최상위 필드 input, size, pdfVersion, xrefStyle과 배열 checks, info, hints, warnings, 그리고 summary 객체로 항목을 묶으며, 여기에는 failed, warnings, result 값이 들어갑니다. 모든 check, hint, warning에는 안정적인 네임스페이스형 ruleId와 구조화된 location 객체도 함께 들어갑니다. location에는 항상 논리적 scope와 슬래시로 구분된 path가 있고, 관련 PDF key가 있을 수 있으며, 보고서가 정확한 좌표를 특정할 수 있으면 objectNumber, pageIndex, byteOffset이 추가됩니다. CSV 출력은 고정 헤더 category,name,status,value,spec을 사용하며, 진단 항목에 대응하는 표준 조항이 있으면 spec에 ISO 참조 텍스트를 넣습니다. 내장 JSON 인코더는 ", \, 제어 바이트를 이스케이프하고 UTF-8 바이트 시퀀스는 그대로 통과시킵니다.

checks, hints, warnings의 각 항목에는 진단이 대상으로 하는 ISO 조항을 알려 주는 선택적 spec 필드가 있습니다(예: trailer 관련 검사는 ISO 32000-1 sec 7.5.5, JavaScript가 포함된 PDF/A의 상호 배타 경고는 ISO 19005-1 sec 6.6.2). 이 매핑은 v2.122.0까지 보고서가 내보내는 모든 check, hint, warning을 다룹니다. pfJSON 출력 구조를 기술하는 정식 JSON Schema(draft 2020-12)는 Docs/preflight-schema.json으로 배포되므로, 다운스트림에서 출력을 파싱하기 전에 검증할 수 있습니다. Rule ID는 안정적인 형식 hotpdf.preflight.<category>.<rule-name>을 따르므로, CI 정책은 번역된 문구나 배열 위치에 의존하지 않고 규칙을 매칭할 수 있습니다

EmbedPreflightReportInPDF는 원본 PDF의 사본을 TargetFile에 쓰고, 기존 텍스트 형식 preflight 보고서를 마지막 %%EOF 뒤에 PDF 스타일 주석 줄(각 줄 앞에 %  접두사)로 덧붙입니다. 덧붙인 바이트는 객체 그래프 밖에 있으므로 PDF 리더는 무시하지만, 아카이브 워크플로, 텍스트 에디터, grep 같은 도구로는 나중에 감사할 때 임베드된 보고서를 그대로 꺼내 볼 수 있습니다. 원본 객체 그래프, cross-reference 테이블, trailer는 수정되지 않습니다.

LoadAndValidatePreflightReport는 EmbedPreflightReportInPDF가 앞서 덧붙인 보고서를 추출하고, 임베드된 주석 블록 앞의 원본 바이트에 대해 현재 preflight 알고리즘을 다시 실행한 뒤 InputFingerprint 값을 비교합니다. 두 fingerprint가 일치하면 True를 반환하며, 이는 임베드된 보고서가 디스크에 있는 파일의 현재 상태를 여전히 정확히 설명한다는 뜻입니다. 검증에 실패하면 호출자는 OriginalReport와 CurrentReport를 diff해서 무엇이 바뀌었는지 확인할 수 있습니다.

ComparePreflightReports는 두 보고서에 대해 unified diff와 비슷한 본문을 만듭니다. 양쪽에 공통인 줄은 공백 두 칸 접두사로, ReportA에만 있는 줄은 - 로, ReportB에만 있는 줄은 + 로 시작합니다. 이 알고리즘은 줄 순서가 결정적인, CreatePreflightReport가 만든 정형 key/value 출력에 맞춰 조정되어 있습니다. 범용 LCS 구현은 아니지만, 대부분 그대로인 보고서라는 일반적인 경우에는 간결하고 읽기 쉬운 diff를 만들어 냅니다.

RepairPDFFromPreflightReport는 손상된 PDF에 바이트 수준 복구 중 보수적인 일부만 적용합니다. 마지막 %%EOF 마커 뒤에 붙은 바이트를 잘라내고, %%EOF가 어디에도 없으면 하나를 덧붙입니다. 복구를 하나라도 적용했으면 True를 반환하며, RepairsApplied에는 변경마다 한 줄씩 복구 내역이 담깁니다. 객체 그래프 수준의 복구(xref 테이블 재구성, 스트림 길이 수정, trailer 사전 수정)는 부분적으로 복구 가능한 파일을 오히려 복구하기 어렵게 만들 수 있으므로 의도적으로 범위에서 제외했습니다. 더 심한 손상에는 전용 PDF 복구 도구를 사용하세요.

AggregatePreflightReports는 파일별 보고서 배열을 받아 하나의 배치 요약을 만듭니다. 배열의 각 항목에서 Input, Size, Summary 필드를 파싱하고, 파일마다 한 줄씩 출력하며, 통과·실패·경고 보고서 수, 스캔한 총 바이트 수, 전체 결과 카운트를 합산해 덧붙입니다. PDF가 든 디렉터리를 처리하면서 마지막에 간결한 요약 하나를 원하는 CI 파이프라인이나 셸 루프에서 유용합니다.

LoadPreflightProfile과 ApplyPreflightProfile을 쓰면 분석을 다시 실행하지 않고도 보고서 출력을 원하는 대로 조정할 수 있습니다. 프로필 파일은 세 개의 선택적 섹션을 가진 INI 스타일 형식입니다. [disable-checks]에는 숨길 check 이름을, [disable-warnings]에는 숨길 warning 이름을 나열하고, [options]에는 hints=false를 지정해 모든 Hint 줄을 숨길 수 있습니다. 필터링이 끝나면 Summary 줄을 다시 계산하므로, 숨긴 항목은 Failed와 Warnings 합계에 더 이상 반영되지 않습니다. 보고서의 나머지는 그대로 통과합니다.

ValidatePreflightProfile은 로드된 프로필의 DisableChecks와 DisableWarnings 목록을 훑어, 현재 preflight 구현이 인식하지 못하는 이름을 보고합니다. 더 새로운 또는 더 오래된 HotPDF 버전을 기준으로 작성된 프로필 파일은 이름이 어떤 check나 warning과도 맞지 않아 아무것도 비활성화하지 않은 채 조용히 넘어갈 수 있는데, 그런 경우를 찾아내는 데 유용합니다.

GetBuiltInPreflightProfile은 자주 쓰는 워크플로에 맞춘 즉시 사용 가능한 프로필 레코드를 반환합니다. 인식되는 이름(대소문자 구분 없음)은 다음과 같습니다. default / ''는 빈 프로필을 반환합니다. compact는 보고서를 짧게 만들도록 모든 Hint 줄을 비활성화합니다. silent-actions는 PDF 1.7 12.6.4절의 모든 액션 경고와 EmbeddedFile, RichMedia 경고를 비활성화하며, 멀티미디어나 인터랙티브 액션을 의도적으로 임베드하는 워크플로에서 preflight 보고서가 이를 지적하지 않았으면 할 때 쓰도록 만든 것입니다. 알 수 없는 이름도 빈 프로필을 반환합니다.

SavePreflightProfile은 THPDFPreflightProfile 레코드를 LoadPreflightProfile이 읽는 것과 같은 형식의 INI 파일로 다시 기록합니다. 올바른 형식의 프로필에서는 두 함수가 정확히 서로의 역함수이므로, '프리셋 로드, 수정, 저장' 워크플로를 만들 수 있습니다. P := GetBuiltInPreflightProfile('compact')로 가져와 P.DisableChecks를 편집한 다음, SavePreflightProfile(P, 'tuned.ini')로 저장해 두었다가 나중에 다시 쓰면 됩니다.

CreatePreflightReportWithProfile은 기존의 CreatePreflightReport, LoadPreflightProfile, ApplyPreflightProfile과 형식 변환기를 하나로 엮은 원스톱 편의 래퍼입니다. 원본 PDF, 선택적 패스워드, 프로필 파일, 대상 형식이 이미 있는 호출자는 네 함수를 차례로 부르는 대신 한 번의 호출로 끝낼 수 있습니다. ProfileFile에 빈 문자열을 전달하면 프로필 단계는 건너뜁니다.

MergePreflightProfiles는 두 프로필을 중복 제거하여 합집합으로 반환합니다. DisableChecks와 DisableWarnings는 양쪽 입력의 이름을 모두 모으고, DisableHints는 논리 OR로 합칩니다. 프로젝트별 조정 위에 프리셋을 겹쳐 쓸 때 유용합니다.

DiffPreflightProfiles는 두 프로필의 구조적 차이를 보고합니다. 두 프로필이 동등하면 True를 반환하고, 그렇지 않으면 False를 반환하면서 OnlyInA와 OnlyInB에 check:<name>, warn:<name>, option:hints=false 형태의 줄을 개행으로 구분해 채워, 어느 항목이 한쪽에만 있는지 알려 줍니다.

EmbedPreflightReportAsXMP는 아카이브에 적합한 EmbedPreflightReportInPDF의 변형입니다. 원본 PDF의 사본을 쓰면서, preflight 보고서를 페이로드가 XMP / RDF(xmlns:hotpdf="http://www.loslab.com/hotpdf/preflight/")인 PDF 스타일 주석 블록으로 덧붙입니다. 보고서의 각 줄은 hotpdf:<name> 요소가 되고, check 행의 심각도는 속성으로 담깁니다. 파일에서 xpacket 마커를 스캔하는 XMP 인식 도구는 임베드된 보고서를 꺼내 볼 수 있으며, 덧붙인 바이트가 객체 그래프 밖에 있으므로 PDF 리더는 계속 무시합니다. 이는 의도적으로 사양을 완전히 준수하는 XMP 통합이 아닙니다. XMP 페이로드가 catalog의 /Metadata 항목에서 참조되지 않기 때문에, catalog 참조만 따라가는 XMP 리더는 이 보고서를 보지 못합니다.

ConvertPreflightReportToVeraPDFStyle은 HotPDF preflight 보고서를 veraPDF 검증 출력의 큰 틀을 따르는 JSON 문서로 바꿉니다. 구성은 최상위 profile 필드, jobs 배열(각 항목에 itemDetails / taskResult / validationResult), 그리고 ruleViolations 배열(validationResult.details 아래)입니다. veraPDF와 와이어 호환되는 것이 아니라 HotPDF 스타일입니다. 이미 veraPDF JSON을 소비하는 다운스트림 도구가 전혀 다른 데이터 레이아웃을 새로 익히지 않고, 최소한의 필드 이름 재매핑만으로 HotPDF 출력에 적응하도록 하는 것이 목적입니다. 각 violation에는 네이티브 JSON 보고서와 동일한 안정적인 ruleId와 구조화된 location이 들어갑니다

Example

var
  PDF: THotPDF;
begin
  PDF := THotPDF.Create(nil);
  try
    PDF.SavePreflightReport('Input.pdf', 'PreflightReport.txt');
    PDF.SavePreflightReport('Encrypted.pdf', 'EncryptedReport.txt', 'user-password');
    PDF.SavePreflightReport('Input.pdf', 'PreflightReport.json', '', pfJSON);
    PDF.SavePreflightReport('Input.pdf', 'PreflightReport.html', '', pfHTML);
    PDF.SavePreflightReport('Input.pdf', 'PreflightReport.csv', '', pfCSV);
    PDF.EmbedPreflightReportInPDF('Input.pdf', 'Input-with-report.pdf');
    if not PDF.LoadAndValidatePreflightReport(
      'Input-with-report.pdf', OriginalReport, CurrentReport)
    then
      WriteLn('Embedded preflight report no longer matches the source.');
  finally
    PDF.Free;
  end;
end;

See Also