Skip to content

Draw what a document says it shows, and no more - #18

Merged
tannevaled merged 1 commit into
mainfrom
feat/optional-content
Aug 27, 2026
Merged

Draw what a document says it shows, and no more#18
tannevaled merged 1 commit into
mainfrom
feat/optional-content

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Optional content is how a document says that some of what it contains is a
layer, and which layers are on when it is opened. A page's content is not a
promise that all of it is to be drawn. None of it was read here: every layer
was drawn, whatever the document said.

What is read is the default configuration, /OCProperties/D — /BaseState, /ON
and /OFF — and then the three places a layer is named: an /OC on a form or
image XObject, an /OC on an annotation, and BDC /OC around a run of marks in a
content stream, looked up through the page's /Resources/Properties or written
out in place. A membership dictionary combines several groups under /P, whose
four policies are all implemented. Options.AllLayers asks for the old
behaviour, for a tool with a layer panel or an archive comparing what a file
holds against what it displays.

WHAT THE CORPUS SAYS, WHICH IS LESS THAN IT FIRST APPEARED

Optional content is common. 275 of the 1 633 real forms carry an /OCProperties,
as do 151 of 6 667 arXiv files; 161 of those forms hide at least one group, and
all 123 that name a /BaseState name /OFF, which says nothing is shown unless it
is listed as on. 155 XObjects and 1 373 annotations on the forms' first three
pages carry an /OC of their own.

Reading all of that moves not one pixel. Walking every page of all 8 300 files
and counting the operators that mark the page, not one falls inside a layer
the document's own configuration hides
: the layers are declared and left
empty, which is what an authoring tool leaves behind. Rendering the 1 633 forms
before and after gives identical pages and the same total ink to the unit —
88 770 158 either way.

One arXiv figure came closest: 2603.24071/Figure2.pdf declares five groups,
hides all five, and does name one of them in a BDC. The renderer identifies
that section as hidden — and the section contains none of the page's 165
marking operators. Empty as well.

So this is not a fix for anything the corpus shows going wrong, and it is not
written up as one. It is here because a document's statement about what it
shows should be obeyed, and because this corpus is forms and figures: layers
are how a CAD drawing, a map, a multilingual overlay and a print-only mark are
written, and none of those are in it. The measurement says the corpus does not
exercise the mechanism, which is a different claim from the mechanism not
mattering. The tests are therefore the only thing holding the behaviour up, and
they are written accordingly: a page for every path, and the policies unit by
unit.

TWO DECISIONS WORTH ARGUING WITH

/OFF wins over /ON where a document names a group in both. The specification
leaves it open; hiding is the safer reading, since showing what a document
tried to hide is the worse mistake.

A /VE visibility expression is not read. It takes precedence over /P where a
document has both, and not one file in the corpus carries one — so reading it
would be guesswork tested against nothing. A document that has one is treated
as visible, which is what this did before.

Suppression is of marks only. A clip narrowed inside a hidden layer still
narrows, because clipping is not marking; the operators that move the text pen
still move it. Otherwise a hidden layer would shift everything after it.

readOptional drops the error from Catalog(): reader.Open refuses a file whose
trailer does not lead to a catalogue, so it cannot happen. An unreachable
branch cannot be tested, and an untested branch in the code that decides what a
reader is shown is worse than no branch — so it is gone rather than covered by
a contrived case.

100% statement coverage, go vet and -race clean, nine cross-compile targets.

Optional content is how a document says that some of what it contains is a
layer, and which layers are on when it is opened. A page's content is not a
promise that all of it is to be drawn. None of it was read here: every layer
was drawn, whatever the document said.

What is read is the default configuration, /OCProperties/D — /BaseState, /ON
and /OFF — and then the three places a layer is named: an /OC on a form or
image XObject, an /OC on an annotation, and BDC /OC around a run of marks in a
content stream, looked up through the page's /Resources/Properties or written
out in place. A membership dictionary combines several groups under /P, whose
four policies are all implemented. Options.AllLayers asks for the old
behaviour, for a tool with a layer panel or an archive comparing what a file
holds against what it displays.

WHAT THE CORPUS SAYS, WHICH IS LESS THAN IT FIRST APPEARED

Optional content is common. 275 of the 1 633 real forms carry an /OCProperties,
as do 151 of 6 667 arXiv files; 161 of those forms hide at least one group, and
all 123 that name a /BaseState name /OFF, which says nothing is shown unless it
is listed as on. 155 XObjects and 1 373 annotations on the forms' first three
pages carry an /OC of their own.

Reading all of that moves not one pixel. Walking every page of all 8 300 files
and counting the operators that mark the page, **not one falls inside a layer
the document's own configuration hides**: the layers are declared and left
empty, which is what an authoring tool leaves behind. Rendering the 1 633 forms
before and after gives identical pages and the same total ink to the unit —
88 770 158 either way.

One arXiv figure came closest: 2603.24071/Figure2.pdf declares five groups,
hides all five, and does name one of them in a BDC. The renderer identifies
that section as hidden — and the section contains none of the page's 165
marking operators. Empty as well.

So this is not a fix for anything the corpus shows going wrong, and it is not
written up as one. It is here because a document's statement about what it
shows should be obeyed, and because this corpus is forms and figures: layers
are how a CAD drawing, a map, a multilingual overlay and a print-only mark are
written, and none of those are in it. The measurement says the corpus does not
exercise the mechanism, which is a different claim from the mechanism not
mattering. The tests are therefore the only thing holding the behaviour up, and
they are written accordingly: a page for every path, and the policies unit by
unit.

TWO DECISIONS WORTH ARGUING WITH

/OFF wins over /ON where a document names a group in both. The specification
leaves it open; hiding is the safer reading, since showing what a document
tried to hide is the worse mistake.

A /VE visibility expression is not read. It takes precedence over /P where a
document has both, and not one file in the corpus carries one — so reading it
would be guesswork tested against nothing. A document that has one is treated
as visible, which is what this did before.

Suppression is of marks only. A clip narrowed inside a hidden layer still
narrows, because clipping is not marking; the operators that move the text pen
still move it. Otherwise a hidden layer would shift everything after it.

readOptional drops the error from Catalog(): reader.Open refuses a file whose
trailer does not lead to a catalogue, so it cannot happen. An unreachable
branch cannot be tested, and an untested branch in the code that decides what a
reader is shown is worse than no branch — so it is gone rather than covered by
a contrived case.

100% statement coverage, go vet and -race clean, nine cross-compile targets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 56da2c3 into main Aug 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant