diff --git a/docs/changes/1.2.0.md b/docs/changes/1.2.0.md index 4247f38b8..25cc38199 100644 --- a/docs/changes/1.2.0.md +++ b/docs/changes/1.2.0.md @@ -17,6 +17,7 @@ - `phpoffice/phpspreadsheet`: Allow version 4.0 by [@nreynis](https://github.com/nreynis) in [#861](https://github.com/PHPOffice/PHPPresentation/pull/861) - Smaller package size by [@nreynis](https://github.com/nreynis) in [#862](https://github.com/PHPOffice/PHPPresentation/pull/862) - Added setFirstSliceAngle to Doughnut Chart by [@seanlynchwv](http://github.com/seanlynchwv) in [#872](https://github.com/PHPOffice/PHPPresentation/pull/872) +- 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..a12df299e 100644 --- a/src/PhpPresentation/Shape/AutoShape.php +++ b/src/PhpPresentation/Shape/AutoShape.php @@ -273,4 +273,31 @@ public function setOutline(Outline $outline): self return $this; } + + /** @var null|int */ + private $roundRectAdj; + + public function getRoundRectAdj(): ?int + { + return $this->roundRectAdj; + } + + /** + * Set corner radius. + */ + public function setRoundRectCorner(int $pixels): self + { + $minHalf = (int) floor(min($this->width, $this->height) / 2); + if ($minHalf > 0) { + $this->roundRectAdj = max(0, min(50000, (int) round($pixels / $minHalf * 50000))); + } + + return $this; + } + + // override the hash so radius works + public function getHashCode(): string + { + return md5(parent::getHashCode() . $this->type . $this->text . (string) $this->roundRectAdj . __CLASS__); + } } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php index 2f0544fd7..678209033 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php @@ -1187,9 +1187,21 @@ 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); + $adj = $shape->getRoundRectAdj(); + + if ($needsAdj && $adj !== null) { + $objWriter->startElement('a:avLst'); + $objWriter->startElement('a:gd'); + $objWriter->writeAttribute('name', 'adj'); + $objWriter->writeAttribute('fmla', 'val ' . (string) $adj); // 0..50000 + $objWriter->endElement(); // a:gd + $objWriter->endElement(); // 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..d9c2e173d 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,83 @@ public function testType(): void self::assertInstanceOf(AutoShape::class, $object->setType(AutoShape::TYPE_HEXAGON)); self::assertEquals(AutoShape::TYPE_HEXAGON, $object->getType()); } + + public function testPixelSetterComputesAdjAndAffectsHash(): 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); + + // adj expected: round(px / (min(w,h)/2) * 50000) + $minHalf = (int) floor(min($width, $height) / 2); // 50 + $expectedAdj1 = (int) round($px1 / $minHalf * 50000); // 5/50 * 50000 = 5000 + $expectedAdj2 = (int) round($px2 / $minHalf * 50000); // 10/50 * 50000 = 10000 + + self::assertSame($expectedAdj1, $shape1->getRoundRectAdj()); + self::assertSame($expectedAdj2, $shape2->getRoundRectAdj()); + + // Hash must differ when radius differs + self::assertNotSame($shape1->getHashCode(), $shape2->getHashCode()); + } + + public function testNoRadiusByDefaultIsNull(): void + { + $shape = new AutoShape(); + self::assertNull($shape->getRoundRectAdj()); + } + + 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();