You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Fix: render run-level text outline (<a:ln>) for PPTX glyphs - #39
Fixes a bug where PPTX text runs declaring a run-level <a:ln> (text outline/stroke) rendered
with that outline silently missing. A real-world deck used a near-white <a:solidFill> paired
with a purple <a:ln> stroke to produce PowerPoint's "hollow outlined numeral" effect (large "1"/
"2" badges); without outline support the numerals rendered as near-invisible pale shapes.
Root cause: PptxEffectiveRunProperties/PaintTextLayout only ever resolved and painted a run's
own <a:solidFill> fill color - there was no concept of a run's own <a:ln> (distinct from a
shape's <p:spPr>/<a:ln>, which strokes the shape's geometry, not its text).
This was investigated after a separate bug report suspected systemic font-fallback issues; direct
testing against two real-world decks confirmed theme/run font resolution (SystemFontCatalog/ ResolveTextFont) is correct, and the actual (narrower) defect was this missing outline support.
Fix
PptxEffectiveRunProperties gains OutlineWidthEmu/OutlineColor, resolved via a new GetRunOutline helper (PptxDocument.TextInheritance.cs) that reuses the existing ResolveLineStyle resolver already used for shape/picture/connector strokes, restricted to
solid-color outlines (matching the pre-existing solid-color-only run fill color scope).
LineGlyph/PptxGlyphPlacement thread the new fields through layout, with OutlineWidthEmu
scaled by the same autofit fontScale factor as SizeEmu.
PaintTextLayout builds each glyph's path in local shape space, then transforms separately for
the fill paint vs. a ResolveStrokeOutline-based outline paint - mirroring the existing
shape-outline rendering pattern, including its coordinate-scale-aware flatten tolerance.
<a:effectLst><a:glow> remains out of scope, consistent with this project's already-documented <a:effectRef> shape-effects deferral.
Type of Change
Bug fix (non-breaking change which fixes an issue)
New feature (non-breaking change which adds functionality)
Breaking change (fix or feature that would cause existing functionality to not work as expected)
Documentation update
Code quality improvement
Related Issues
Closes #
Pre-Submission Checklist
Build and Test
Code builds successfully and all tests pass: pwsh ./build.ps1 (ran dotnet build/dotnet test directly: 10,503 tests passed, 0 failed)
Code produces zero warnings
Code Quality
New code has appropriate XML documentation comments
Static analyzer warnings have been addressed
Quality Checks
All linters pass: pwsh ./lint.ps1 (passed - no errors)
Testing
Added unit tests for new functionality (PptxTextTests.cs: 2 new inheritance-resolution
tests; PptxTextRenderTests.cs: 1 new outline-painting test)
Updated existing tests if behavior changed (none required - fill-path refactor is
mathematically equivalent; pre-existing tests pass unmodified)
All tests follow the AAA (Arrange, Act, Assert) pattern
Test coverage is maintained or improved
Documentation
Updated README.md (if applicable) - not applicable, no public API/package changes
Updated docs/ documentation (if applicable) - added a new "Phase 2 Follow-Up: Run Text
Outline Rendering" section to docs/design/canvas-net-pptx/pptx-document.md
Added code examples for new features (if applicable) - not applicable
Updated requirements.yaml (if applicable) - not applicable, no new/changed requirement
Additional Notes
Visually verified against the real-world fixture that reported this (slide 3, "Objective" badges):
before the fix the numerals rendered as near-invisible pale shapes; after, both render as clearly
visible purple-outlined hollow numerals, matching PowerPoint's own rendering.
Previously PaintTextLayout only ever painted a run's own <a:solidFill>
fill color, silently ignoring a run-level <a:ln> stroke. A real-world
deck used a near-white fill paired with a purple <a:ln> outline to
produce PowerPoint's hollow-outlined-numeral effect; without outline
support the numerals rendered nearly invisible.
- PptxEffectiveRunProperties gains OutlineWidthEmu/OutlineColor,
resolved via a new GetRunOutline helper (PptxDocument.TextInheritance.cs)
that reuses ResolveLineStyle, restricted to solid-color outlines.
- LineGlyph/PptxGlyphPlacement thread the new fields through layout.
- PaintTextLayout builds each glyph path in local shape space, then
transforms separately for fill vs. ResolveStrokeOutline-based outline
painting, mirroring the existing shape-outline pattern.
- Added unit tests for inheritance resolution and outline painting.
- Documented the fix in docs/design/canvas-net-pptx/pptx-document.md.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds PPTX run-level <a:ln> text-outline rendering for glyphs.
Changes:
Resolves inherited outline width and color.
Threads outline data through layout, autofit scaling, and painting.
Adds tests and design documentation.
Review notes include unresolved moderate issues for explicit stroke cancellation, autofit outline scaling coverage, and dashed outline preservation, plus a documentation nit.
…ine dash arrays
Address two unresolved Copilot code-review findings on PR #39:
1. PptxDocument.TextInheritance.cs: ResolveEffectiveRunProperties now selects
the first tier whose <a:ln> element is present at all (run, then paragraph
defRPr, then placeholder level, then master level) before resolving it,
instead of chaining GetRunOutline''s resolved result with ??. This fixes a
bug where a run''s own explicit outline cancellation (<a:ln><a:noFill/></a:ln>
or a non-positive width) was indistinguishable from "undeclared here" and
incorrectly fell through to inherit a shallower tier''s outline.
2. Threaded the resolved DashArray from ResolveLineStyle all the way through
PptxEffectiveRunProperties, LineGlyph, and PptxGlyphPlacement into
PaintTextLayout''s stroke call, replacing the previously hard-coded
DashArray: null. A dashed run-level <a:ln> now renders dashed instead of
silently degrading to solid. The dash array is also scaled by the autofit
fontScale alongside OutlineWidthEmu.
Added regression tests in PptxTextTests.cs (explicit no-fill/zero-width
cancellation, dash-array resolution matching ResolveLineStyle) and
PptxTextRenderTests.cs (dashed outline paints visible gaps). Updated the
design doc''s "Phase 2 Follow-Up: Run Text Outline Rendering" section with
both corrections.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GetRunOutline delegates to ResolveLineStyle without a blip resolver. For a run-level <a:ln><a:blipFill>...</a:blipFill></a:ln>, ResolveFill therefore throws PptxUnsupportedFeatureException instead of applying the documented solid-only/out-of-scope behavior of skipping the outline; a single unsupported text paint can abort rendering the whole slide. Short-circuit non-solidFill line paints before calling ResolveLineStyle (or otherwise convert unsupported paints to no outline).
…line-scale test
- GetRunOutline now returns null for a run-level <a:ln><a:blipFill> instead
of letting PptxUnsupportedFeatureException propagate and abort the whole
slide render (gradient/pattern line paints already degraded gracefully;
only blipFill threw).
- Documented both null cases (undeclared vs. explicitly cancelled) for
PptxEffectiveRunProperties.OutlineWidthEmu.
- Reconciled the design doc's Scope paragraph with the actual tier-selection
semantics and added a Correction subsection for the blip-fill short-circuit.
- Added regression tests: a run-level blipFill outline resolves to no
outline instead of throwing, and BuildLines' autofit shrink loop scales
OutlineWidthEmu/OutlineDashArray by the same fontScale as SizeEmu.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
Copilot review overview
🟡 Changes recommended
Two critical review issues remain unresolved: unsupported non-solid outlines can throw, and the layout regression test reparents its shared XML element.
…ment reuse
- GetRunOutline now short-circuits gradFill/pattFill/blipFill run-level
line paints (not just blipFill) before calling ResolveLineStyle, since
an unsupported gradient direction or pattern preset can throw
PptxUnsupportedFeatureException just like an unresolved blipFill did -
a single out-of-scope run stroke could otherwise abort rendering the
whole slide.
- Fixed the autofit-scaled-outline layout regression test: the shared
<a:ln> XElement was being reparented into only the last paragraph by
LINQ to XML's Add/Select semantics, leaving glyph[0] without an
outline. Each paragraph now gets its own clone via 'new XElement(ln)'.
- Added regression tests for unsupported path-gradient and unsupported
pattern-preset run outlines resolving to no outline.
- Updated design doc with a fourth Correction subsection documenting the
broader short-circuit.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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
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.
Description
Fixes a bug where PPTX text runs declaring a run-level
<a:ln>(text outline/stroke) renderedwith that outline silently missing. A real-world deck used a near-white
<a:solidFill>pairedwith a purple
<a:ln>stroke to produce PowerPoint's "hollow outlined numeral" effect (large "1"/"2" badges); without outline support the numerals rendered as near-invisible pale shapes.
Root cause:
PptxEffectiveRunProperties/PaintTextLayoutonly ever resolved and painted a run'sown
<a:solidFill>fill color - there was no concept of a run's own<a:ln>(distinct from ashape's
<p:spPr>/<a:ln>, which strokes the shape's geometry, not its text).This was investigated after a separate bug report suspected systemic font-fallback issues; direct
testing against two real-world decks confirmed theme/run font resolution (
SystemFontCatalog/ResolveTextFont) is correct, and the actual (narrower) defect was this missing outline support.Fix
PptxEffectiveRunPropertiesgainsOutlineWidthEmu/OutlineColor, resolved via a newGetRunOutlinehelper (PptxDocument.TextInheritance.cs) that reuses the existingResolveLineStyleresolver already used for shape/picture/connector strokes, restricted tosolid-color outlines (matching the pre-existing solid-color-only run fill color scope).
LineGlyph/PptxGlyphPlacementthread the new fields through layout, withOutlineWidthEmuscaled by the same autofit
fontScalefactor asSizeEmu.PaintTextLayoutbuilds each glyph's path in local shape space, then transforms separately forthe fill paint vs. a
ResolveStrokeOutline-based outline paint - mirroring the existingshape-outline rendering pattern, including its coordinate-scale-aware flatten tolerance.
<a:effectLst><a:glow>remains out of scope, consistent with this project's already-documented<a:effectRef>shape-effects deferral.Type of Change
Related Issues
Closes #
Pre-Submission Checklist
Build and Test
pwsh ./build.ps1(randotnet build/dotnet testdirectly: 10,503 tests passed, 0 failed)Code Quality
Quality Checks
pwsh ./lint.ps1(passed - no errors)Testing
PptxTextTests.cs: 2 new inheritance-resolutiontests;
PptxTextRenderTests.cs: 1 new outline-painting test)mathematically equivalent; pre-existing tests pass unmodified)
Documentation
Outline Rendering" section to
docs/design/canvas-net-pptx/pptx-document.mdAdditional Notes
Visually verified against the real-world fixture that reported this (slide 3, "Objective" badges):
before the fix the numerals rendered as near-invisible pale shapes; after, both render as clearly
visible purple-outlined hollow numerals, matching PowerPoint's own rendering.