PDFPipe

Accessibility and structure / The logical structure tree

The logical structure tree in a PDF

The tree of typed elements, Document at the root and paragraphs, lists, tables and figures below it, that describes what the document is made of.

Where this API stands

This API does not produce this standard, and there is no option that would. What follows is what the requirement actually is and where a rendered document stands against it. No /StructTreeRoot, no /ParentTree and no /RoleMap are written by this API, and no option asks for them. The part of this that is yours is the nesting in the source markup, because a tagger builds its tree by walking your DOM and mapping elements to structure types. Markup that nests the way the document is actually organised produces a tree that needs light correction. Markup where a table is a stack of flex rows, or a list is a series of paragraphs with bullet characters typed into them, produces a tree that is wrong in a way no automated pass can detect, because the content really does look like paragraphs.

What the standard requires

The requirements as a checker enforces them, rather than as a procurement document paraphrases them. A file satisfies all of these or it satisfies none of them.

  • /StructTreeRoot in the catalogue, with a /K array of children forming the tree and a /ParentTree mapping marked content back up it.
  • Standard structure element types: /Document, /P, /H1 to /H6, /L with /LI and /LBody, /Table with /TR, /TH and /TD, /Figure, /Link.
  • /RoleMap for any custom type, mapping it to a standard one, because a reader that meets an unknown tag with no mapping has no idea what to do with it.
  • Nesting that reflects the document rather than the layout: a list inside a table cell is /L inside /TD, not two unrelated runs of content that happen to sit near each other.
  • /ParentTreeNextKey kept consistent with the highest /StructParents value in use, which is the sort of bookkeeping that hand-built trees get wrong.

Who asks for this

Accessibility work needs it, PDF/A-1a and the other -a conformance levels require it, and anyone doing structured extraction from PDFs wants it because it is the difference between reading a table and guessing at one from coordinates.

What to do about it

Use the element that matches the meaning, every time: `table` for tabular data, `ul` and `ol` for lists, `h1` to `h6` for headings in order, `figure` and `figcaption` for images with captions. This is not a style preference, it is the input to a process that will happen later whether or not you plan for it. Once the markup is right, auto-tagging in Acrobat or pdfToolbox gets a simple document most of the way, and a person fixes tables and reading order.

How to check a file rather than assume

PAC 2024 renders the whole tree in a browsable panel, which is the fastest way to see whether it matches the document. `qpdf --json=latest` will dump the raw objects if you need to check /RoleMap or /ParentTree consistency. Acrobat's Tags panel is the tool people actually edit trees in.

The mistake people make here

Producing a tree where every element is /P. Auto-tagging a document with no semantic markup does exactly this: it produces a technically valid structure tree, containing one long undifferentiated run of paragraphs, which passes the presence check and helps nobody. The tree exists and it says nothing.

Frequently asked

Does this API produce The logical structure tree?

Not produced by this API. No /StructTreeRoot, no /ParentTree and no /RoleMap are written by this API, and no option asks for them. The part of this that is yours is the nesting in the source markup, because a tagger builds its tree by walking your DOM and mapping elements to structure types. Markup that nests the way the document is actually organised produces a tree that needs light correction. Markup where a table is a stack of flex rows, or a list is a series of paragraphs with bullet characters typed into them, produces a tree that is wrong in a way no automated pass can detect, because the content really does look like paragraphs.

Can I turn on the pdf_a option and be done?

No. The option writes an XMP packet declaring PDF/A-1b into the document catalogue, and the render response comes back carrying a warning that says in as many words that this is best-effort and that full conformance requires tagged structure and an embedded ICC profile. It is a claim written into the file, not a certificate earned by it, and a validator checks the claim against the bytes. Read the warning array in the response rather than taking the absence of an error as a pass.

Should I convert before rendering or after?

After, always. A conformance converter rewrites colour, embeds profiles, sets boxes and stamps metadata on a finished PDF. Trying to satisfy a conformance target from the source side means guessing at what the converter would have done, and the guesses that go wrong are expensive: flattening transparency you did not need to flatten changes what the page looks like.

Standards this one is confused with

The neighbours that matter are the ones people mistake for this, not the ones that sort next to it.

Conformance is decided after a file exists. Render the document, then check it, then convert only what the checker actually flags.