Skip to content

feat(odf): draw the shape elements the parser used to drop - #797

Merged
andiwand merged 2 commits into
mainfrom
odf-drawings/shapes
Aug 31, 2026
Merged

feat(odf): draw the shape elements the parser used to drop#797
andiwand merged 2 commits into
mainfrom
odf-drawings/shapes

Conversation

@andiwand

@andiwand andiwand commented Aug 31, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Stage 2 of #771, on main now that #796 has landed.

parse_any_element_tree knew nine drawing tags; everything else fell through
its final return {null_element_id, …} and vanished with its subtree. This
maps the rest onto the types that already exist rather than inventing new ones,
the way draw:g already collapses onto frame:

tag type why
draw:path, draw:polygon, draw:polyline, draw:regular-polygon, draw:connector custom_shape geometry given as a path, which is what ODF's own term means
draw:ellipse, and a draw:circle/draw:ellipse draw:kind cuts circle / custom_shape the box, or the arc it traces
draw:measure line svg:x1/y1/x2/y2, same as draw:line
draw:caption rect the box; the callout tail is dropped
dr3d:scene a 3-D scene is not a 2-D path

CustomShape::path() hands the renderer a DrawingPath — an svg d plus the
user-space box it is written in — and the renderer draws it into an
<svg viewBox> sized to the shape. odf_geometry.cpp builds one from svg:d,
draw:points, draw:corners or the arc draw:kind asks for. An svg:d is
parsed and re-serialised rather than forwarded, so nothing the file wrote
reaches the output as markup.

Notes

  • A draw:connector carries svg:x1/y1/x2/y2 but no box, and a curve's
    control points can reach outside its endpoints, so its path is what places
    it; the svg:d is in the page's own coordinates at 1/100 mm. A straight one boxes
    to nothing in one axis, which svg refuses, hence the one-unit floor.
  • An outline that never closes is drawn fill="none": svg would else fill it as
    if it did, and libreoffice draws an open draw:path as a line. 3 of the
    corpus's 6 draw:path are open; all 349 enhanced paths close.
  • vector-effect="non-scaling-stroke" on the path: the view box scales, and
    with preserveAspectRatio="none" unevenly, which the stroke must not follow.
  • Two fixes along the way: translate_circle wrote r="50%", a circle
    inscribed in the box rather than the ellipse ODF means, and text:measure
    had no parser, so a measure's label came out empty.
  • New public DrawingPath and CustomShape::path(), mirrored in the JNI,
    Apple and Python bindings.

Verification

Five of the nine tags occur nowhere in the test corpus, so they are covered by
unit tests over string literals instead: odf_geometry_test.cpp.
SpotfaceCoord40270-L.odg was compared against libreoffice's own png export.

The reference-output pin is advanced: 17 files over 8 documents, every changed
line an insertion of a newly drawn shape or its label, bar the 26 <circle>
that become <ellipse>.

@andiwand
andiwand force-pushed the odf-drawings/shapes branch 2 times, most recently from 3620672 to 7b7778d Compare August 31, 2026 06:10
@andiwand
andiwand force-pushed the odf-drawings/transform branch 3 times, most recently from 1f02184 to eb177f2 Compare August 31, 2026 18:33
Base automatically changed from odf-drawings/transform to main August 31, 2026 18:43
andiwand and others added 2 commits August 31, 2026 21:15
`parse_any_element_tree` knew nine drawing tags; everything else fell through
its final `return {null_element_id, …}` and vanished with its subtree. This maps
the rest onto the types that already exist rather than inventing new ones, the
way `draw:g` already collapses onto `frame`:

- `draw:path`, `draw:polygon`, `draw:polyline`, `draw:regular-polygon` and
  `draw:connector` are custom shapes, which is what ODF's own term means for a
  shape whose geometry is given rather than named
- `draw:ellipse` is a circle, and so is a `draw:circle`; one that `draw:kind`
  cuts to an arc, section or chord becomes a custom shape carrying that arc
- `draw:measure` is a line, `draw:caption` a rect
- `dr3d:scene` stays unhandled: a 3-D scene is not a 2-D path

`CustomShape::path()` hands the renderer a `DrawingPath` — an svg `d` plus the
user-space box it is written in — and the renderer draws it into an
`<svg viewBox>` sized to the shape. `odf_geometry.cpp` builds one from
`svg:d`, `draw:points`, `draw:corners` or the arc `draw:kind` asks for. An
`svg:d` is parsed and re-serialised rather than forwarded, so nothing the file
wrote reaches the output as markup.

A `draw:connector` states no box at all — its `svg:d` is in the page's own
coordinates at 1/100 mm — so its path is what places it. A straight one boxes to
nothing in one axis, which svg refuses, hence the one-unit floor.

Two fixes made along the way: `translate_circle` wrote `r="50%"`, which is a
circle inscribed in the box rather than the ellipse ODF means, and
`text:measure` had no parser, so a measure's label came out empty.

Five of the nine tags occur nowhere in the test corpus, so they are covered by
unit tests over string literals instead: `odf_geometry_test.cpp`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XABfEapaADjFQCt1vjmDF
17 files over 8 documents. Every changed line is an insertion of a newly drawn
shape or its label, bar 26 `<circle>` that become `<ellipse>`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UJ6NPShQBp1nX7WuMvty8V
@andiwand
andiwand force-pushed the odf-drawings/shapes branch from 7b7778d to c5ecb74 Compare August 31, 2026 19:17
@andiwand
andiwand merged commit 3d17edf into main Aug 31, 2026
36 checks passed
@andiwand
andiwand deleted the odf-drawings/shapes branch August 31, 2026 19:26
andiwand added a commit that referenced this pull request Aug 31, 2026
`PLAN.md` said to delete it once nothing was left under *Today*, and #771's
four goals are on main: `draw:transform` (#796), the shape elements with no
parser (#797), `draw:enhanced-geometry` (#798, #799) and the `draw:object`
chart (#800). What remains of drawings is the unticked boxes in `README.md`;
the two decisions the plan held that the checklist cannot express — preset
`draw:type` shapes and connector routing — move to `AGENTS.md`.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@andiwand andiwand mentioned this pull request Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant