Repository navigation
CSS 2.1 test parity: generated content, counters, list markers (stacked on #264) - #265
Merged
eXpl0it3r merged 4 commits intoNov 9, 2026
Merged
Conversation
This was referenced Aug 29, 2026
eXpl0it3r
force-pushed
the
feature/css21-generated-content
branch
2 times, most recently
from
November 9, 2026 07:49
a5ea468 to
0c5f63b
Compare
Collaborator
|
Rebased onto the updated #264 and fixed the counter scoping on top:
Since the scoping is a proper stack now, |
eXpl0it3r
force-pushed
the
feature/css21-generated-content
branch
from
November 9, 2026 08:09
0c5f63b to
9377e7f
Compare
…counter() counter-reset/counter-increment parsed at the CSS-OM level but had zero effect anywhere - content: counter()/counters() were explicitly out of scope in CssContentEngine's own doc comment, and there was no counter-value tracking anywhere in Core. Adds CssCounterEngine: a forward-order tree walk (hooked into DomParser right after CascadeApplyStyles, before pseudo-element content gets resolved) that threads named counter values through document order - a box inherits its parent's counters for its first child, and each child's resulting counters feed the next sibling - covering ordinary sequential/nested counter usage (including the common case content:counter() needs). Deliberately not the full CSS 2.1 §12.4 algorithm: reversed() and counter-set aren't implemented, and a nested counter-reset for a name already in scope always starts a fresh value rather than the spec's more precise scoping rule - see the class's own remarks for the exact boundary. FormatCounterValue is the single counter-style resolver (decimal, decimal-leading-zero, roman, alpha, with CSS Counter Styles Level 3 §2's fallback-to-decimal for unknown/out-of-range styles), ported from PeachPDF's CssCounterEngine and reusing this fork's existing CommonUtils.ConvertToAlphaNumber for the alphabetic styles. Ports PeachPDF's CssCounterEngineTests.cs (pure formatting, 17 tests) and the counter-related subset of CssContentEngineTests.cs, adapted to run through the real box tree (LayoutHarness + a ::before/::after rule) rather than a hand-built box, since pseudo-element boxes here are only constructible via real selector matching. Adds two new integration tests proving the document-order threading itself works (sequential sibling increments, nested-ancestor visibility) beyond what PeachPDF's own box-local unit tests exercise.
<li value="N"> (a WHATWG HTML presentational hint) had no effect on GetIndexForList's numbering - only <ol start>/reversed were consulted. Later items without their own explicit value now continue counting from a value-carrying item, matching every browser. Also fixes list-style-type:square rendering "♠" (U+2660 BLACK SPADE SUIT card-suit glyph) instead of an actual square - presumably a copy/paste typo, noticed while auditing this code for the counter port above. Re-approves the Bullets.png baseline for the corrected marker glyph. Ports the CSS2.1-relevant subset of PeachPDF's ListItemCounterIntegrationTests.cs (ol start/reversed were already correct; li value and the marker-glyph fix are new).
… test CssContentWithCssEscapeInString_RendersLiterally was Ignore'd, citing "this fork has no pseudo-element support at all" - false: ::before already works (the CSS-escape test right above it in this same file passes), and CssContentEngine/CssData's pseudo-element creation predates this port entirely. Un-ignoring it surfaced a real bug in the TEST itself, not the engine: its box-finding predicate (HtmlTag == null && Text != null) matches the real text node "text" (which also has no HtmlTag) before it reaches the ::after box, since ::after is appended at the end of p.Boxes while ::before is inserted at index 0 - the sibling test just above happens to pass because ::before's insert-at-0 placement wins the FirstOrDefault race by coincidence. Fixed to key off IsAfterPseudoElement directly, and it passes.
- End a counter-reset's scope with its parent instead of leaking it to later siblings of the parent - Skip display:none subtrees when resolving counters - Read counters that aren't in scope as 0
eXpl0it3r
force-pushed
the
feature/css21-generated-content
branch
from
November 9, 2026 08:14
9377e7f to
5c13e9a
Compare
eXpl0it3r
approved these changes
Nov 9, 2026
Collaborator
|
Thank you! |
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.
Part 2 of a CSS 2.1 test-parity sweep against PeachPDF's Integration test suite. Stacked on #264 (float/clear/positioning) and #263 - the diff below includes both until they merge. Only the last 4 commits (counter engine, list-marker fixes, one stale-test fix) are this PR's own.
Summary
Scope: chapter 12 (generated content, automatic numbering, lists) - previously zero behavioral coverage in either test project (only
content/list-styleCSS-OM parsing).content: counter()was explicitly out of scope perCssContentEngine's own doc comment, and there was no counter-value tracking inCoreat all. ImplementedCssCounterEngine: a forward-order tree walk threading named counter values through document order (parent→first-child, sibling→sibling), covering ordinary sequential/nested counter usage. Deliberately scoped down from PeachPDF's full §12.4 algorithm -reversed()andcounter-setaren't implemented; see the class's own remarks for the exact boundary. Ported PeachPDF'sCssCounterEngineTests.cs(pure counter-style formatting, 17 tests) and the counter-related subset ofCssContentEngineTests.cs, plus two new integration tests proving the document-order threading itself (sequential siblings, nested-ancestor visibility).<li value="N">had no effect (only<ol start>/reversedwere consulted) - fixed. Also found and fixedlist-style-type:squarerendering "♠" (a card-suit glyph, not a square) while auditing this code.[Ignore]d test claimed "this fork has no pseudo-element support at all" - false (::beforeclearly already worked, per the passing test right above it in the same file). Un-ignoring it surfaced a real bug in the test itself (aFirstOrDefaultpredicate ambiguous between the real text node and the::afterbox), not the engine - fixed and passing.quotesproperty (andopen-quote/close-quotenesting depth per CSS 2.1 §12.2/§15.3) has no test coverage, and doesn't appear to be implemented there either.Test plan
HtmlRenderer.Testfull suite: 2449 passed, 0 failedHtmlRenderer.IntegrationTestfull suite: 374 passed, 0 failedHtmlRenderer.PdfSharp.Testfull suite: 39 passed, 0 failedBullets.pngbaseline for the corrected square-marker glyph - reviewed the diff image, confirmed it's the intended fix