Preflight Plugins and XML MRR
pfXML serializes built-in checks, warnings, hints, stable rule IDs, specification references, and object, page, or byte locations into the versioned urn:hotpdf:preflight:mrr:1 vocabulary
CreatePreflightMRR adds trusted in-process plugins for feature extraction and custom validation rules, while ValidatePDFWithPluginBundle combines those rules with an existing THPDFValidationPolicy
Register a bounded plugin
function EvaluateVendorRules(
const Context: THPDFPreflightPluginContext;
out PluginResult: THPDFPreflightPluginResult;
out ErrorMessage: AnsiString): Boolean;
begin
SetLength(PluginResult.Findings, 1);
PluginResult.Findings[0] := THPDFPreflightPluginFinding.Create(
'vendor.metadata.required', 'Vendor metadata is present', True, ppsError);
PluginResult.Findings[0].Scope := 'metadata';
PluginResult.Findings[0].Path := '/Vendor';
ErrorMessage := '';
Result := True;
end;
Limits := THPDFPreflightPluginLimits.Default;
SetLength(Plugins, 1);
Plugins[0] := THPDFPreflightPluginDescriptor.Create(
'vendor-policy', '1.0', [ppcValidationRules], EvaluateVendorRules);
XML := PDF.CreatePreflightMRR('Input.pdf', '', Plugins, Limits);
Determinism and trust boundary
HotPDF validates unique ASCII plugin IDs and ABI version 1, executes plugins in stable ID order, sorts returned features and findings, omits timing jitter from the report, escapes XML values, and rejects undeclared capabilities or invalid rule IDs
Limits bound plugin count, feature count, finding count, aggregate string bytes, XML output bytes, per-plugin elapsed time, and total elapsed time; a rejected plugin contributes a deterministic execution finding and does not consume shared feature, finding, or string budgets
Plugins run inside the caller process and receive the source path, password, base text report, policy name, limits, and cancellation token, so only trusted plugin code should be registered
Elapsed limits are checked after an evaluator returns because an in-process callback cannot be preempted safely; long-running evaluators should poll Context.CancellationToken and return promptly
See THotPDF.CreatePreflightMRR, THotPDF.ValidatePDFWithPluginBundle, THPDFPreflightPluginDescriptor, and THPDFPreflightPluginLimits