Skip to content

feat(odf): draw a shape where its draw:transform puts it - #796

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

feat(odf): draw a shape where its draw:transform puts it#796
andiwand merged 2 commits into
mainfrom
odf-drawings/transform

Conversation

@andiwand

@andiwand andiwand commented Aug 31, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Stage 1 of #771, stacked on #795.

draw:transform (19.228) was not read at all, so a rotated, skewed or
matrix-placed shape was drawn unrotated — and a shape placed by a transform
rather than by svg:x/svg:y, which is what libreoffice writes for a rotated
one, was drawn at the origin. 25 occurrences over 4 files in the test corpus,
on draw:custom-shape, draw:frame and draw:path.

odf_geometry.cpp composes the operation list into one DrawingTransform
four unitless numbers and a translation carrying a length, which is what
matrix(a b c d e f) means — and Frame, Rect, Line, Circle and
CustomShape hand it out. The renderer writes it as css, with
transform-origin:0 0, because css otherwise turns a box about its centre.
Composing rather than passing the string through is not optional: ODF angles
are radians where css wants degrees, and no input markup authors our output.

Two things the spec text does not settle

Both decided against libreoffice's own svg export of style-drawing-1.odp:

  • The list applies to the shape left to right, so the translate in
    rotate (a) translate (x y) is not itself rotated. The other reading puts
    the shape's left edge at 22779; libreoffice reports 23084, which is what this
    one gives, to the unit.
  • rotate is counter-clockwise for a positive angle, the opposite of svg's
    handedness in the same y-down space. Libreoffice writes rotate(-19.4) in
    degrees for the file's rotate (0.340164671213695).

Notes

  • A transform is an element accessor rather than a GraphicStyle field because
    get_intermediate_style overrides down the element parent chain, which would
    leak a group's transform onto every child that carries none of its own.
  • The four engines that implement FrameAdapter without a transform to give
    return nothing; ooxml's presentation adapter gets the TODO for a:xfrm.
  • New public DrawingTransform and transform(), mirrored in the JNI, Apple
    and Python bindings.

Verification

odf_geometry_test.cpp, from string literals: composition order, radians and
handedness, the unit a translation keeps, and what is refused.

The reference-output pin is advanced: 6 files over 4 documents
(style-drawing-1.odp, presentation-yunation.odp,
Sealver Measurements.odg), each changed line differing by the inserted
transform / transform-origin declaration and nothing else.

Base automatically changed from odf-drawings/plan to main August 31, 2026 06:45
@andiwand
andiwand force-pushed the odf-drawings/transform branch 2 times, most recently from 902fe5b to 1f02184 Compare August 31, 2026 18:13
andiwand and others added 2 commits August 31, 2026 20:33
`draw:transform` (19.228) was not read at all, so a rotated, skewed or
matrix-placed shape was drawn unrotated — and a shape placed by a transform
rather than by `svg:x`/`svg:y`, which is what libreoffice writes for a rotated
one, was drawn at the origin. 25 occurrences over 4 files in the test corpus,
on `draw:custom-shape`, `draw:frame` and `draw:path`.

`odf_geometry.cpp` composes the operation list into one `DrawingTransform` —
four unitless numbers and a translation carrying a length, which is what
`matrix(a b c d e f)` means — and `Frame`, `Rect`, `Line`, `Circle` and
`CustomShape` hand it out. The renderer writes it as css, with
`transform-origin:0 0`, because css otherwise turns a box about its centre.
Composing rather than passing the string through is not optional: ODF angles
are radians where css wants degrees, and no input markup authors our output.

Two things the spec text does not settle, both decided against libreoffice's
own svg export of `style-drawing-1.odp`:

- the list applies to the shape left to right, so the `translate` in
  `rotate (a) translate (x y)` is *not* itself rotated. The other reading puts
  the shape's left edge at 22779; libreoffice reports 23084, which is what this
  one gives, to the unit.
- `rotate` is counter-clockwise for a positive angle, the opposite of svg's
  handedness in the same y-down space. Libreoffice writes `rotate(-19.4)` in
  degrees for the file's `rotate (0.340164671213695)`.

A transform is an element accessor rather than a `GraphicStyle` field because
`get_intermediate_style` overrides down the element parent chain, which would
leak a group's transform onto every child that carries none of its own.

The four engines that implement `FrameAdapter` without a transform to give
return nothing; ooxml's presentation adapter gets the `TODO` for `a:xfrm`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XABfEapaADjFQCt1vjmDF
The four drawing files the transform moves: two `draw:frame` on a slide of
`style-drawing-1.odp`, one on `presentation-yunation.odp`, and the shapes of
`Sealver Measurements.odg`. Every changed line differs by the inserted
`transform` / `transform-origin` declaration and nothing else.

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/transform branch from 1f02184 to eb177f2 Compare August 31, 2026 18:33
@andiwand
andiwand merged commit 2528663 into main Aug 31, 2026
34 checks passed
@andiwand
andiwand deleted the odf-drawings/transform branch August 31, 2026 18:43
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>
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