diff --git a/docs/changes/1.3.0.md b/docs/changes/1.3.0.md index 33b870b7a..a79857690 100644 --- a/docs/changes/1.3.0.md +++ b/docs/changes/1.3.0.md @@ -4,6 +4,7 @@ ## Enhancements - `phpoffice/phpspreadsheet`: Allow version 5.0 by [@seanlynchwv](http://github.com/seanlynchwv) in [#879](https://github.com/PHPOffice/PHPPresentation/pull/879) +- Added ability to set rounded corner radius by [@seanlynchwv](http://github.com/seanlynchwv) in [#880](https://github.com/PHPOffice/PHPPresentation/pull/880) ## Bug fixes diff --git a/src/PhpPresentation/Shape/AutoShape.php b/src/PhpPresentation/Shape/AutoShape.php index 4bb6b1237..3fbd8527a 100644 --- a/src/PhpPresentation/Shape/AutoShape.php +++ b/src/PhpPresentation/Shape/AutoShape.php @@ -273,4 +273,28 @@ public function setOutline(Outline $outline): self return $this; } + + /** @var null|int */ + private $roundRectCorner; + + public function getRoundRectCorner(): ?int + { + return $this->roundRectCorner; + } + + /** + * Set corner radius. + */ + public function setRoundRectCorner(int $pixels): self + { + $this->roundRectCorner = max(0, $pixels); + + return $this; + } + + // override the hash so radius works + public function getHashCode(): string + { + return md5(parent::getHashCode() . $this->type . $this->text . (string) $this->roundRectCorner . __CLASS__); + } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php index 2f0544fd7..bbea6160d 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php @@ -1187,9 +1187,31 @@ protected function writeShapeAutoShape(XMLWriter $objWriter, AutoShape $shape, i // p:sp\p:spPr\a:prstGeom $objWriter->startElement('a:prstGeom'); $objWriter->writeAttribute('prst', $shape->getType()); - // p:sp\p:spPr\a:prstGeom\a:avLst - $objWriter->writeElement('a:avLst'); - // p:sp\p:spPr\a:prstGeom\ + + // a:avLst (+ optional adj for roundRect) + $needsAdj = ($shape->getType() === AutoShape::TYPE_ROUNDED_RECTANGLE); + $cornerPx = $shape->getRoundRectCorner(); + + if ($needsAdj && $cornerPx !== null) { + $minHalf = (int) floor(min($shape->getWidth(), $shape->getHeight()) / 2); + + if ($minHalf > 0) { + $adj = (int) round($cornerPx / $minHalf * 50000); + $adj = max(0, min(50000, $adj)); + + $objWriter->startElement('a:avLst'); + $objWriter->startElement('a:gd'); + $objWriter->writeAttribute('name', 'adj'); + $objWriter->writeAttribute('fmla', 'val ' . (string) $adj); + $objWriter->endElement(); // a:gd + $objWriter->endElement(); // a:avLst + } else { + // invalid size, just emit empty avLst + $objWriter->writeElement('a:avLst'); + } + } else { + $objWriter->writeElement('a:avLst'); + } $objWriter->endElement(); // Fill $this->writeFill($objWriter, $shape->getFill()); diff --git a/tests/PhpPresentation/Tests/Shape/AutoShapeTest.php b/tests/PhpPresentation/Tests/Shape/AutoShapeTest.php index cd601562d..4fc23ce8a 100644 --- a/tests/PhpPresentation/Tests/Shape/AutoShapeTest.php +++ b/tests/PhpPresentation/Tests/Shape/AutoShapeTest.php @@ -20,9 +20,14 @@ namespace PhpOffice\PhpPresentation\Tests\Shape; +use PhpOffice\PhpPresentation\PhpPresentation; use PhpOffice\PhpPresentation\Shape\AutoShape; +use PhpOffice\PhpPresentation\Style\Color; +use PhpOffice\PhpPresentation\Style\Fill; use PhpOffice\PhpPresentation\Style\Outline; +use PhpOffice\PhpPresentation\Writer\PowerPoint2007; use PHPUnit\Framework\TestCase; +use ZipArchive; class AutoShapeTest extends TestCase { @@ -64,4 +69,56 @@ public function testType(): void self::assertInstanceOf(AutoShape::class, $object->setType(AutoShape::TYPE_HEXAGON)); self::assertEquals(AutoShape::TYPE_HEXAGON, $object->getType()); } + + public function testNoRadiusByDefaultIsNull(): void + { + $shape = new AutoShape(); + self::assertNull($shape->getRoundRectCorner()); + } + + public function testWriterEmitsAdjGuideForRoundRect(): void + { + $ppt = new PhpPresentation(); + $slide = $ppt->getActiveSlide(); + + $width = 200; + $height = 100; + $padding = 5; + $minHalf = (int) floor(min($width, $height) / 2); + $expectedAdj = (int) round($padding / $minHalf * 50000); // 5000 + + $shape = (new AutoShape()) + ->setType(AutoShape::TYPE_ROUNDED_RECTANGLE) + ->setWidth($width)->setHeight($height) + ->setRoundRectCorner($padding); + + // Give it a fill so it's an obvious shape + $shape->getFill()->setFillType(Fill::FILL_SOLID)->setStartColor(new Color('FFFFFFFF')); + $slide->addShape($shape); + + $tmpFile = tempnam(sys_get_temp_dir(), 'pptx_'); + $writer = new PowerPoint2007($ppt); + $writer->save($tmpFile); + + // Open the pptx and read slide1.xml + $zip = new ZipArchive(); + self::assertTrue($zip->open($tmpFile) === true, 'Failed to open pptx zip'); + $xml = $zip->getFromName('ppt/slides/slide1.xml'); + $zip->close(); + @unlink($tmpFile); + + self::assertIsString($xml); + + // Must contain roundRect geometry and the adj guide with expected value + self::assertStringContainsString('', $xml); + + // fmla="val N" (there is a space after 'val' in writer) + self::assertSame( + 1, + preg_match( + sprintf('/]+name="adj"[^>]+fmla="val %d"/', $expectedAdj), + (string) $xml + ) + ); + } } diff --git a/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php b/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php index bd5dd4ad6..6c86e7558 100644 --- a/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php +++ b/tests/PhpPresentation/Tests/Writer/ODPresentation/ObjectsChartTest.php @@ -578,7 +578,7 @@ public function testTypeAxisTickLabelPosition(): void public function testTypeAxisUnit(): void { - $value = mt_rand(0, 100); + $value = max(1, mt_rand(0, 100)); $series = new Series('Downloads', $this->seriesData); $line = new Line(); diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php index 663bb6015..282f23f64 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php @@ -739,6 +739,28 @@ public function testParagraphSpacingBefore(): void $this->assertIsSchemaECMA376Valid(); } + public function testRoundRectCornerPixelRadiusStoredAndAffectsHash(): void + { + $width = 200; // px + $height = 100; // px + $px1 = 5; // softer radius + $px2 = 10; // larger radius + + $shape1 = (new AutoShape()) + ->setType(AutoShape::TYPE_ROUNDED_RECTANGLE) + ->setWidth($width) + ->setHeight($height) + ->setRoundRectCorner($px1); + + $shape2 = (clone $shape1)->setRoundRectCorner($px2); + + self::assertSame($px1, $shape1->getRoundRectCorner()); + self::assertSame($px2, $shape2->getRoundRectCorner()); + + // Hash must differ when radius differs + self::assertNotSame($shape1->getHashCode(), $shape2->getHashCode()); + } + public function testPlaceHolder(): void { $expectedType = Placeholder::PH_TYPE_SLIDENUM;