refactor(html): share one style writer between the sheet and pdf views - #829
Merged
Conversation
`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
This was referenced Sep 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
Follow-up to #824.
AtomicStylesinpdf_file.cppandStyleRegistrywere thesame 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.
RankWhether 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 viewBase 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:
f,ff,l,t,z,m,ml,s,ws,w,k,x,y. In base 36,wat index 1008 spellsws0,which is also
wsat index 0 — likewisem/mlat 756 andf/ffat 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.
strip_width_classrecognises atrailing width class as
wfollowed by decimal digits, so a base-36 suffixwould stop being stripped.
So the pdf view keeps decimal, and
Digitsdocuments the condition on theconstructor 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
odr_test: 1456 passed, 6 pre-existing skips. One pdf test pinnedf1and now pins
f0.compare-html --driver chromeover all 802 pdf views: 794 matched on thefirst 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.)
No
CHANGELOG.mdentry: a consumer sees no API, format, rendering or packagingchange, and generated class names are not a contract.
Reference output
Regenerated for the pdf views, pushed, and
test/data.cmakeadvanced to the newrevisions. 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.