typst / typst/typst

Vector graphics inside figure() produce empty structure element in tagged PDF

Open
#8,826 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

accessibility bug pdf
Dominant language
Rust
Stars
56.1k
Forks
1.7k
Avg merge
3d 10h
Merged PRs (30d)
22

Description

Description

When exporting a tagged PDF (--pdf-standard ua-1), a figure() with alt: that contains only vector graphics (paths, strokes, fills — no text) produces an empty Figure structure element: /K[] with no /Pg reference. Screen readers cannot associate the alt text with the drawing because there are no marked content sequences linking the rendered paths to the Figure tag.

Figures containing text (e.g., axis tick labels in a chart) tag correctly — the text creates marked content items that get linked to the Figure element.

Minimal reproduction
// Works — figure contains text (tick labels from axes)
#figure(
  {
    import "@preview/lilaq:0.6.0" as lq
    import "@preview/tiptoe:0.4.0"
    lq.diagram(
      width: 200pt, height: 200pt,
      xlim: (-5, 5), ylim: (-5, 5),
      xaxis: (position: 0, tip: tiptoe.stealth, tick-distance: 1, subticks: none),
      yaxis: (position: 0, tip: tiptoe.stealth, tick-distance: 1, subticks: none),
    )
  },
  alt: "Empty coordinate plane with axes from -5 to 5",
)

// Broken — figure contains only vector paths (no text)
#figure(
  {
    import "@preview/lilaq:0.6.0" as lq
    lq.diagram(
      width: 200pt, height: 60pt,
      xlim: (0, 20), ylim: (-2, 2),
      xaxis: none, yaxis: none, grid: none,
      lq.plot((0, 4), (0, 0)),
      lq.plot((6, 10), (-1, 1)),
    )
  },
  alt: "Two line segments with different slopes",
)

Compile with:

typst compile --pdf-standard ua-1 repro.typ
Observed

Inspecting the PDF structure tree:

Working figure (has text from tick labels):

/Type/StructElem /S/Figure /Alt(...) /K[8 9 10 11 ...] /Pg 3 0 R

Broken figure (only vector paths):

/Type/StructElem /S/Figure /Alt(...) /K[]

The Figure element is created with the correct Alt attribute, but /K is empty — no marked content IDs are linked, and no /Pg page reference exists. VoiceOver and other assistive technology cannot reliably encounter the alt text because the figure has no page location.

Expected

All rendered content inside a figure() — including vector paths, strokes, and fills — should be wrapped in marked content sequences (/BMC.../EMC) and linked to the Figure structure element via /K. This would give the figure a page association and let screen readers present the alt text at the correct reading position.

Environment
  • Typst 0.15.1
  • macOS, tested with VoiceOver and PDF tag tree inspection via pikepdf
Notes

This affects any drawing library that produces pure vector output (lilaq plots without axes, CeTZ canvas drawings, etc.) when wrapped in figure(alt: ...). The alt text is present in the structure tree but effectively orphaned.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by compiling the two minimal reproductions with typst compile --pdf-standard ua-1 repro.typ and compare their PDF structure trees using pikepdf. Trace how the tagged figure() is associated with rendered content when text is present versus vector-only content. Done means vector-only figures have marked content linked through /K and a page association so assistive technology can locate the alt text.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
accessibility
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.