fix(layout): paint the Word 97-2003 run effects and the double strike - #3983
Open
Nathaniel-260 wants to merge 1 commit into
Open
fix(layout): paint the Word 97-2003 run effects and the double strike#3983Nathaniel-260 wants to merge 1 commit into
Nathaniel-260 wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Nathaniel-260
force-pushed
the
fix/legacy-run-effects
branch
from
September 6, 2026 23:49
e3d352b to
4512671
Compare
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Nathaniel-260
force-pushed
the
fix/legacy-run-effects
branch
from
September 6, 2026 23:59
4512671 to
00a91f7
Compare
Nathaniel-260
added a commit
to Nathaniel-260/otzaria-word-editor
that referenced
this pull request
Sep 7, 2026
ืืืื: โืืคืชืืจ ืืชืงืื ืคืืชื ืคืืค, ืื ื ืืืืจ ืขืืฆืื, ืืืืฅ, ืืื ืื ืขืืฉื ืืืืโ. ื ืืื ื-Chrome ืืืืชื ืขื ื-dist ืืืจืื โ ืคืืจืืง ื-zip ืฉื `export.toDocx` ืืื `getComputedStyle` ืขื `.superdoc-text-run`, ืืืืฉืืืืช ืฆืืืื ืืกื ืืืื-ืืืืื ืฉื ืืฉืืจื ืืคื ื ืืืืจื โ ืื ืืฆืื ืฉื ื ืืืจืื ืฉืื ืื ืืืืจื ืฉื ืจืืื ืืืฉืชืืฉ ืืืชื ืืืจ. **ืืืื: ืืื ืืงืกื ืืกืืื.** `format.apply` ืืงืื SelectionTarget ืืืื, ืืขื ืขืืฉืื ืืชืฉืืื ืืืืขื ืจืง ืืจืืข ืโืืืฉืืจโ: ืืืืืืื ื ืกืืจ, ืืงื ืืืชื ืืช ืฉืืขื-ืขืฉืจ ืืฉืืืช ืฉืืืืื, ืืืฉืืืจ โืืฉ ืืกืื ืืงืกื ืชืืืืโ ืืฉืืจืช ืืืฆื. ืื ืืื ื ืืืืืช ืื ืืข ืืื ืกืืจ ืฉื ืืืฉืง, ืืืื ืชืืงื ื ืืฉืืืฉื ืืืงืื: 1. `hasRangeSelection` (engine/font-advanced.ts) ืืื ืืืชื ืงืจืืื ืืืืืง ืฉื `applyFontAdvanced` โ ืื ืฉืืื ืฉื ืืื ืฉืขืืืื ืืขื ืืช ืืืจืช โ ืืืื ื ืฉืืืช ืืคืชืืื. ืืจืืข ืชืฉืืืืช ืืื ืฉืชืืื: โืืฉ ืืืืโ, โืืื ืืืืจืโ, โืืื `format.apply` ืืืจืกื ืืืืชโ, ืโืืื ืืืขืชโ ืฉืืื ื ื ืืขื ืืืจ. โืืื ืืกืืโ ื ืืื ืืืืจืื: ืืื ืืกืืืื ืืืืื ืืขืื ื ืืืกืื ืืื `null`, ืืืืืขื ืฉืืืืจืช ืฉื โืืื ื ืืืื ืืืจืกื ืื ืฉื ืืื ืืขโ ืืื ืืขื ื ืฉืงืจืืช ืขื ืืื ืืื. 2. **ืืืื ื ืฉืืืช ืฉืื.** ืืืืืืื ืืื ื ืืืกื ืืช ืืืกืื โ ืืื ืืืืืจืื ืจืงืข, ืืื ื ืืจืจ ืืืืื ื, ืืืคืฉืจ ืืืืืฅ ืืืงืกื ืืืื ืฉืืื ืคืชืื. ืชืฉืืื ืฉื ืงืจืื ืคืขื ืืืช ืืชืืืฉื ืช ืืฉื ื ืืืืืื ืื, ืืฉื ืืื ืืืืืจืื ืืช ืืืื: ืืืืจื ืฉืืชืืืืฆื ืืชืืช ืืืคืชืืจ ืฉื ืฉืืจ ืคืชืื, ืื ืืคืชืืจ ืฉื ืขืื ืื ืฆื ืืืจื ืฉืืืฉืชืืฉ ืกืืื ืืงืกื ืืื ืฉืืืืืขื ืืืงืฉื. ืืืืื `selectionchange` ืืืกื ืืช ืฉืื ืืื ืืืืืจื, ื-`watch` ืขื ืืืกืื ืืคืขืื ืืืกื ืืขืืจ ืืื ืืฉืื ืืืช โ ืฉืืื ื ืืืืฆืจ `selectionchange`. ืคืก ืืชืฆืืื ืืืงืืืื ื ืงืจื ืืืืฉ ืืื ืขื ืืชืฉืืื. 3. **ืืืืืืืื ื ืกืืจ ืขื ืืฆืืื ืืืื.** ืงืืื ืืื ื ืกืืจ ืืคื ื ืฉืืืืื ืืฆืื ืืืจื. **ืืืฉื ื: ืคืงืืื ืฉื ืืชืืื ืืืื ื ืืฆืืืจืื.** ืืกืืจืช ืืชื, ืฆื, ืืจืื ืืฉืงืืข ืืืฆืืื ื-docx ืงื ืื ืืช ืืืืฆืืื ื-Word, ืืืขืืจื ืืื ื ืืฆืืืจ ืืฃ ืืื ืืื โ ืฆืืืื ืืืกื ืืื ืืืื-ืืืืื. โืงื ืืืฆื ืืคืืโ ืืฆืืืจ, ืืื ืืงื ืืืื. ืืืืืชื ืจืฉืช ืคืงืืื ืื โืงืจื ืื ืโ ืโืืืื ืืืืคื ืืืืจืืโ ืืื ื ืืฆืืืจืื, ืืื ืื ืืืกื ืืช ืืืชื ืืืืจืื. ืืืืื ืช ืื ืฉืืืฅ โืืืฉืืจโ ืื ืืืืืง ืืื ืคืงื ืฉืืื ื ืขืืื โ ืืืืืืื ืฉืืคืขืืื ืืืืืืช ืืฆืืื, ืื ืฉืืจืช ืืืฆื ื ืฉืืจืช ืจืืงื. ืืื ืื ืืขืฉืืช ืืฆื ืฉืื ื: `.superdoc-text-run` ืืื ื ื ืืฉื ืฉืื ืกืืื ืืืืคืืื ื ืืจืืฆื, ืืืื ืืื ืื ื-CSS ืืงืืื, ืืืฉืื ืืืงืืื (docx-preflight) ืจืฅ ืืคืชืืื ืืืื ืืื ืืื ื ืืืข ืืขืืฆืื ืฉืืืื ืขืืฉืื โ ืฉืื ืืืืืง ืืืงืจื ืฉืืืื. ืื ืฉืื: ืืืืืืื ืืืืจ ืืช ืื, ืืชืืช ืืคืก ืืชืฆืืื ืืืงืืืื, ืืจืง ืขื ืื ืฉื ืืืจ. ืืืืชืื ืฉื ืืคืก ืืคื ืโืื ืืืจืื ื-Word (ืงืืจืื)โ โ ืืื ืืชืืืื ืขื ืืงืืืฅ, ืืืืืืขื ืฉืืชืืชืื ืืืืจืช ืื ืืืกื ืื ืืจืื. ืืืืขื ืืื ืืืืจื: ืฉืื ืืืจ ืื ื ืืฉื. **ืืชืืงืื ืขืฆืื ื ืฉืื ืืืขืื:** superdoc/docx-editor#3983. ืฉืชื ืืฉืืืืช ืฉืืืืขืืช ืืืชืจื ืืืืืื โ `dstrike` ืืื ืคืขืจ ืฉื ืืฆืืืจ ืืืื, ืืืจืืขืช ืืืืจืื ื ืืืขืื ื-`normalizeRunAttrsFromOoxml`. ืืืฉืจ ืื ืืืื ืืคืจืืกื ืื ืจื, ืืืื ืื ืืชืืงืื ืืืฉื ืืงืื ื-OSS. `w:kern` ืืืฉืืจ ืืืืฅ ืืืคืืจืฉ: ืงืจื ืื ื ืืฉื ื ืืช ืจืืื ืืืืืื ืฉื ืืืืืคืื, ืืืืืจ ืืช ืืืืืื ืืื ืจืง ืืช ืืฆืืืจ. ืืจืืฉืื ืืืื ื-docs/engine-gaps.md. **ืฉืขืจ ืืืฉ:** `npm run check:font-advanced` โ 5/5 ื-Chrome ืืืืชื. ืฉืืืฉ ืืฉืืจืืชืื ืืืืืืช ืืช ืืืืืขื ืขืฆืื ืืื ืืคืืงืกืืื, ืคืงื ืืื ืืื ืจืืฆื ืืื ืฉืืคืฉืจ ืืืื ืืืืืก ืคืืงืกื ืืคืงื: ืืจืืข ืฉืืื ืืข ืืชืืื ืืฆืืืจ ืฆื, ืื ืงื ืืคืื, ืืฉืืจื ืชืืคืื ืืชืืืจ ืื ืืืืจืื ืืืืืืขื. ืืืืขื ืฉืืืฉ ืืื ื ืืืื ืืื ืืืืขื ืฉืชืฉืงืจ ืืฉืงื. `check:dialog-drag` ื ืืจืฉ ืืชืืงืื: ืืื ืคืชื ืืช ืืืืืืื ืขื ืกืื ืืืืืฅ, ืืืื โEnter ืืืฉืจโ ื ืืื ืขื ืืคืชืืจ ืฉื ืขืื ืืกืืื ืืืจืช. ืืื ืืกืื ืืงืกื ืืคื ื, ืืืคืจืื ืขืืฉืื ืืื โื-Enter ืื ืืืืขโ ืืืื โืืืืื ื ืืฉืืโ ืืคื ืฉืืจืช ืืืฆื. ืืืืฆืื, ืืคื ื ืฉื ืกืืืื ืขื ืืืช ืืื: ืื ืฉืืืจ ืืืกืจ ืืชืืจื ืื ืืื ืฉืืืืืงื ื ืฆืืขืช ืืืื โ ืืจืฉืืื, ืืฉืขืจ, ืืืืืขื, ืืืืืื, ืืขืงื ืืืกืื, ืืืืคืื โืืื ืืกืืโ. ืฉืืืฉืื ืืืืช ืืืืงืืช ืืืฉืืช (ืจืืื + ืืืืื). `npm test` ืืื: 3992 ืขืืืจืืช; typecheck ื ืงื; ืฉื ื ืืฉืขืจืื ืืจืืงืื. ืฉืืื ื ืืืืงืืช ืงืืืืืช ืฉืืจืืืื ืืช ืืืืืืื ืืื ืืืืจื ืืฆืืืจืืช ืขืืื ืขืืฉืื ืืืคืืจืฉ โ ืืชื ืื ืืืืงืื ืฉืื ืืื ืืงืกื ืืกืืื. `createSuperdocDouble` ืงืืื `setSelection`: ืืืืืจื ืืื ื ืงืืืขื ืืืื ืืืจืืื, ืืืืฉืง ืฉืงืืจื ืืืชื ืืืชืจ ืืคืขื ืืืช ืืื ื ื ืืชื ืืืืืงื ืืื ืื.
`w:outline`, `w:shadow`, `w:emboss` and `w:imprint` are what the Font dialog's "Effects" group writes, and Word still honours them โ a document can carry them with no `w14:` counterpart anywhere. They round-tripped correctly through import and export and were then dropped: the normalizer never read them, so nothing downstream could paint them. Applying one to a selection changed the file and left the page identical. `w:dstrike` had the other half of the same problem. `normalizeRunAttrsFromOoxml` already emitted `doubleStrike`, and it reached the painter โ the painter simply never read it, so a double strikethrough was drawn as a single line. - style-engine passes the four flags through as authored, including an explicit `false` (`<w:shadow w:val="0"/>` is how Word clears an inherited effect, and dropping the key would let a lower cascade layer switch it on). - `RunMarks` carries them, next to `strike`, for every run kind. - The DOM painter draws them, after `applyTextEffects` so it can see what an authored `w14:` effect already wrote. It stands aside for a `w14:textOutline` that actually painted a stroke, for a `w14:textFill`, and for a `w14:shadow`; it composes with a `w14:glow`, which is a different effect. The outline test reads what was written rather than whether the object exists, because `<w14:textOutline w14:w="0"><w14:noFill/></w14:textOutline>` is a present object that paints nothing. - The outline empties the glyph through `-webkit-text-fill-color`, not `color`. The stroke is `currentColor`, and clearing `color` would take the stroke with it โ a run carrying `<w:outline/>` with Automatic color, which is exactly what the Font dialog writes, would have painted nothing at all. Formatting marks opt back out of both inherited properties, so the dot inside an outlined run is not emptied with the text around it. - `doubleStrike` draws its own line: it is a separate mark and a separately settable run attribute, so a run carrying only `w:dstrike` still has a strikethrough. `render-line.ts` re-derives the decoration on the paths that lift an underline onto an overlay, so that rule is now shared rather than written twice. - The five paint-invalidation signatures count them โ `textRunMergeSignature`, `hashRunVisualMarks`, `deriveBlockVersion`, `deriveParagraphBlockVersion` and `hashParagraphBlockForTableVersion`. Without those, an edit that flips only `<w:emboss/>` leaves the already-painted span on screen, and two adjacent runs differing only in one flag merge into a single span. Outline is faithful โ a hairline stroke with the fill removed is what Word draws. Shadow is a hard-edged offset copy. Emboss and imprint keep the authored glyph color and put a light edge on one side and a dark edge on the other; Word instead paints the glyph near the page color, which needs a resolved page background that paint does not have, and guessing it wrong turns the text invisible. The reasoning sits in the source. CSS carries one `text-decoration-style` for the whole decoration set, so a run that is both underlined and double-struck keeps the underline's authored style and the strike stays single โ except on the overlay paths, where the underline has already been lifted off the run and the double can be drawn.
Nathaniel-260
force-pushed
the
fix/legacy-run-effects
branch
from
September 7, 2026 01:01
00a91f7 to
3803f14
Compare
Y-PLONI
pushed a commit
to Nathaniel-260/otzaria-word-editor
that referenced
this pull request
Sep 7, 2026
ืืืื: โืืคืชืืจ ืืชืงืื ืคืืชื ืคืืค, ืื ื ืืืืจ ืขืืฆืื, ืืืืฅ, ืืื ืื ืขืืฉื ืืืืโ. ื ืืื ื-Chrome ืืืืชื ืขื ื-dist ืืืจืื โ ืคืืจืืง ื-zip ืฉื `export.toDocx` ืืื `getComputedStyle` ืขื `.superdoc-text-run`, ืืืืฉืืืืช ืฆืืืื ืืกื ืืืื-ืืืืื ืฉื ืืฉืืจื ืืคื ื ืืืืจื โ ืื ืืฆืื ืฉื ื ืืืจืื ืฉืื ืื ืืืืจื ืฉื ืจืืื ืืืฉืชืืฉ ืืืชื ืืืจ. **ืืืื: ืืื ืืงืกื ืืกืืื.** `format.apply` ืืงืื SelectionTarget ืืืื, ืืขื ืขืืฉืื ืืชืฉืืื ืืืืขื ืจืง ืืจืืข ืโืืืฉืืจโ: ืืืืืืื ื ืกืืจ, ืืงื ืืืชื ืืช ืฉืืขื-ืขืฉืจ ืืฉืืืช ืฉืืืืื, ืืืฉืืืจ โืืฉ ืืกืื ืืงืกื ืชืืืืโ ืืฉืืจืช ืืืฆื. ืื ืืื ื ืืืืืช ืื ืืข ืืื ืกืืจ ืฉื ืืืฉืง, ืืืื ืชืืงื ื ืืฉืืืฉื ืืืงืื: 1. `hasRangeSelection` (engine/font-advanced.ts) ืืื ืืืชื ืงืจืืื ืืืืืง ืฉื `applyFontAdvanced` โ ืื ืฉืืื ืฉื ืืื ืฉืขืืืื ืืขื ืืช ืืืจืช โ ืืืื ื ืฉืืืช ืืคืชืืื. ืืจืืข ืชืฉืืืืช ืืื ืฉืชืืื: โืืฉ ืืืืโ, โืืื ืืืืจืโ, โืืื `format.apply` ืืืจืกื ืืืืชโ, ืโืืื ืืืขืชโ ืฉืืื ื ื ืืขื ืืืจ. โืืื ืืกืืโ ื ืืื ืืืืจืื: ืืื ืืกืืืื ืืืืื ืืขืื ื ืืืกืื ืืื `null`, ืืืืืขื ืฉืืืืจืช ืฉื โืืื ื ืืืื ืืืจืกื ืื ืฉื ืืื ืืขโ ืืื ืืขื ื ืฉืงืจืืช ืขื ืืื ืืื. 2. **ืืืื ื ืฉืืืช ืฉืื.** ืืืืืืื ืืื ื ืืืกื ืืช ืืืกืื โ ืืื ืืืืืจืื ืจืงืข, ืืื ื ืืจืจ ืืืืื ื, ืืืคืฉืจ ืืืืืฅ ืืืงืกื ืืืื ืฉืืื ืคืชืื. ืชืฉืืื ืฉื ืงืจืื ืคืขื ืืืช ืืชืืืฉื ืช ืืฉื ื ืืืืืื ืื, ืืฉื ืืื ืืืืืจืื ืืช ืืืื: ืืืืจื ืฉืืชืืืืฆื ืืชืืช ืืืคืชืืจ ืฉื ืฉืืจ ืคืชืื, ืื ืืคืชืืจ ืฉื ืขืื ืื ืฆื ืืืจื ืฉืืืฉืชืืฉ ืกืืื ืืงืกื ืืื ืฉืืืืืขื ืืืงืฉื. ืืืืื `selectionchange` ืืืกื ืืช ืฉืื ืืื ืืืืืจื, ื-`watch` ืขื ืืืกืื ืืคืขืื ืืืกื ืืขืืจ ืืื ืืฉืื ืืืช โ ืฉืืื ื ืืืืฆืจ `selectionchange`. ืคืก ืืชืฆืืื ืืืงืืืื ื ืงืจื ืืืืฉ ืืื ืขื ืืชืฉืืื. 3. **ืืืืืืืื ื ืกืืจ ืขื ืืฆืืื ืืืื.** ืงืืื ืืื ื ืกืืจ ืืคื ื ืฉืืืืื ืืฆืื ืืืจื. **ืืืฉื ื: ืคืงืืื ืฉื ืืชืืื ืืืื ื ืืฆืืืจืื.** ืืกืืจืช ืืชื, ืฆื, ืืจืื ืืฉืงืืข ืืืฆืืื ื-docx ืงื ืื ืืช ืืืืฆืืื ื-Word, ืืืขืืจื ืืื ื ืืฆืืืจ ืืฃ ืืื ืืื โ ืฆืืืื ืืืกื ืืื ืืืื-ืืืืื. โืงื ืืืฆื ืืคืืโ ืืฆืืืจ, ืืื ืืงื ืืืื. ืืืืืชื ืจืฉืช ืคืงืืื ืื โืงืจื ืื ืโ ืโืืืื ืืืืคื ืืืืจืืโ ืืื ื ืืฆืืืจืื, ืืื ืื ืืืกื ืืช ืืืชื ืืืืจืื. ืืืืื ืช ืื ืฉืืืฅ โืืืฉืืจโ ืื ืืืืืง ืืื ืคืงื ืฉืืื ื ืขืืื โ ืืืืืืื ืฉืืคืขืืื ืืืืืืช ืืฆืืื, ืื ืฉืืจืช ืืืฆื ื ืฉืืจืช ืจืืงื. ืืื ืื ืืขืฉืืช ืืฆื ืฉืื ื: `.superdoc-text-run` ืืื ื ื ืืฉื ืฉืื ืกืืื ืืืืคืืื ื ืืจืืฆื, ืืืื ืืื ืื ื-CSS ืืงืืื, ืืืฉืื ืืืงืืื (docx-preflight) ืจืฅ ืืคืชืืื ืืืื ืืื ืืื ื ืืืข ืืขืืฆืื ืฉืืืื ืขืืฉืื โ ืฉืื ืืืืืง ืืืงืจื ืฉืืืื. ืื ืฉืื: ืืืืืืื ืืืืจ ืืช ืื, ืืชืืช ืืคืก ืืชืฆืืื ืืืงืืืื, ืืจืง ืขื ืื ืฉื ืืืจ. ืืืืชืื ืฉื ืืคืก ืืคื ืโืื ืืืจืื ื-Word (ืงืืจืื)โ โ ืืื ืืชืืืื ืขื ืืงืืืฅ, ืืืืืืขื ืฉืืชืืชืื ืืืืจืช ืื ืืืกื ืื ืืจืื. ืืืืขื ืืื ืืืืจื: ืฉืื ืืืจ ืื ื ืืฉื. **ืืชืืงืื ืขืฆืื ื ืฉืื ืืืขืื:** superdoc/docx-editor#3983. ืฉืชื ืืฉืืืืช ืฉืืืืขืืช ืืืชืจื ืืืืืื โ `dstrike` ืืื ืคืขืจ ืฉื ืืฆืืืจ ืืืื, ืืืจืืขืช ืืืืจืื ื ืืืขืื ื-`normalizeRunAttrsFromOoxml`. ืืืฉืจ ืื ืืืื ืืคืจืืกื ืื ืจื, ืืืื ืื ืืชืืงืื ืืืฉื ืืงืื ื-OSS. `w:kern` ืืืฉืืจ ืืืืฅ ืืืคืืจืฉ: ืงืจื ืื ื ืืฉื ื ืืช ืจืืื ืืืืืื ืฉื ืืืืืคืื, ืืืืืจ ืืช ืืืืืื ืืื ืจืง ืืช ืืฆืืืจ. ืืจืืฉืื ืืืื ื-docs/engine-gaps.md. **ืฉืขืจ ืืืฉ:** `npm run check:font-advanced` โ 5/5 ื-Chrome ืืืืชื. ืฉืืืฉ ืืฉืืจืืชืื ืืืืืืช ืืช ืืืืืขื ืขืฆืื ืืื ืืคืืงืกืืื, ืคืงื ืืื ืืื ืจืืฆื ืืื ืฉืืคืฉืจ ืืืื ืืืืืก ืคืืงืกื ืืคืงื: ืืจืืข ืฉืืื ืืข ืืชืืื ืืฆืืืจ ืฆื, ืื ืงื ืืคืื, ืืฉืืจื ืชืืคืื ืืชืืืจ ืื ืืืืจืื ืืืืืืขื. ืืืืขื ืฉืืืฉ ืืื ื ืืืื ืืื ืืืืขื ืฉืชืฉืงืจ ืืฉืงื. `check:dialog-drag` ื ืืจืฉ ืืชืืงืื: ืืื ืคืชื ืืช ืืืืืืื ืขื ืกืื ืืืืืฅ, ืืืื โEnter ืืืฉืจโ ื ืืื ืขื ืืคืชืืจ ืฉื ืขืื ืืกืืื ืืืจืช. ืืื ืืกืื ืืงืกื ืืคื ื, ืืืคืจืื ืขืืฉืื ืืื โื-Enter ืื ืืืืขโ ืืืื โืืืืื ื ืืฉืืโ ืืคื ืฉืืจืช ืืืฆื. ืืืืฆืื, ืืคื ื ืฉื ืกืืืื ืขื ืืืช ืืื: ืื ืฉืืืจ ืืืกืจ ืืชืืจื ืื ืืื ืฉืืืืืงื ื ืฆืืขืช ืืืื โ ืืจืฉืืื, ืืฉืขืจ, ืืืืืขื, ืืืืืื, ืืขืงื ืืืกืื, ืืืืคืื โืืื ืืกืืโ. ืฉืืืฉืื ืืืืช ืืืืงืืช ืืืฉืืช (ืจืืื + ืืืืื). `npm test` ืืื: 3992 ืขืืืจืืช; typecheck ื ืงื; ืฉื ื ืืฉืขืจืื ืืจืืงืื. ืฉืืื ื ืืืืงืืช ืงืืืืืช ืฉืืจืืืื ืืช ืืืืืืื ืืื ืืืืจื ืืฆืืืจืืช ืขืืื ืขืืฉืื ืืืคืืจืฉ โ ืืชื ืื ืืืืงืื ืฉืื ืืื ืืงืกื ืืกืืื. `createSuperdocDouble` ืงืืื `setSelection`: ืืืืืจื ืืื ื ืงืืืขื ืืืื ืืืจืืื, ืืืืฉืง ืฉืงืืจื ืืืชื ืืืชืจ ืืคืขื ืืืช ืืื ื ื ืืชื ืืืืืงื ืืื ืื.
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.
The bug
The Font dialog's Effects group in Word writes four run flags โ
w:outline,w:shadow,w:emboss,w:imprintโ and Word still honours all four. Theypredate the
w14:effect family, so a document can carry them with now14:counterpart anywhere.
SuperDoc imports them, keeps them, and exports them correctly. It never paints
them. Applying one to a selection writes the file and leaves the page identical.
w:dstrikeis the same seam from the other side.normalizeRunAttrsFromOoxmlalready emits
doubleStrikeand it reaches the painter โ the painter simplynever reads it, so a double strikethrough is drawn as a single line.
Reproduce
.docxin SuperDoc.format.apply({ target, inline: { outline: true } })(or
shadow/emboss/imprint/dstrike).dstrike, a single line.Or in Word: type a word, Font โ Effects โ Outline, save, open in SuperDoc.
Measurement
Chrome, packaged dist, one effect per run so each pixel is attributable, with
getComputedStyleon the.superdoc-text-runelement and a byte-compare ofa PNG clip of the line before and after:
document.xmloutline<w:outline/>shadow<w:shadow/>text-shadow, one offset copyemboss<w:emboss/>imprint<w:imprint/>dstrike<w:dstrike/>line-throughsolidline-throughdoubleThe four flags never reached paint at all. Dumping the keys of the
TextRunthepainter receives, on a run carrying each flag, gave:
So
dstrikewas a painter-only gap, and the other four were dropped one layerearlier, in the normalizer. Both halves are in this patch, and the same dump with
the patch applied shows
outline/shadow/emboss/imprinton the runalongside
doubleStrike.The change
style-engine/normalize/run-attrs.tspasses the four flags through asauthored, including an explicit
falseโ<w:shadow w:val="0"/>is how Wordclears an inherited effect, and dropping the key would let a lower cascade layer
switch it back on. This mirrors the
vanish/specVanishlines directly below.contractscarries them onRunMarks, next tostrike, so every run kindthat supports inline formatting has them.
painters/dom/runs/text-run.tsdraws them in a smallapplyLegacyRunEffects,called after
applyTextEffectsso it can see what an authoredw14:effectalready wrote:
w14:textOutlinethat actuallypainted a stroke, or a
w14:textFill, rather than relying on declarationorder โ order would have left its empty fill behind on a run whose
w14:textOutlinehas no fill of its own, turning an authored outline into ahollow glyph. The test is what was written, not whether the object exists:
<w14:textOutline w14:w="0"><w14:noFill/></w14:textOutline>โ what Wordwrites for "no outline" โ is a present object that paints nothing, and
standing aside for it would leave the run blank;
w14:shadowin the same way, since that is the authoredform of the same drop shadow โ while emboss and imprint, which have no
w14:counterpart and are not drop shadows, keep composing;
text-shadowinstead ofassigning over it, because
w14:glowis a different effect from an embossededge and the two compose as two shadow layers โ assigning would let a glow
silently erase an emboss on the same run.
The outline empties the glyph through
-webkit-text-fill-color, notcolor.The stroke is
currentColor, andcurrentColorresolves against the element'sown
color; clearingcolortakes the stroke down with the fill, and a runcarrying
<w:outline/>with Automatic color โ exactly what the Font dialogwrites โ would paint nothing at all.
normalizeRunAttrsFromOoxmldeliberatelyreports Automatic as no color, so that is the default path, not an edge case.
doubleStrikedraws its own line. It is a separateRunMarksfield and aseparately settable run attribute, so a run carrying only
w:dstrikestill has astrikethrough; reading the line off
strikealone would render it undecorated.render-line.tsre-derives the decoration on the two paths that lift an underlineonto a line overlay, so that rule now lives in one shared helper rather than in
three places that had already drifted.
The outline also fixes the formatting marks it would otherwise empty.
-webkit-text-fill-coloris inherited and beats a child's owncolor, so theยทandโmarks rendered inside an outlined run would have come out as hairlinerings. Both
::afterrules opt back out โ they are chrome, not document text.Five signatures gate paint reuse, and all five count the new flags โ
textRunMergeSignature,hashRunVisualMarks,deriveBlockVersion,deriveParagraphBlockVersionandhashParagraphBlockForTableVersion. Without them an edit that flips only<w:emboss/>leaves the already-painted span on screen โ the same "the file changed and the
page did not" symptom, one layer up โ and two adjacent runs differing only in one
flag merge into a single span, painting one run's effect over the other's text.
On fidelity, stated rather than hidden
draws. Stroke weight scales with font size, with a floor so it does not round
away on a low-density screen.
large display sizes is not reproduced.
side and a dark edge on the other; the direction is what separates the two.
Word instead paints the glyph itself near the page color and lets the edges
carry the shape. Doing that in paint needs the resolved page background, which
the painter does not have โ and guessing it wrong turns the text invisible.
That is the one place this trades the last of the fidelity for legibility, and
the reasoning is in the source rather than only here.
This is the same trade
applyTextEffectsalready makes forw14:glow, which itapproximates with concentric zero-offset shadows.
One CSS constraint
text-decoration-styleis a single value for the whole decoration set, so a runthat is both underlined and double-struck can keep only one of the two styles.
The underline already claimed it, and it claimed it from an authored value
(
w:u/@w:val) whilew:dstrikehas no style to lose โ so the underline keepsits style and the strike stays single. A visible shortfall on a rare combination,
rather than a wrong underline on a common one.
Deliberately not in scope
w:kernis measured as written-but-not-drawn in the same dialog โ the paintersets
font-kerning: noneunconditionally and nothing opts a run back in. It isleft out on purpose: kerning changes glyph advance widths, so it is a
measurement change, not a paint-only one, and it belongs with the layout path
rather than in this patch.
Tests
Added, and each was watched fail before it passed โ the fix was disabled in place
and the suites re-run:
painters/dom, 18 new cases: the hollow outline; an outlined run with noexplicit color staying visible (the case that goes blank if the fill is
cleared through
color); the single offset shadow; emboss vs imprint differingby side; both scaling with font size; standing aside for
w14:textOutline;standing aside for
w14:textFill; standing aside forw14:shadow; composing withw14:glow; the double strike;a run carrying only
doubleStrike; the underline keeping its style againsta double strike; no effect painting nothing; and the merge signature changing
for each of the five flags. With the painter pass disabled, 5 go red; with the
fill cleared through
coloragain, 2 go red; with the ordering reverted, 1 goesred; with
doubleStrikedropped from the decoration test, 1 goes red.layout-bridge, 2 new cases onhashRunVisualMarks: a different hash foreach flag, and the flags distinguished from one another. Both go red when the
five lines are removed from the hash.
style-engine, 3 new cases: the flags carried through, an explicitfalsepreserved, and absence staying absent. With the four normalizer lines removed,
2 go red.
A note on how this was reviewed: three independent review passes were run over
the change across two rounds. They found eleven real defects, including two that
an earlier revision of this patch introduced โ the invisible default-color
outline and the missed invalidation signatures. All are fixed here, each with a
test that was watched go red first.