Skip to content

refactor(html): share one style writer between the sheet and pdf views - #829

Merged
andiwand merged 2 commits into
mainfrom
refactor/share-the-style-registry
Sep 6, 2026
Merged

refactor(html): share one style writer between the sheet and pdf views#829
andiwand merged 2 commits into
mainfrom
refactor/share-the-style-registry

Conversation

@andiwand

@andiwand andiwand commented Sep 6, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Follow-up to #824. AtomicStyles in pdf_file.cpp and StyleRegistry were the
same class written twice — a declaration→class map, a first-seen order and a
rule writer — so one now serves both, with the two things that actually differ
as parameters.

StyleRegistry styles(StyleRegistry::Rank::plain);                 // pdf
StyleRegistry styles(StyleRegistry::Rank::replaces_inline,        // sheet
                     StyleRegistry::Digits::base36);

Rank

Whether a rule has to outrank the stylesheets around it. A sheet's classes stand
in for the inline styles they replaced and name themselves three times for that
specificity; a pdf page's compete with nothing, so one class each.

Digits — base 36 is not safe for the pdf view

Base 36 names the first 36 of a prefix in one character past it, which is what
keeps a sheet's class references short. It cannot be used for the pdf view,
and I found this the hard way — by trying it:

  • Prefixes alias. pdf interns under f, ff, l, t, z, m, ml,
    s, ws, w, k, x, y. In base 36, w at index 1008 spells ws0,
    which is also ws at index 0 — likewise m/ml at 756 and f/ff at 540.
    That is not theoretical: on the private corpus 14 documents came out with
    duplicate rule names
    , one declaration silently taking another's class.
    Decimal cannot alias, because a decimal suffix can never spell a prefix.
  • One site reads a class name back. strip_width_class recognises a
    trailing width class as w followed by decimal digits, so a base-36 suffix
    would stop being stripped.

So the pdf view keeps decimal, and Digits documents the condition on the
constructor rather than leaving it as a trap. Fixing pdf so it could take base
36 means renaming the overlapping prefixes and keeping the width class in the
run struct instead of parsing it out of a string — worth doing, but not in a
refactor.

What moves

Only the pdf views, and only their class names: numbered from 0 rather than 1.
The rule sets are identical modulo the rename — 5290 declarations in and out for
pdf.pdf, no additions, no losses — and the html is 0.47% smaller (25.03 MB
→ 24.91 MB across the corpus's pdf views). Every sheet view is byte-identical.

Verification

  • Full odr_test: 1456 passed, 6 pre-existing skips. One pdf test pinned f1
    and now pins f0.
  • compare-html --driver chrome over all 802 pdf views: 794 matched on the
    first pass and the 8 it flagged all matched on re-run — Chrome noise from
    parallel workers on very large pages, not a difference. (The base-36 attempt
    above was caught structurally, by duplicate rule names in the html, not by
    pixels.)
  • Checked every emitted view for duplicate rule names: none.
  • Net −73/+71 lines, one class instead of two.

No CHANGELOG.md entry: a consumer sees no API, format, rendering or packaging
change, and generated class names are not a contract.

Reference output

Regenerated for the pdf views, pushed, and test/data.cmake advanced to the new
revisions. The bump covers two commits in each output repo: the regeneration
#824 needed (merged without it, so main is pinned to output that predates it)
and the pdf rename this branch makes. Merging this makes the html comparison
green again.

andiwand and others added 2 commits September 6, 2026 13:19
`AtomicStyles` in `pdf_file.cpp` and `StyleRegistry` were the same class
written twice: a declaration→class map, a first-seen order, and a rule
writer. One class now serves both, with the two things that actually differ
as parameters.

- `Rank`: whether a rule has to outrank the stylesheets around it. A sheet's
  classes stand in for inline styles and name themselves three times for the
  specificity they took over; a pdf page's compete with nothing.
- `Digits`: base 36 names the first 36 of a prefix in one character, but
  only where no prefix extends another. The sheet view uses one prefix and
  takes it; the pdf view keeps decimal, because `w` at 1008 spells `ws0`,
  which is also `ws` at 0 — and because `strip_width_class` reads a width
  class back out of the class string as `w` and digits.

The pdf views' class names are numbered from 0 rather than 1. The rules are
the same set, renamed: 802 pdf views render identically, and the html is
0.47% smaller. Nothing else moves — every sheet view is byte-identical.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUh7UU43PbVPEBq2LVtoVN
Covers the regeneration #824 needed and the pdf rename this branch makes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FUh7UU43PbVPEBq2LVtoVN
@andiwand
andiwand merged commit 4a80837 into main Sep 6, 2026
25 checks passed
@andiwand
andiwand deleted the refactor/share-the-style-registry branch September 6, 2026 11:30
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