feat(odf): draw a shape where its draw:transform puts it - #796
Merged
Conversation
andiwand
force-pushed
the
odf-drawings/transform
branch
2 times, most recently
from
August 31, 2026 18:13
902fe5b to
1f02184
Compare
`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
force-pushed
the
odf-drawings/transform
branch
from
August 31, 2026 18:33
1f02184 to
eb177f2
Compare
This was referenced Aug 31, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
Stage 1 of #771, stacked on #795.
draw:transform(19.228) was not read at all, so a rotated, skewed ormatrix-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 rotatedone, was drawn at the origin. 25 occurrences over 4 files in the test corpus,
on
draw:custom-shape,draw:frameanddraw:path.odf_geometry.cppcomposes the operation list into oneDrawingTransform—four unitless numbers and a translation carrying a length, which is what
matrix(a b c d e f)means — andFrame,Rect,Line,CircleandCustomShapehand it out. The renderer writes it as css, withtransform-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:translateinrotate (a) translate (x y)is not itself rotated. The other reading putsthe shape's left edge at 22779; libreoffice reports 23084, which is what this
one gives, to the unit.
rotateis counter-clockwise for a positive angle, the opposite of svg'shandedness in the same y-down space. Libreoffice writes
rotate(-19.4)indegrees for the file's
rotate (0.340164671213695).Notes
GraphicStylefield becauseget_intermediate_styleoverrides down the element parent chain, which wouldleak a group's transform onto every child that carries none of its own.
FrameAdapterwithout a transform to givereturn nothing; ooxml's presentation adapter gets the
TODOfora:xfrm.DrawingTransformandtransform(), mirrored in the JNI, Appleand Python bindings.
Verification
odf_geometry_test.cpp, from string literals: composition order, radians andhandedness, 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 insertedtransform/transform-origindeclaration and nothing else.