Description
A RichText shape that carries a rotation is written into content.xml as a draw:transform rather than as svg:x/svg:y. Two things go wrong with it.
1. The shape is written in the wrong place. translate moves the frame after rotate has turned it about the origin, while the rotation a shape carries is about its own centre. The point to write is therefore where the top left corner lands once the shape has been turned. Writer/ODPresentation/Content.php writes the raw offsets instead — and writes them transposed, X into Y and Y into X.
2. Nothing reads it back. Reader/ODPresentation.php looks only at svg:x/svg:y, so a turned shape comes back at (0, 0) with no rotation, whichever loader picks it up.
The first half is visible without any reader involved: an ODP written today puts a rotated shape somewhere else than the PPTX written from the same deck.
Steps to reproduce
$shape = $presentation->getActiveSlide()->createRichTextShape();
$shape->setOffsetX(400)->setOffsetY(100)->setWidth(200)->setHeight(100)->setRotation(30);
$shape->createTextRun('Turned');
Expected behavior
The shape renders where the same deck renders through the PowerPoint2007 Writer, and reads back with the offset and the rotation it was given.
Actual behavior
Written:
draw:transform="rotate (-0.5235987755983) translate (2.646cm 10.583cm)"
2.646cm is the Y offset and 10.583cm is the X one. LibreOffice, saving the same shape, writes translate (11.599cm 1.5cm).
Rendered through LibreOffice 26.2 and measured on the shape's fill, the ODP puts its centre at (214, 653) where the PPTX from the same deck puts it at (661, 198). The bounding boxes match at 293×245, so the rotation itself is applied — only the placement is wrong.
Read back with IOFactory::createReader('ODPresentation'), the shape returns getOffsetX() === 0, getOffsetY() === 0, getRotation() === 0.
Notes
Fixing the Writer moves a rotated shape in newly written files. Files written by earlier versions keep the position they were given.
Found while walking every property of a shape through each Writer/Reader pair and diffing what came back.
Description
A
RichTextshape that carries a rotation is written intocontent.xmlas adraw:transformrather than assvg:x/svg:y. Two things go wrong with it.1. The shape is written in the wrong place.
translatemoves the frame afterrotatehas turned it about the origin, while the rotation a shape carries is about its own centre. The point to write is therefore where the top left corner lands once the shape has been turned.Writer/ODPresentation/Content.phpwrites the raw offsets instead — and writes them transposed, X into Y and Y into X.2. Nothing reads it back.
Reader/ODPresentation.phplooks only atsvg:x/svg:y, so a turned shape comes back at (0, 0) with no rotation, whichever loader picks it up.The first half is visible without any reader involved: an ODP written today puts a rotated shape somewhere else than the PPTX written from the same deck.
Steps to reproduce
Expected behavior
The shape renders where the same deck renders through the PowerPoint2007 Writer, and reads back with the offset and the rotation it was given.
Actual behavior
Written:
2.646cm is the Y offset and 10.583cm is the X one. LibreOffice, saving the same shape, writes
translate (11.599cm 1.5cm).Rendered through LibreOffice 26.2 and measured on the shape's fill, the ODP puts its centre at (214, 653) where the PPTX from the same deck puts it at (661, 198). The bounding boxes match at 293×245, so the rotation itself is applied — only the placement is wrong.
Read back with
IOFactory::createReader('ODPresentation'), the shape returnsgetOffsetX() === 0,getOffsetY() === 0,getRotation() === 0.Notes
Fixing the Writer moves a rotated shape in newly written files. Files written by earlier versions keep the position they were given.
Found while walking every property of a shape through each Writer/Reader pair and diffing what came back.