Skip to content

fix(shapes): render grouped DrawingML shapes with custom geometry (SD-1877) ❇️#2105

Merged
harbournick merged 6 commits intomainfrom
tadeu/sd-1877-grouped-drawingml-black-shape
Feb 27, 2026
Merged

fix(shapes): render grouped DrawingML shapes with custom geometry (SD-1877) ❇️#2105
harbournick merged 6 commits intomainfrom
tadeu/sd-1877-grouped-drawingml-black-shape

Conversation

@tupizz
Copy link
Contributor

@tupizz tupizz commented Feb 19, 2026

Summary

  • Fixed grouped DrawingML shapes rendering as solid black rectangles when they use custom geometry (a:custGeom) instead of preset geometry (a:prstGeom)
  • Added extractCustomGeometry() parser that converts DrawingML path commands (moveTo, lnTo, cubicBezTo, quadBezTo, close) to SVG path data, wired through the full pipeline: DOCX import → PM node → pm-adapter → contracts → DomPainter + ShapeGroupView
  • Used fill-rule="evenodd" for hollow frame paths and vector-effect="non-scaling-stroke" hairline strokes to keep thin borders visible after non-uniform group coordinate scaling
  • Fixed floating drawing width constraint: wrapNone drawings no longer incorrectly constrained to content area width
  • Rewrote group coordinate rendering to use visual-space positioning (pre-scaled by importer) instead of CSS scale transforms

Visual Comparison

Before (main) After (this PR) Word Reference
CleanShot 2026-02-18 at 22 10 53 CleanShot 2026-02-18 at 22 11 19 CleanShot 2026-02-18 at 22 11 12@2x
Solid black rectangle hides all content Border frame + gray background render correctly Expected rendering in Microsoft Word

Test plan

  • All unit tests pass (super-editor: 6162, painter-dom: 598, measuring-dom: 219, pm-adapter: 1533)
  • All 56 visual regression tests pass (0 failures)
  • Verified in browser with the sample DOCX — shape group with border frame and gray background renders correctly matching Word
  • Test with other documents containing grouped shapes to verify no regressions
  • Test with documents containing preset geometry shapes to verify existing rendering is unaffected

…-1877)

Grouped DrawingML shapes with custom geometry (a:custGeom) were rendering
as solid black rectangles because the importer and renderers only
supported preset geometry (a:prstGeom). This adds full custom geometry
support and fixes several related sizing issues.

- Add extractCustomGeometry() parser that converts DrawingML path commands
  (moveTo, lnTo, cubicBezTo, quadBezTo, close) to SVG path data
- Wire custom geometry through the full pipeline: import → PM node attr →
  pm-adapter → contracts → DomPainter + ShapeGroupView
- Use fill-rule="evenodd" for hollow frame paths (winding creates cutout)
- Add vector-effect="non-scaling-stroke" hairline stroke so thin borders
  remain visible after non-uniform group coordinate scaling
- Fix floating drawing width: wrapNone drawings no longer constrained to
  content area width, matching Word's behavior
- Rewrite group coordinate rendering to use visual-space positioning
  (pre-scaled by importer) instead of CSS scale transforms
Copilot AI review requested due to automatic review settings February 19, 2026 01:04
@linear
Copy link

linear bot commented Feb 19, 2026

@chatgpt-codex-connector
Copy link

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes rendering of grouped DrawingML shapes that use custom geometry (a:custGeom) instead of preset geometry (a:prstGeom). Previously, these shapes would render as solid black rectangles. The fix adds a complete pipeline for parsing custom geometry from DOCX files and rendering them as SVG paths.

Changes:

  • Added extractCustomGeometry() function to parse DrawingML path commands and convert them to SVG path data
  • Updated rendering in both DomPainter and ShapeGroupView to handle custom geometry with proper coordinate space mapping
  • Fixed floating drawing width constraints to not constrain wrapNone drawings to content area width
  • Simplified group coordinate rendering by removing CSS scale transforms in favor of pre-scaled positions

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
packages/super-editor/src/extensions/vector-shape/vector-shape.js Added customGeometry attribute to vectorShape node schema
packages/super-editor/src/extensions/shape-group/ShapeGroupView.js Added custom geometry rendering logic with nested SVG and viewBox mapping
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/vector-shape-helpers.js Added extractCustomGeometry() and convertDrawingMLPathToSvg() to parse DrawingML path commands
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/encode-image-node-helpers.js Integrated custom geometry extraction into shape and shape group import
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/encode-image-node-helpers.test.js Updated test to reflect new behavior when shape kind is missing
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/encode-shape-group-helpers.test.js Added mock for extractCustomGeometry function
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/encode-image-node-helpers-header.test.js Added mock for extractCustomGeometry function
packages/layout-engine/pm-adapter/src/converters/shapes.ts Added customGeometry to DrawingBlock conversion
packages/layout-engine/painters/dom/src/renderer.ts Added tryCreateCustomGeometrySvg() method with hairline stroke workaround and simplified group transform rendering
packages/layout-engine/measuring/dom/src/index.ts Fixed width constraint logic to exclude floating drawings (wrapNone)
packages/layout-engine/contracts/src/index.ts Added CustomGeometryData type definition

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…-1877)

- Add zero-dimension viewBox guard in DomPainter (return null when viewW/viewH is 0)
- Add the same guard in ShapeGroupView so it falls through to fallback rendering
- Add hairline edge stroke workaround to ShapeGroupView to match DomPainter behavior
  (filled shapes without a stroke can lose sub-pixel borders under non-uniform group transforms)
- Add explicit default: break in the DrawingML path command switch to document that
  unknown commands (e.g. arcTo) are intentionally skipped
- Fix JSDoc: remove arcTo from the supported commands list since it has no handler
@tupizz tupizz enabled auto-merge (squash) February 25, 2026 21:39
@tupizz tupizz changed the title fix(shapes): render grouped DrawingML shapes with custom geometry (SD-1877) fix(shapes): render grouped DrawingML shapes with custom geometry (SD-1877) ❇️ Feb 25, 2026
Copy link
Collaborator

@harbournick harbournick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@harbournick harbournick disabled auto-merge February 27, 2026 00:08
@harbournick harbournick merged commit 14985a5 into main Feb 27, 2026
8 checks passed
@harbournick harbournick deleted the tadeu/sd-1877-grouped-drawingml-black-shape branch February 27, 2026 00:08
@superdoc-bot
Copy link
Contributor

superdoc-bot bot commented Feb 27, 2026

🎉 This PR is included in superdoc v1.17.0-next.26

The release is available on GitHub release

@superdoc-bot
Copy link
Contributor

superdoc-bot bot commented Feb 27, 2026

🎉 This PR is included in superdoc-cli v0.2.0-next.21

The release is available on GitHub release

harbournick pushed a commit that referenced this pull request Feb 28, 2026
# [1.17.0](v1.16.0...v1.17.0) (2026-02-28)

### Bug Fixes

* active track change ([#2163](#2163)) ([108c14d](108c14d))
* add currentTotalPages getter and pagination-update event ([#2202](#2202)) ([95b4579](95b4579)), closes [#958](#958)
* always call resolveComment after custom TC bubble handlers (SD-2049) ([#2204](#2204)) ([34fb4e0](34fb4e0))
* backward replace insert text ([#2172](#2172)) ([66f0849](66f0849))
* **collaboration:** deduplicate updateYdocDocxData during replaceFile (SD-1920) ([#2162](#2162)) ([52962fc](52962fc))
* **comments:** cross-page collision avoidance for floating comment bubbles (SD-1998) ([#2180](#2180)) ([6cfbeca](6cfbeca))
* **comments:** remove synchronous dispatch from plugin apply() (SD-1940) ([#2157](#2157)) ([887175b](887175b))
* **css:** scope ProseMirror CSS to prevent bleeding into host apps (SD-1850) ([#2134](#2134)) ([b9d98fa](b9d98fa))
* document-api improvements, plan mode, query.match, mutations ([6221580](6221580))
* **document-api:** delete table cell fix ([#2209](#2209)) ([5e5c43f](5e5c43f))
* **document-api:** distribute columns command fixes ([#2207](#2207)) ([8f4eaf7](8f4eaf7))
* **document-api:** fix cell shading in document api ([#2215](#2215)) ([456f60e](456f60e))
* **document-api:** insert table cell ([#2210](#2210)) ([357ee90](357ee90))
* **document-api:** plan-engine reliability fixes and error diagnostics ([#2185](#2185)) ([abfd81b](abfd81b))
* **document-api:** split table cell command ([#2217](#2217)) ([0b3e2b4](0b3e2b4))
* **document-api:** split table command ([#2214](#2214)) ([ec31699](ec31699))
* **editor:** render styles applied inside SDT fields (SD-2011) ([#2188](#2188)) ([9c34be3](9c34be3))
* **editor:** selection highlight flickers when dragging across mark boundaries (SD-2024) ([#2205](#2205)) ([ba03e76](ba03e76))
* extract duplicate block identity normalization from docxImporter ([7f7ff93](7f7ff93))
* improve backspace behavior near run boundaries for tracked changes ([#2175](#2175)) ([6c9c7a3](6c9c7a3))
* **layout:** per-section footer constraints for multi-section docs (SD-1837) ([#2022](#2022)) ([e11acc5](e11acc5))
* normalize review namespace into trackChanges, harden input validation ([33e907b](33e907b))
* outside click for toolbar dropdown ([#2174](#2174)) ([5f859c7](5f859c7))
* preserve line spacing and indentation on Google Docs paste ([#2183](#2183)) ([b9a7357](b9a7357)), closes [#2151](#2151)
* **shapes:** render grouped DrawingML shapes with custom geometry (SD-1877) ❇️ ([#2105](#2105)) ([14985a5](14985a5))
* support cell spacing ([#1879](#1879)) ([1639967](1639967))
* **tables:** expand auto-width tables to fill available page width ([#2109](#2109)) ([15f36bc](15f36bc))
* text highlight on export ([#2189](#2189)) ([9cbd022](9cbd022))
* track highlight changes ([#2192](#2192)) ([e164625](e164625))
* undo/redo actions ([#2161](#2161)) ([495e92f](495e92f))

### Features

* allow custom accept/reject handlers for TC bubbles ([#1921](#1921)) ([e30abf6](e30abf6))
* **document-api:** add format operations font size alignment color font family ([#2179](#2179)) ([f19c688](f19c688))
* **document-api:** add plan-based mutation engine with query.match and style capture ([#2160](#2160)) ([365293a](365293a))
* **document-api:** doc default initial styles ([#2184](#2184)) ([f25e41f](f25e41f))
* **document-api:** include anchored text in comments list response ([#2177](#2177)) ([b3a2912](b3a2912))
* **document-api:** inline formatting parity core end-to-end ([#2197](#2197)) ([b405b03](b405b03))
* **document-api:** inline formatting rpr parity ([#2198](#2198)) ([41ab771](41ab771))
* **document-api:** section commands ([#2199](#2199)) ([ec4abe3](ec4abe3))
* **document-api:** support deleting entire block nodes not only text ([#2181](#2181)) ([2897246](2897246))
* **document-api:** table of contents commands ([#2200](#2200)) ([baa72c4](baa72c4))
* **document-api:** tables namespace and commands ([#2182](#2182)) ([b80ee31](b80ee31))
* **markdown:** add markdown override to sdk, improve conversion ([#2196](#2196)) ([04a1c71](04a1c71))
* preserve w:view setting through DOCX round-trip ([#2190](#2190)) ([48b4210](48b4210)), closes [#2070](#2070)
* **track-changes:** clear comment bubbles when bulk accept or reject TCs ([#2159](#2159)) ([27fbe8e](27fbe8e))

### Performance Improvements

* **comments:** batch tracked change comment creation on load ([#2166](#2166)) ([0c2eca5](0c2eca5))
* **comments:** batch tracked change creation and virtualize floating bubbles (SD-1997) ([#2168](#2168)) ([70fd7d9](70fd7d9))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants