Technical Article

PDF/E-1 Engineering Documents in Delphi with PDFlibPas

PDF/E-1 is the archival profile for engineering documents, and PDFlibPas implements it as an author mode you switch on with SetPDFEMode plus a bounded preflight that reads content streams operator by operator. The profile is not PDF/A with a different label: it has its own identification namespace, its own lifecycle metadata requirement, and one rule that makes content validation stricter than any archival profile you have met

Engineering deliverables are why the profile exists. A drawing set that has to be readable and provably unchanged twenty years from now, with a revision history that survives, and with colour that means the same thing on the plotter in another building. Those requirements produce a specification whose demands sit mostly outside the page content, in metadata and colour management, which is exactly where a generic PDF writer gets them wrong

Its own identification, not a variation on PDF/A

The first thing to get right is that PDF/E-1 identification cannot be produced by adapting the PDF/A or PDF/X pattern. It uses a distinct XMP namespace, http://www.aim.org/pdfe/ns/id/, and the version value has to appear in two places: as a document information entry and as the namespace-qualified XMP property. Emitting only the XMP property, or only the information entry, produces a file that carries the intent and fails validation

The output intent has an equally specific shape. PDF/E-1 requires an embedded ICC profile with the subtype identifier ISO_PDFE1, and the profile must have a component count matching the device colour family the document actually uses. That last clause is where implementations quietly go wrong, because it means the intent cannot be chosen up front and then ignored

Why does device colour need a whole-document sweep?

Because colour spaces hide in resource dictionaries that a page-level scan never reaches. PDF/E-1 treats DeviceRGB and DeviceCMYK as mutually exclusive families for a document, so validating the profile means knowing every device colour space anything in the file uses. A form XObject has its own resources. So does a pattern, and so does an image. A tiling pattern inside a form XObject inside a page is three levels deep, and a validator that checks only the top-level page resources will pass a document that uses both families

The sweep therefore registers colour spaces whilst walking pages, forms, images and patterns as one traversal, and only then decides whether the document is coherent and whether the output intent matches. The same reasoning drives the preflight architecture generally: partial traversal produces false passes, and a false pass on a conformance check is worse than no check, because it is recorded as evidence

var
  Lib: TPDFlib;
  Diag: WideString;
begin
  Lib := TPDFlib.Create(nil);
  try
    Lib.LoadFromFile('assembly-drawings.pdf');

    if Lib.SetPDFEMode(1) = 0 then
      raise Exception.Create('PDF/E author mode was refused');

    // Author mode keeps the lifecycle metadata in step on every save.
    // Ask before saving whether the document would pass its own gate
    if not Lib.PDFEReadyForSave then
    begin
      Diag := Lib.GetPDFEDiagnostics;
      Writeln('PDF/E blockers: ', Diag);
      Exit;
    end;

    Lib.SaveToFile('assembly-drawings-pdfe.pdf');
  finally
    Lib.Free;
  end;
end;

Lifecycle metadata is a per-save obligation

PDF/E-1 asks for more than a document identifier. The minimum set includes the media management document identifier, a version identifier, a rendition class, creation time, modification time, metadata time and a title. That is a revision-tracking vocabulary, and it exists because an engineering deliverable is expected to be reissued rather than written once

The consequence for an implementation is that these fields cannot be set at document creation. If the modification time is written when you enable the mode and the document is edited afterwards, the XMP snapshot and the actual document state have drifted, and a validator comparing them reports an inconsistency nobody intended. Author mode therefore synchronises the fields immediately before every save, so the metadata describes the bytes about to be written rather than the bytes that existed when the mode was switched on

This is a general principle for conformance metadata and it is worth stating separately from PDF/E: derived metadata belongs on the save path, not the edit path. Any field computed from document state has to be recomputed at the moment the state is frozen, or it is a cache with no invalidation

PDFlibPas PDF/E-1 diagram of the whole-document device colour sweep that walks page, form XObject, tiling pattern and image resource dictionaries collecting DeviceRGB and DeviceCMYK families before judging coherence, beside the lifecycle metadata fields author mode re-synchronises immediately before every save so the XMP snapshot matches the bytes about to be written
Colour coherence can only be judged after one traversal reaches every resource dictionary, and derived lifecycle metadata is recomputed at the moment document state is frozen, not when the mode is switched on

The rule that makes content validation strict

PDF/E-1 does not allow the compatibility section operators to absorb unknown content. In ordinary PDF, BX and EX bracket a region in which a consumer must ignore operators it does not recognise, which is the escape hatch that lets a producer emit newer constructs without breaking older readers. Under PDF/E-1 that escape is closed, so any operator the preflight does not recognise is reported unconditionally, whether or not it sits inside a compatibility section

The effect on a validator is significant. It cannot skip regions it does not understand, which means the operand parser has to actually parse every operator in every content stream. That is where the bounds come in. The traversal is capped at 128 levels of nesting, one million objects and 64 MiB of content, and those limits are not performance tuning. A hostile or merely broken file can present an object graph with cycles or a nesting depth that turns a recursive validator into a stack overflow, and the limits are what keep a validation pass from becoming a denial-of-service vector. The same defensive posture is described in parsing untrusted PDFs safely

// Standalone validation of a file you did not produce, without loading
// it into a document instance
var
  Issues: TStringList;
  Stream: TFileStream;
  I: Integer;
begin
  Issues := TStringList.Create;
  Stream := TFileStream.Create('incoming.pdf', fmOpenRead or fmShareDenyWrite);
  try
    if CheckCompliancePDFE(Stream, '', 0, Issues) = 0 then
      for I := 0 to Issues.Count - 1 do
        Writeln('PDF/E: ', Issues[I]);
  finally
    Stream.Free;
    Issues.Free;
  end;
end;

What the save gate repairs and what it refuses

The gate splits its work into two stages, and the split is a usable design idea in its own right. First it normalises the things that are safely repairable: annotation print flags, the no-zoom and no-rotate flags on text annotations, and the appearance-generation flag on the form dictionary. These are settings with one correct value under the profile and no information content, so fixing them silently is right and refusing over them would be pedantry

Then it checks the constraints that cannot be repaired without changing what the document means: version, identification, encryption, output intent, device colour coherence and the presence of dynamic form content. A document failing any of those is refused, because inventing an output intent or picking a colour family on the author's behalf would produce a file that passes validation and misrepresents the content

PDFlibPas PDF/E-1 save gate diagram for Delphi showing the bounded preflight that scans every content stream operator under 128-level nesting, one-million-object and 64 MiB caps, silently repairs annotation print, zoom and rotate flags, refuses wrong version, identification, encryption, output intent, device colour or dynamic form content, and reports blockers through GetPDFEDiagnostics
The gate repairs silently only what carries no information, refuses every constraint a repair would distort, and turns refusal into a blocker list through GetPDFEDiagnostics before any bytes reach disk

Reading the diagnostics back through GetPDFEDiagnostics before saving turns that refusal into an actionable list rather than a failed operation. In a batch pipeline, call it on every document, log the blockers per file, and route the failures to a queue a human looks at. That is far more useful than a save that raises, because the blockers usually cluster: forty documents failing for the same missing output intent is one fix, not forty

Choosing between the archival profiles

PDF/E-1 is the right target when the deliverable is engineering documentation with a revision lifecycle, and specifically when device colour coherence matters because the output goes to plotters and large-format printers. PDF/A is the right target when the goal is long-term readability of documents in general, and it is the profile with the widest validator support. The two are not interchangeable, and a document can satisfy one and fail the other

PDFlibPas decision diagram comparing PDF/E-1 and PDF/A archival profiles for Delphi: PDF/E-1 for engineering deliverables with revision lifecycles, plotter colour and contractual validation under its own XMP namespace with ISO_PDFE1 output intent, PDF/A for general long-term readability with the widest validator support
Start from who validates the file at the far end: the profiles demand different identification, metadata and colour guarantees, and a document can satisfy one whilst failing the other

If you are choosing, start from who validates the file at the far end. PDF/A validation tooling is everywhere, and the corresponding preflight in PDFlibPas is described in PDF/A and PDF/UA preflight. PDF/E validation is more specialised and is usually a contractual requirement rather than a default. When an existing archive has to be brought up to a profile it was never written for, the metadata repair path in converting to PDF/A with metadata repair is the pattern to follow, and the same shape applies here: identify, repair what is safe, refuse the rest with a list

Author mode, the bounded content preflight and the standalone compliance check all ship with the PDFlibPas Delphi PDF library, so a document can be produced under the profile and independently verified afterwards through a separate code path, which is the only arrangement worth trusting for a conformance claim