fix(shapes): render grouped DrawingML shapes with custom geometry (SD-1877) ❇️#2105
Conversation
…-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
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
There was a problem hiding this comment.
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
wrapNonedrawings 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.
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/vector-shape-helpers.js
Show resolved
Hide resolved
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/vector-shape-helpers.js
Show resolved
Hide resolved
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/vector-shape-helpers.js
Show resolved
Hide resolved
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/vector-shape-helpers.js
Show resolved
Hide resolved
packages/super-editor/src/extensions/shape-group/ShapeGroupView.js
Outdated
Show resolved
Hide resolved
packages/super-editor/src/core/super-converter/v3/handlers/wp/helpers/vector-shape-helpers.js
Show resolved
Hide resolved
…-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
…-drawingml-black-shape
…-drawingml-black-shape
|
🎉 This PR is included in superdoc v1.17.0-next.26 The release is available on GitHub release |
|
🎉 This PR is included in superdoc-cli v0.2.0-next.21 The release is available on GitHub release |
# [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))
Summary
a:custGeom) instead of preset geometry (a:prstGeom)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 + ShapeGroupViewfill-rule="evenodd"for hollow frame paths andvector-effect="non-scaling-stroke"hairline strokes to keep thin borders visible after non-uniform group coordinate scalingwrapNonedrawings no longer incorrectly constrained to content area widthVisual Comparison
Test plan