Skip to content

feat(drawing): improve drawing-object fidelity for picture fills, connectors, charts, and anchored media (SD-2998 and SD-2996)#3779

Open
luccas-harbour wants to merge 30 commits into
mainfrom
luccas/sd-2998-feature-drawing-objects-parent-surface-fidelity
Open

feat(drawing): improve drawing-object fidelity for picture fills, connectors, charts, and anchored media (SD-2998 and SD-2996)#3779
luccas-harbour wants to merge 30 commits into
mainfrom
luccas/sd-2998-feature-drawing-objects-parent-surface-fidelity

Conversation

@luccas-harbour

Copy link
Copy Markdown
Contributor

Summary

This branch is a broad fidelity pass on DrawingML objects so they render against their parent surface (page, column, table, header/footer) the way Word does. It adds support for picture-filled vector shapes, real connector geometry, horizontal bar charts with data labels, and corrects the positioning of page-relative anchored media in headers/footers and column-anchored images inside tables. Along the way it consolidates the line-end/marker and connector rendering logic into a single shared module so the three shape renderers (DomPainter, VectorShapeView, ShapeGroupView) stop duplicating it.

Picture fills for vector shapes

DrawingML shapes can carry an image fill (a:blipFill) instead of a solid/gradient color. Previously these rendered as a flat #cccccc placeholder.

  • New PictureFill type in @superdoc/contracts ({ type: 'picture', src, rId?, extension? }) added to the FillColor union.
  • The converter (vector-shape-helpers.js) now resolves a:blipFillr:embed relationship → media target path, returning a PictureFill for both standalone shapes and shape-group children. extractFillColor takes converter params to do the relationship lookup.
  • The v1 layout-adapter hydrates picture-fill src (media path → data URI) for vector shapes, textbox shapes, and shape-group vector children, mirroring how image runs are hydrated. normalizeFillColor / isPictureFill recognize the new shape.
  • All three renderers paint it as an SVG <pattern>/<image> clipped to the shape geometry (with a CSS background-image fallback path in DomPainter). Shared createPictureFillPattern helper added in svg-utils.js.

Connector shapes

  • New shared module packages/preset-geometry/connectors.js (+ .d.ts) provides bent/curved connector path geometry (bentConnector2-5, curvedConnector2-5), isConnectorPresetShape, non-scaling-stroke helpers, and the canonical line-end marker/shape builders.
  • DomPainter, VectorShapeView, and ShapeGroupView now import connector geometry and line-end rendering from this module instead of each carrying their own copy. The per-class createLineEndMarker / createLineEndShape implementations are deleted.
  • Connectors and line-like shapes render with vector-effect: non-scaling-stroke and a stretched/preserveAspectRatio="none" viewBox so strokes stay uniform when the shape box is non-square. Non-connector preset SVGs get their viewBox expanded by half the stroke width so centered strokes aren't clipped (container overflow switched from hidden to visible).
  • Degenerate horizontal/vertical lines (line, straightConnector1) are now centered on the thin axis instead of drawn corner-to-corner.
  • Line-end marker sizing changed from effect-extent-driven to stroke-relative (base size 4 → 8), and marker head/tail assignment was corrected (headmarker-start, tailmarker-end).

Charts

  • Horizontal bar charts (barDirection === 'bar') are now rendered with their own layout: category labels on the value axis, value ticks/gridlines along the bottom, gap-width-aware bar sizing, and category ordering that respects axis orientation.
  • Data labels (c:dLbls) are parsed at both chart and series level (merged, with c:delete honored) and painted for vertical and horizontal bars, with outEnd/inEnd/inBase/center placement and percentage/number formatting from c:numFmt.
  • Additional chart-model fidelity: gapWidth, axis deleted and majorGridlines flags, chartAreaBorder (chart-space outline), and a right-side legend layout. Gridlines are only drawn when the source declares major gridlines; the SVG element-budget estimate accounts for the new elements.
  • Paint-cache invalidation: versionSignature now hashes the full chartData and fillColor objects (via a new valueVersion helper) and includes chart geometry rotation/flips, so chart and fill changes actually bust the cache.

Anchored media positioning

  • Header/footer page-relative anchors: normalization previously only corrected footer Y. It now runs for headers too and additionally corrects the X axis for horizontally page-relative anchors, using the real physical page dimensions via resolveAnchoredGraphicX. Horizontal and vertical anchors are treated independently (each branch re-checks its own axis), and column-relative X is left content-local so the painter adds the page margin exactly once. Painter comments updated to match.
  • Column-anchored images in tables: removed the tableIndent parameter threaded through renderTableFragmentrenderTableRowrenderTableCell. Column (ST_RelFromH) anchors already encode wp:posOffset relative to the column, so subtracting tblInd in addition to the cell's local x shifted table images left. Now only the cell's local x is subtracted.

Shared media-target normalization

Extracted the duplicated normalizeTargetPath (relationship target → word/media/... media key) into a single helpers/media-target-path.js, now used by the watermark importer, image-node encoder, and vector-shape helpers.

Tests

New/expanded unit coverage for connectors, picture-fill hydration and rendering, horizontal bar charts and data labels, header/footer X normalization, table-indent removal, media-target-path, and the versionSignature object hashing.

@luccas-harbour luccas-harbour self-assigned this Jun 26, 2026
@luccas-harbour luccas-harbour requested a review from a team as a code owner June 26, 2026 20:42
@linear-code

linear-code Bot commented Jun 26, 2026

Copy link
Copy Markdown

SD-2998

SD-2996

…d images

The table indent is already applied to the cell x offset, so subtracting it
again shifted column-relative anchored images left by the indent amount.
Replace the local computePhysicalAnchorX helper with the shared
resolveAnchoredGraphicX from @superdoc/contracts, and apply X
normalization to all page-relative fragments rather than only those with
a page-relative horizontal anchor. Column-relative drawings in headers
and footers now resolve their X against the physical page margins,
fixing media placed at the wrong horizontal position.
Add support for horizontal (bar-direction) bar charts in the DOM chart
renderer, including category labels, in-bar percentage data labels, gap
width, value-axis ticks/gridlines, and axis deletion.

- Parse c:gapWidth, c:dLbls (showVal/numFmt/dLblPos), axis c:delete, and
  c:majorGridlines from chart XML, merging series-level data labels over
  chart-level ones per field
- Extend ChartModel/ChartSeriesData/ChartAxisConfig contracts with
  gapWidth, dataLabels, deleted, and majorGridlines
- Add a dedicated horizontal bar layout and renderers, reversing
  category order for top-down display and formatting percentage labels
- Account for the new elements in the SVG element-budget estimate
…ends

Render the chart-area outline from c:spPr (honoring a:noFill to disable it),
gate value-axis gridlines behind the OOXML majorGridlines flag instead of
always drawing them, and lay out right-positioned legends as a column beside
bar charts that reserve right-edge space. Thread per-chart padding through the
bar layout so the right legend gets dedicated room, and align gridline color
with the axis color.
Drop the connector re-export barrel in shared/svg-utils.js and have
ShapeGroupView, VectorShapeView, and the svg-utils test import
connector helpers directly from @superdoc/preset-geometry/connectors.
normalizeFragmentsForRegion was rewriting column-relative X whenever the
vertical anchor was page-relative, conflating the two independent OOXML
axes. Switch the gate to the horizontal anchor (hRelativeFrom === 'page')
so column-relative X stays content-local and the painter applies the
physical page margin exactly once. Update tests to cover both axes
independently.
@luccas-harbour luccas-harbour force-pushed the luccas/sd-2998-feature-drawing-objects-parent-surface-fidelity branch from bead74d to 0c8ce93 Compare June 26, 2026 20:47

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bead74d2d7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread packages/layout-engine/layout-engine/src/normalize-header-footer-fragments.ts Outdated
Comment thread packages/layout-engine/painters/dom/src/chart-renderer.ts Outdated
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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