THotPDF 预检报告方法

语法

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;

说明

CreatePreflightReport 读取输入 PDF,返回带轻量结构检查的文本报告。口令重载对受支持的加密 PDF 走既有的口令感知加载路径。SavePreflightReport 把同一报告写入目标文本文件。

ValidatePDFVT 运行聚焦的 PDF/VT 验证,只在所需标记与结构都存在时返回 True。返回的文本报告检查 pdfvtid:GTS_PDFVTVersion XMP 声明、PDF/VT 元数据命名空间、PDF/VT 修改日期与 xmp:ModifyDate 匹配、PDF/X 基础标记、无加密、PDF/X 输出意图、Catalog DPartRoot、DPartRootNode、NodeNameList、文档部分存在性、可加载页数,以及页面级 DPart 覆盖。PDF/VT 与 XMP 日期属性既可作为元素文本写出,也可作为 rdf:Description 元素上的 RDF 属性写出。

该报告检查 PDF header、EOF 标记、最后的 %%EOF 标记是否靠近文件末尾、最后一个 startxref 偏移、该偏移指向 xref 表还是 xref 流、%%EOF 与 startxref 标记数量、修订标记平衡与最终标记顺序、trailer 或 XRef 流标记、可加载页数、加密状态、catalog、页面树、页面对象标记、MediaBox、信息字典引用、root 引用、间接对象数、间接对象定义数、最高对象号、重复对象号计数、对象号唯一性、catalog 对象数、页面树对象数、页面对象数、页面树声明页数、页面树计数与可加载页一致性、页面对象与可加载页一致性、stream 与 endstream 平衡、流 /Length 条目覆盖、xref 表数量、xref 子区段数量、xref 表条目数、xref 空闲与在用条目数、畸形 xref 行数、xref 行有效性、xref 对对象号的覆盖、xref 流数量、对象流数量、trailer 数量、trailer /Size、trailer /Size 是否覆盖最高对象号、trailer /Root 与 /Info 间接引用、被引用对象是否已定义、trailer /Root 是否指向 catalog 对象、trailer /ID 数组条目数与配对结构、trailer /Root / /Info / /ID / /Encrypt 存在性、估算增量更新数、线性化状态、已加载页面框、表单字段数、资源字典数、字体资源数、嵌入字体程序数(/FontFile、/FontFile2、/FontFile3)、image XObject 数、form XObject 数、ColorSpace 资源数、注释数及 widget / link 注释子计数、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 互斥警告)、警告总数以及整体 pass / fail 摘要。这些辅助方法用于把文件交给外部标准验证器之前的快速进程内检查;不能替代完整的 PDF/A、PDF/X、PDF/UA 或 ISO 32000 预检验证引擎。

The Format overload accepts pfText (default plain-text path, byte-stable with the legacy overloads), pfJSON (CI-friendly JSON document), or pfHTML(适合仪表盘、自带内联 CSS 与按严重度着色行的 HTML 报告),或 pfCSV(面向电子表格的行,覆盖元数据、检查、提示、警告与汇总值)。JSON 输出把条目分组为input, size, pdfVersion, and xrefStyle top-level fields plus checks, info, hints, and warnings arrays and a summary object carrying failed, warnings, and result 值。每项检查、提示和警告还携带稳定的带命名空间 ruleId 与结构化 location 对象。location 始终包含逻辑 scope 与斜杠分隔的 path,可包含关联的 PDF key,并在报告能定位精确坐标时附加 objectNumber、pageIndex 或 byteOffset。CSV 输出使用稳定表头 category,name,status,value,spec,诊断有映射的标准条款时 spec 中放入 ISO 参考文本。内建 JSON 编码器转义", \, and control bytes; UTF-8 byte sequences pass through unchanged.

checks、hints 与 warnings 的每个条目都带可选 spec 字段,命名诊断针对的 ISO 条款(例如 trailer 相关检查用 ISO 32000-1 sec 7.5.5,PDF/A 与 JavaScript 互斥警告用 ISO 19005-1 sec 6.6.2)。该映射覆盖报告截至 v2.122.0 输出的每项检查、提示与警告。描述 pfJSON 输出结构的正式 JSON Schema(draft 2020-12)发布于 Docs/preflight-schema.json,下游消费者可在解析前校验输出。规则 ID 使用稳定形式 hotpdf.preflight.<category>.<rule-name>,CI 策略匹配规则不依赖译文措辞或数组位置

EmbedPreflightReportInPDF 把源 PDF 的副本写到 TargetFile,并在最后一个 %%EOF 后以 PDF 风格注释行(每行以 %  开头)追加旧式文本格式预检报告。PDF 阅读器忽略这些对象图之外的追加字节,但归档工作流、文本编辑器和 grep 类工具仍可找出嵌入报告供后续审计。原始对象图、交叉引用表和 trailer 不被修改。

LoadAndValidatePreflightReport extracts the report that EmbedPreflightReportInPDF previously appended, re-runs the current preflight algorithm against the source bytes preceding the embedded comment block, and compares the InputFingerprint values. The function returns True when both fingerprints match, indicating the embedded report still describes the file as it exists on disk When validation fails, callers can inspect OriginalReport and CurrentReport to diff what changed.

ComparePreflightReports emits a unified-diff-like body for two reports. Shared lines appear with a two-space prefix, lines unique to ReportA are prefixed with - , and lines unique to ReportB are prefixed with + . The algorithm is tuned for the well-formed key/value output produced by CreatePreflightReport where line order is deterministic; it is not a general-purpose LCS implementation but produces compact, readable diffs for the typical case of mostly-unchanged reports.

RepairPDFFromPreflightReport applies a conservative subset of byte-level repairs to a damaged PDF: it drops trailing bytes that follow the final %%EOF marker, and appends a missing %%EOF when one is not present anywhere. The function returns True when at least one repair was applied; RepairsApplied lists the repairs as one line per change. Object-graph repairs (rebuilding xref tables, patching stream lengths, fixing trailer dictionaries) are intentionally out of scope because such fixes can make a partially-recoverable file less recoverable; use a dedicated PDF recovery tool for deeper damage.

AggregatePreflightReports 接收逐文件报告数组并生成单一批次摘要。数组每个条目解析其 Input、Size 与 Summary 字段;汇总为每个文件输出一行,外加通过、失败与带警告报告的数量、扫描总字节数和整体结果计数。适合处理 PDF 目录、末尾想要一份简洁汇总的 CI 管线与 shell 循环。

LoadPreflightProfile and ApplyPreflightProfile let callers tailor the report output without re-running the analysis. The profile file uses an INI-style format with three optional sections: [disable-checks] lists check names to suppress; [disable-warnings] lists warning names to suppress; and [options] accepts hints=false to suppress every Hint line. After filtering, the Summary line is recomputed so the suppressed entries no longer contribute to the Failed and Warnings totals; the rest of the report passes through unchanged.

ValidatePreflightProfile 遍历已加载 profile 的 DisableChecks 与 DisableWarnings 列表,报告当前预检实现无法识别的任何名称。用于检测针对更新或更旧 HotPDF 版本编写的 profile 文件——否则这些文件因名称不匹配任何已输出检查或警告而静默禁用了nothing。

GetBuiltInPreflightProfile returns ready-to-use profile records for common workflows. Recognised names (case-insensitive): default / '' returns an empty profile; compact disables every Hint line for shorter reports; silent-actions disables every PDF 1.7 sec 12.6.4 action warning plus EmbeddedFile and RichMedia, intended for workflows that intentionally embed multimedia or interactive actions and do not want the preflight report to flag them. Unknown names also return an empty profile.

SavePreflightProfile writes a THPDFPreflightProfile record back to an INI file in the same format that LoadPreflightProfile consumes The two functions are exact inverses for well-formed profiles, so callers can build a 'load preset, tweak, save' workflow: P := GetBuiltInPreflightProfile('compact'), edit P.DisableChecks, then SavePreflightProfile(P, 'tuned.ini') for later reuse.

CreatePreflightReportWithProfile 是一站式便捷包装器,组合既有的 CreatePreflightReport、LoadPreflightProfile、ApplyPreflightProfile 与格式转换器;已有源 PDF、可选口令、profile 文件和目标格式的调用方一次调用即可,无需串联四步。传空 ProfileFile 跳过 profile 步骤。

MergePreflightProfiles 返回两个 profile 的去重并集:DisableChecks 与 DisableWarnings 收集两个输入中的名称,DisableHints 取逻辑或。适合把预设叠加到项目专用调整之上。

DiffPreflightProfiles reports the structural difference between two profiles. Returns True when they are equivalent; otherwise False with OnlyInA and OnlyInB filled with newline-separated lines of the form check:<name>, warn:<name>, or option:hints=false describing which entries are exclusive to each side.

EmbedPreflightReportAsXMP is an archival-friendly variant of EmbedPreflightReportInPDF: it writes a copy of the source PDF with the preflight report appended as a PDF-style comment block whose payload is XMP / RDF (xmlns:hotpdf="http://www.loslab.com/hotpdf/preflight/") Each report line becomes a hotpdf:<name> element with severity carried as an attribute on check rows XMP-aware tools that scan a file for xpacket markers can surface the embedded report; PDF readers continue to ignore the appended bytes because they fall outside the object graph. This is intentionally not a spec-compliant XMP integration: the XMP payload is not referenced from the catalog /Metadata entry, so XMP readers that only follow the catalog reference will not see it.

ConvertPreflightReportToVeraPDFStyle 把 HotPDF 预检报告整形为大致遵循 veraPDF 验证输出轮廓的 JSON 文档:顶层 profile 字段、含 itemDetails / taskResult / validationResult 的 jobs 数组,以及 validationResult.details 下的 ruleViolations 数组。它是 HotPDF 风格而非与 veraPDF 线上兼容;目标是让已消费 veraPDF JSON 的下游工具用最少的字段名重映射适配 HotPDF 输出,而不是重新学习一套完全不同的数据布局。每个 violation 携带与原生 JSON 报告相同的稳定 ruleId 与结构化 location

示例

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;

另请参阅