From c6231bbabf341cd1be4c565bbf820c9073d0a13f Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Wed, 13 Dec 2023 15:30:29 +0100 Subject: [PATCH] Feedbacks from @Progi1984 --- docs/changes/1.1.0.md | 13 + docs/usage/presentation.md | 32 +- docs/usage/styles.md | 35 +- samples/Sample_Header.php | 3 +- src/PhpPresentation/AbstractShape.php | 77 +---- src/PhpPresentation/DocumentProperties.php | 35 +- .../Exception/InvalidParameterException.php | 11 +- .../PresentationProperties.php | 45 +-- src/PhpPresentation/Reader/PowerPoint2007.php | 231 ++++++------- src/PhpPresentation/Reader/PowerPoint97.php | 2 +- src/PhpPresentation/Reader/Serialized.php | 13 +- src/PhpPresentation/Shape/AbstractGraphic.php | 31 -- src/PhpPresentation/Shape/Drawing/Gd.php | 4 +- src/PhpPresentation/Shape/Group.php | 31 +- src/PhpPresentation/Shape/RichText.php | 68 ++-- .../Shape/RichText/Paragraph.php | 61 +--- src/PhpPresentation/Shape/RichText/Run.php | 61 +--- .../ShapeContainerInterface.php | 9 +- src/PhpPresentation/Slide.php | 16 +- src/PhpPresentation/Slide/AbstractSlide.php | 60 +--- src/PhpPresentation/Slide/Animation.php | 36 +- src/PhpPresentation/Slide/Note.php | 33 +- src/PhpPresentation/Slide/SlideLayout.php | 2 - src/PhpPresentation/Slide/SlideMaster.php | 2 - src/PhpPresentation/Style/Effect.php | 310 ------------------ src/PhpPresentation/Style/Font.php | 231 ++++++------- src/PhpPresentation/Style/Shadow.php | 37 ++- .../Traits/ShapeCollection.php | 90 +++++ src/PhpPresentation/Writer/AbstractWriter.php | 15 +- .../Writer/PowerPoint2007/AbstractSlide.php | 130 +++----- .../Writer/PowerPoint2007/ContentTypes.php | 2 +- .../Writer/PowerPoint2007/DocPropsCore.php | 11 +- .../PowerPoint2007/DocPropsThumbnail.php | 24 +- .../Writer/PowerPoint2007/PptCharts.php | 60 ++-- .../Writer/PowerPoint2007/PptSlides.php | 81 ++--- .../Writer/PowerPoint2007/Relationships.php | 23 +- src/PhpPresentation/Writer/Serialized.php | 22 +- .../Tests/DocumentPropertiesTest.php | 2 + .../Tests/PresentationPropertiesTest.php | 26 +- .../Tests/Reader/PowerPoint2007Test.php | 2 + .../PhpPresentation/Tests/Shape/GroupTest.php | 4 +- .../Tests/Shape/RichTextTest.php | 2 +- .../Tests/Slide/AbstractSlideTest.php | 34 ++ .../PhpPresentation/Tests/Slide/NoteTest.php | 8 +- .../Tests/Slide/SlideLayoutTest.php | 2 +- .../Tests/Slide/SlideMasterTest.php | 2 +- .../PhpPresentation/Tests/Style/FontTest.php | 78 +++++ .../PowerPoint2007/DocPropsCoreTest.php | 18 +- .../PowerPoint2007/DocPropsThumbnailTest.php | 30 +- .../PowerPoint2007/PptSlideMastersTest.php | 2 +- .../Writer/PowerPoint2007/PptSlidesTest.php | 141 ++++++++ .../_includes/PhpPresentationTestCase.php | 12 + tests/resources/files/serialized.phppt | Bin 14433 -> 14489 bytes 53 files changed, 1019 insertions(+), 1291 deletions(-) delete mode 100644 src/PhpPresentation/Style/Effect.php create mode 100644 src/PhpPresentation/Traits/ShapeCollection.php diff --git a/docs/changes/1.1.0.md b/docs/changes/1.1.0.md index 81ba6ae1e..103d901a0 100644 --- a/docs/changes/1.1.0.md +++ b/docs/changes/1.1.0.md @@ -17,9 +17,22 @@ - PowerPoint2007 Reader - PowerPoint2007 Writer - PowerPoint2007 Writer: Enable style and position of a Placeholder - [@qmachard](https://github.com/qmachard) in [#787](https://github.com/PHPOffice/PHPPresentation/pull/787) +- PowerPoint2007 Reader: Added support for thumbnail - [@devX2712](https://github.com/devX2712) in [#788](https://github.com/PHPOffice/PHPPresentation/pull/787) ## Improvements - Slide : Raised max value for identifier rand call - [@Scheissy](https://github.com/Scheissy) in [#777](https://github.com/PHPOffice/PHPPresentation/pull/777) +- Document Properties : Support for Revision & Status - [@devX2712](https://github.com/devX2712) in [#788](https://github.com/PHPOffice/PHPPresentation/pull/787) + - PowerPoint2007 Reader + - PowerPoint2007 Writer +- Presentation Properties : Added support to define content of the thumbnail - [@devX2712](https://github.com/devX2712) in [#788](https://github.com/PHPOffice/PHPPresentation/pull/787) +- Font : Support for Strikethrough mode - [@devX2712](https://github.com/devX2712) in [#788](https://github.com/PHPOffice/PHPPresentation/pull/787) + - PowerPoint2007 Reader + - PowerPoint2007 Writer +- Font : Support for Pitch Family, Charset & Panose - [@devX2712](https://github.com/devX2712) in [#788](https://github.com/PHPOffice/PHPPresentation/pull/787) + - PowerPoint2007 Reader + - PowerPoint2007 Writer +(@todo Doc) +- Font : Replaced Superscript/Subscript by baseline in PowerPoint2007 Writer - [@devX2712](https://github.com/devX2712) in [#788](https://github.com/PHPOffice/PHPPresentation/pull/787) ## Bugfixes diff --git a/docs/usage/presentation.md b/docs/usage/presentation.md index 03080aa1c..a06e0958b 100644 --- a/docs/usage/presentation.md +++ b/docs/usage/presentation.md @@ -62,6 +62,8 @@ $properties->setCreated(mktime(0, 0, 0, 3, 12, 2014)); $properties->setModified(mktime(0, 0, 0, 3, 14, 2014)); $properties->setSubject('My subject'); $properties->setKeywords('my, key, word'); +$properties->setStatus('Work in Progress'); +$properties->setRevision('Version 1.2.3'); ``` ### Custom Properties @@ -203,16 +205,44 @@ echo $properties->getSlideshowType(); You can define the thumbnail of the presentation with the method `setThumbnailPath`. + +#### From a file ``` php getPresentationProperties(); // Set path of the thumbnail -$properties->setThumbnailPath(__DIR__.'\resources\phppowerpoint_logo.gif'); +$properties->setThumbnailPath( + __DIR__.'\resources\phppowerpoint_logo.gif', + PresentationProperties::THUMBNAIL_FILE +); // Get path of the thumbnail echo $properties->getThumbnailPath(); +// Get content of the thumbnail +echo $properties->getThumbnail(); +``` + +#### From the content of the file +``` php +getPresentationProperties(); +// Set path of the thumbnail +$properties->setThumbnailPath( + '', + PresentationProperties::THUMBNAIL_DATA, + file_get_contents(__DIR__.'\resources\phppowerpoint_logo.gif') +); +// Get content of the thumbnail +echo $properties->getThumbnail(); ``` ### Zoom diff --git a/docs/usage/styles.md b/docs/usage/styles.md index e9ff72c60..a21efdc8e 100644 --- a/docs/usage/styles.md +++ b/docs/usage/styles.md @@ -97,12 +97,24 @@ echo $alignment->isRTL(); - `name` - `bold` - `italic` -- `superScript` -- `subScript` +- `superScript` (deprecated) +- `subScript` (deprecated) - `underline` - `strikethrough` - `color` -- `capitalization` +- `pitchFamily` +- `charset` + +### Baseline + +The baseline set the position relative to the line. +The value is a percentage. + +You can use some predefined values : + +* `Font::BASELINE_SUPERSCRIPT` (= 300000 = 300%) +* `Font::BASELINE_SUBSCRIPT` (= -250000 = -250%) + ### Capitalization @@ -145,6 +157,23 @@ $font->setFormat(Font::FORMAT_EAST_ASIAN); // Get format of font echo $font->getFormat(); ``` + +### Panose +The support of Panose 1.0 is only used. + +``` php +setPanose('4494D72242'); +// Get panose of font +echo $font->getPanose(); +``` + ## Bullet - `bulletType` diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php index 61796fe6f..ea250e96a 100644 --- a/samples/Sample_Header.php +++ b/samples/Sample_Header.php @@ -326,7 +326,7 @@ protected function displayPhpPresentationInfo(PhpPresentation $oPHPPpt): void } } $oNote = $oSlide->getNote(); - if ($oNote->getShapeCollection()->count() > 0) { + if (count($oNote->getShapeCollection()) > 0) { $this->append('
Notes
'); foreach ($oNote->getShapeCollection() as $oShape) { if ($oShape instanceof RichText) { @@ -442,6 +442,7 @@ protected function displayShapeInfo(AbstractShape $oShape): void $this->append('Italic : ' . ($oRichText->getFont()->isItalic() ? 'Y' : 'N') . ' - '); $this->append('Underline : Underline::' . $this->getConstantName('\PhpOffice\PhpPresentation\Style\Font', $oRichText->getFont()->getUnderline()) . ' - '); $this->append('Strikethrough : ' . ($oRichText->getFont()->isStrikethrough() ? 'Y' : 'N') . ' - '); + $this->append('Baseline : ' . $oRichText->getFont()->getBaseline() . ' - '); $this->append('SubScript : ' . ($oRichText->getFont()->isSubScript() ? 'Y' : 'N') . ' - '); $this->append('SuperScript : ' . ($oRichText->getFont()->isSuperScript() ? 'Y' : 'N')); $this->append(''); diff --git a/src/PhpPresentation/AbstractShape.php b/src/PhpPresentation/AbstractShape.php index 176247149..c68fa5cb3 100644 --- a/src/PhpPresentation/AbstractShape.php +++ b/src/PhpPresentation/AbstractShape.php @@ -20,6 +20,7 @@ namespace PhpOffice\PhpPresentation; use PhpOffice\PhpPresentation\Exception\ShapeContainerAlreadyAssignedException; +use PhpOffice\PhpPresentation\Shape\Group; use PhpOffice\PhpPresentation\Shape\Hyperlink; use PhpOffice\PhpPresentation\Shape\Placeholder; use PhpOffice\PhpPresentation\Style\Border; @@ -102,12 +103,6 @@ abstract class AbstractShape implements ComparableInterface */ protected $placeholder; - /** - * List of effect apply to shape - * @var array \PhpOffice\PhpPresentation\Style\Effect[] - */ - protected ?array $effectCollection = null; - /** * Hash index * @@ -131,7 +126,6 @@ public function __construct() $this->fill = new Fill(); $this->shadow = new Shadow(); $this->border = new Border(); - $this->effectCollection = null; $this->border->setLineStyle(Style\Border::LINE_NONE); } @@ -143,12 +137,10 @@ public function __clone() { $this->container = null; $this->name = $this->name; + $this->border = clone $this->border; if (isset($this->fill)) { $this->fill = clone $this->fill; } - if (isset($this->border)) { - $this->border = clone $this->border; - } if (isset($this->shadow)) { $this->shadow = clone $this->shadow; } @@ -158,11 +150,6 @@ public function __clone() if (isset($this->hyperlink)) { $this->hyperlink = clone $this->hyperlink; } - // Clone each effect - if (isset($this->effectCollection)) { - foreach ($this->effectCollection as &$effect) { - $effect = clone $effect; - }} } /** @@ -186,21 +173,18 @@ public function setContainer(?ShapeContainerInterface $pValue = null, $pOverride // Add drawing to ShapeContainerInterface $this->container = $pValue; if (null !== $this->container) { - $this->container->getShapeCollection()->append($this); + $this->container->addShape($this); } } else { if ($pOverrideOld) { // Remove drawing from old ShapeContainerInterface - $iterator = $this->container->getShapeCollection()->getIterator(); - - while ($iterator->valid()) { - if ($iterator->current()->getHashCode() == $this->getHashCode()) { - $this->container->getShapeCollection()->offsetUnset($iterator->key()); + foreach ($this->container->getShapeCollection() as $key => $shape) { + if ($shape->getHashCode() == $this->getHashCode()) { + $this->container->unsetShape($key); $this->container = null; break; } - $iterator->next(); } // Set new \PhpOffice\PhpPresentation\Slide @@ -215,21 +199,16 @@ public function setContainer(?ShapeContainerInterface $pValue = null, $pOverride /** * Get Name - * - * @return string */ - public function getName() + public function getName(): string { return $this->name; } /** * Set Name - * - * @param string $pValue - * @return \PhpOffice\PhpPresentation\Shape\AbstractGraphic */ - public function setName($pValue = '') + public function setName(string $pValue = ''): static { $this->name = $pValue; return $this; @@ -423,46 +402,6 @@ public function setHyperlink(?Hyperlink $pHyperlink = null): self return $this; } - - /** - * Add an effect to the shpae - * - * @param \PhpOffice\PhpPresentation\Style\Effect $effect - * @return $this - */ - public function addEffect(Shape\Effect $effect) - { - if (!isset($this->effectCollection)) { - $this->effectCollection = array(); - } - $this->effectCollection[] = $effect; - return $this; - } - - /** - * Get the effect collection - * - * @return array \PhpOffice\PhpPresentation\Style\Effect[] - */ - public function getEffectCollection():?array - { - return $this->effectCollection; - } - - /** - * Set the effect collection - * - * @param array \PhpOffice\PhpPresentation\Style\Effect $effectCollection - * @return $this - */ - public function setEffectCollection(array $effectCollection) - { - if ( isset($effectCollection) - && is_array($effectCollection)) { - $this->effectCollection = $effectCollection; - } - return $this; - } /** * Get hash code. diff --git a/src/PhpPresentation/DocumentProperties.php b/src/PhpPresentation/DocumentProperties.php index c6a24e453..96d545b84 100644 --- a/src/PhpPresentation/DocumentProperties.php +++ b/src/PhpPresentation/DocumentProperties.php @@ -102,11 +102,18 @@ class DocumentProperties private $company; /** - * revision + * Revision. * * @var string */ private $revision; + + /** + * Status. + * + * @var string + */ + private $status; /** * Custom Properties. @@ -116,14 +123,7 @@ class DocumentProperties private $customProperties = []; /** - * status - * - * @var string - */ - private $status; - - /** - * Create a new \PhpOffice\PhpPresentation\DocumentProperties + * Create a new \PhpOffice\PhpPresentation\DocumentProperties. */ public function __construct() { @@ -139,7 +139,7 @@ public function __construct() $this->category = ''; $this->company = 'Microsoft Corporation'; $this->revision = ''; - $this->status = ''; + $this->status = ''; } /** @@ -479,7 +479,7 @@ public function getRevision(): string } /** - * Set Revision + * Set Revision. */ public function setRevision(string $pValue = ''): self { @@ -489,22 +489,17 @@ public function setRevision(string $pValue = ''): self } /** - * Get Status - * - * @return string + * Get Status. */ - public function getStatus() + public function getStatus(): string { return $this->status; } /** - * Set Status - * - * @param string $pValue - * @return \PhpOffice\PhpPresentation\DocumentProperties + * Set Status. */ - public function setStatus($pValue = '') + public function setStatus(string $pValue = ''): self { $this->status = $pValue; diff --git a/src/PhpPresentation/Exception/InvalidParameterException.php b/src/PhpPresentation/Exception/InvalidParameterException.php index 961809623..502705e67 100644 --- a/src/PhpPresentation/Exception/InvalidParameterException.php +++ b/src/PhpPresentation/Exception/InvalidParameterException.php @@ -21,12 +21,17 @@ class InvalidParameterException extends PhpPresentationException { - public function __construct(string $parameter, string $value) + public function __construct(string $parameter, string $value, string $error = null) { - parent::__construct(sprintf( + $message = sprintf( 'The parameter %s can\'t have the value "%s"', $parameter, $value - )); + ); + if ($error) { + $message = sprintf('%s (Validation: %s)', $message, $error); + } + + parent::__construct($message); } } diff --git a/src/PhpPresentation/PresentationProperties.php b/src/PhpPresentation/PresentationProperties.php index 63eca8fb5..ff2cc5aaa 100644 --- a/src/PhpPresentation/PresentationProperties.php +++ b/src/PhpPresentation/PresentationProperties.php @@ -30,8 +30,8 @@ class PresentationProperties public const VIEW_SLIDE_SORTER = 'sldSorterView'; public const VIEW_SLIDE_THUMBNAIL = 'sldThumbnailView'; - public const THUMBNAIL_FILE = 'file'; // Thumbnail path is out of PPT - public const THUMBNAIL_ZIP = 'zip'; // Thumbnail path point to an image store into file loaded + public const THUMBNAIL_FILE = 'file'; + public const THUMBNAIL_DATA = 'data'; /** * @var array @@ -72,17 +72,17 @@ class PresentationProperties */ protected $markAsFinal = false; - /* - * @var string Define the thumbnail content (if content into zip file) + /** + * @var string|null Define the thumbnail content (if content into zip file) */ protected $thumbnail = null; - /* - * @var string Define the thumbnail place + /** + * @var string|null Define the thumbnail place */ - protected $thumbnailPath = ''; + protected $thumbnailPath = null; - /* + /** * @var string Define if thumbnail is out of PPT or previouly store into PPT */ protected $thumbnailType = self::THUMBNAIL_FILE; @@ -130,36 +130,38 @@ public function getThumbnailPath(): ?string } /** - * Return the content of thumbnail - * - * @return binary Content of image + * Return the content of thumbnail. */ - public function getThumbnail() + public function getThumbnail(): ?string { // Return content of local file if ($this->getThumbnailType() == self::THUMBNAIL_FILE) { - if (file_exists($this->getThumbnailPath())) + if ($this->getThumbnailPath()) { return file_get_contents($this->getThumbnailPath()); + } + + return null; } + // Return content of image stored into zip file - if ($this->getThumbnailType() == self::THUMBNAIL_ZIP) { + if ($this->getThumbnailType() == self::THUMBNAIL_DATA) { return $this->thumbnail; } - // Return null if no thumbnail + return null; } /** * Define the path for the thumbnail file / preview picture. */ - public function setThumbnailPath(string $path = '', $type = self::THUMBNAIL_FILE, $content = null) + public function setThumbnailPath(string $path = '', string $type = self::THUMBNAIL_FILE, string $content = null): self { - if (file_exists($path) && ($type == self::THUMBNAIL_FILE)) { + if (file_exists($path) && $type == self::THUMBNAIL_FILE) { $this->thumbnailPath = $path; $this->thumbnailType = $type; } - if (($path != '') && ($type == self::THUMBNAIL_ZIP)) { - $this->thumbnailPath = $path; + if ($content != '' && $type == self::THUMBNAIL_DATA) { + $this->thumbnailPath = ''; $this->thumbnailType = $type; $this->thumbnail = $content; } @@ -168,10 +170,9 @@ public function setThumbnailPath(string $path = '', $type = self::THUMBNAIL_FILE } /** - * Return the thumbnail type - * @return string + * Return the thumbnail type. */ - public function getThumbnailType() + public function getThumbnailType(): string { return $this->thumbnailType; } diff --git a/src/PhpPresentation/Reader/PowerPoint2007.php b/src/PhpPresentation/Reader/PowerPoint2007.php index e619a2607..eeeb6cdb9 100644 --- a/src/PhpPresentation/Reader/PowerPoint2007.php +++ b/src/PhpPresentation/Reader/PowerPoint2007.php @@ -51,6 +51,7 @@ use PhpOffice\PhpPresentation\Style\Fill; use PhpOffice\PhpPresentation\Style\Font; use PhpOffice\PhpPresentation\Style\SchemeColor; +use PhpOffice\PhpPresentation\Style\Shadow; use PhpOffice\PhpPresentation\Style\TextStyle; use ZipArchive; @@ -230,7 +231,7 @@ protected function loadDocumentProperties(string $sPart): void '/cp:coreProperties/dcterms:modified' => 'setModified', '/cp:coreProperties/cp:revision' => 'setRevision', '/cp:coreProperties/cp:contentStatus' => 'setStatus', - ); + ]; $oProperties = $this->oPhpPresentation->getDocumentProperties(); foreach ($arrayProperties as $path => $property) { $oElement = $xmlReader->getElement($path); @@ -248,27 +249,23 @@ protected function loadDocumentProperties(string $sPart): void /** * Read information of the document thumbnail - * @param string $sPart Content of XML file for retrieving data */ - protected function loadThumbnailProperties($sPart) + protected function loadThumbnailProperties(string $sPart): void { $xmlReader = new XMLReader(); - if ($xmlReader->getDomFromString($sPart)) { - $oElement = $xmlReader->getElement('*[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]'); - if ($oElement instanceof \DOMElement) { + $xmlReader->getDomFromString($sPart); + + $oElement = $xmlReader->getElement('*[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]'); + if ($oElement instanceof \DOMElement) { $path = $oElement->getAttribute('Target'); $this->oPhpPresentation - ->getPresentationProperties() - ->setThumbnailPath($path - , \PhpOffice\PhpPresentation\PresentationProperties::THUMBNAIL_ZIP - , $this->oZip->getFromName($path)); - } + ->getPresentationProperties() + ->setThumbnailPath('', PresentationProperties::THUMBNAIL_DATA, $this->oZip->getFromName($path)); } } /** - * Read Custom Properties - * @param string $sPart + * Read Custom Properties. */ protected function loadCustomProperties(string $sPart): void { @@ -878,79 +875,61 @@ protected function loadShapeDrawing(XMLReader $document, DOMElement $node, Abstr } } // Load shape effects - $oEffect = $document->getElement('p:spPr/a:effectLst', $node); - if ($oEffect instanceof \DOMElement) { - $aEffect = $this->loadEffect($document, $oEffect); - if (isset($aEffect) && is_array($aEffect)) { - $oShape->setEffectCollection($aEffect); - } + $oElement = $document->getElement('p:spPr/a:effectLst', $node); + if ($oElement instanceof DOMElement) { + $oShape->setShadow( + $this->loadShadow($document, $oElement) + ); } $oSlide->addShape($oShape); } /** - * Load Effect for shape or paragraph - * - * @param XMLReader $document - * @param \DOMElement $node - * @return array \PhpOffice\PhpPresentation\Style\Effect[] + * Load Shadow for shape or paragraph. */ - protected function loadEffect(XMLReader $document, \DOMElement $nodeEffect) + protected function loadShadow(XMLReader $document, DOMElement $node): ?Shadow { - $aEffect = null; - if ($nodeEffect instanceof \DOMElement) { - - $aNodes = $document->getElements('*', $nodeEffect); - foreach ($aNodes as $node) { - - $type = explode(':', $node->tagName); - $type = array_pop($type); - if ( $type == 'outerShdw' - || $type == 'innerShdw') { -// @TODO || $type == 'reflection') { - - // Create a new effect - $effect = new \PhpOffice\PhpPresentation\Style\Effect($type); - // load blur radius - if ($node->hasAttribute('blurRad')) { - $effect->setBlurRadius(CommonDrawing::emuToPixels($node->getAttribute('blurRad'))); - } - // load distance - if ($node->hasAttribute('dist')) { - $effect->setDistance(CommonDrawing::emuToPixels($node->getAttribute('dist'))); - } - // load direction - if ($node->hasAttribute('dir')) { - $effect->setDirection(CommonDrawing::angleToDegrees($node->getAttribute('dir'))); - } - // load alignment - if ($node->hasAttribute('algn')) { - $effect->setAlignment($node->getAttribute('algn')); - } - - // Get color define by prstClr - $oSubElement = $document->getElement('a:prstClr', $node); - if ($oSubElement instanceof \DOMElement && $oSubElement->hasAttribute('val')) { - $oColor = new Color(); - $oColor->setRGB($oSubElement->getAttribute('val')); - $effect->setColor($oColor); - // Get Alpha - $oSubElt = $document->getElement('a:alpha', $oSubElement); - if ($oSubElt instanceof \DOMElement && $oSubElt->hasAttribute('val')) { - $effect->setAlpha((int)$oSubElt->getAttribute('val') / 1000); - } - } - // Load reflection atributs -// @TODO future implementation - if ($node->tagName == 'a:reflection') { - } - - if (!isset($aEffect)) $aEffect = array(); - $aEffect[] = $effect; - } - } - } - return $aEffect; + if ($node instanceof DOMElement) { + $aNodes = $document->getElements('*', $node); + foreach ($aNodes as $nodeShadow) { + $type = explode(':', $nodeShadow->tagName); + $type = array_pop($type); + if ($type == Shadow::TYPE_SHADOW_INNER || $type == Shadow::TYPE_SHADOW_OUTER || $type == Shadow::TYPE_REFLECTION) { + $oShadow = new Shadow(); + $oShadow->setType($type); + if ($nodeShadow->hasAttribute('blurRad')) { + $oShadow->setBlurRadius(CommonDrawing::emuToPixels((int) $nodeShadow->getAttribute('blurRad'))); + } + if ($nodeShadow->hasAttribute('dist')) { + $oShadow->setDistance(CommonDrawing::emuToPixels((int) $nodeShadow->getAttribute('dist'))); + } + if ($nodeShadow->hasAttribute('dir')) { + $oShadow->setDirection((int) CommonDrawing::angleToDegrees((int) $nodeShadow->getAttribute('dir'))); + } + if ($nodeShadow->hasAttribute('algn')) { + $oShadow->setAlignment($node->getAttribute('algn')); + } + + // Get color define by prstClr + $oSubElement = $document->getElement('a:prstClr', $nodeShadow); + if ($oSubElement instanceof \DOMElement && $oSubElement->hasAttribute('val')) { + $oColor = new Color(); + $oColor->setRGB($oSubElement->getAttribute('val')); + + $oSubElt = $document->getElement('a:alpha', $oSubElement); + if ($oSubElt instanceof \DOMElement && $oSubElt->hasAttribute('val')) { + $oColor->setAlpha((int)$oSubElt->getAttribute('val') / 1000); + } + + $oShadow->setColor($oColor); + } + + return $oShadow; + } + } + } + + return null; } /** @@ -1004,33 +983,34 @@ protected function loadShapeRichText(XMLReader $document, DOMElement $node, $oSl } // Load shape effects - $oEffect = $document->getElement('p:spPr/a:effectLst', $node); - if ($oEffect instanceof \DOMElement) { - $aEffect = $this->loadEffect($document, $oEffect); - if (isset($aEffect) && is_array($aEffect)) { - $oShape->setEffectCollection($aEffect); - } + $oElement = $document->getElement('p:spPr/a:effectLst', $node); + if ($oElement instanceof DOMElement) { + $oShape->setShadow( + $this->loadShadow($document, $oElement) + ); } -// FBU-20210202+ Read body definitions + // FBU-20210202+ Read body definitions $bodyPr = $document->getElement('p:txBody/a:bodyPr', $node); - if (($bodyPr instanceof \DOMElement) && $bodyPr->hasAttribute('lIns')) { - $oShape->setInsetLeft((int)$bodyPr->getAttribute('lIns')); - } - if (($bodyPr instanceof \DOMElement) && $bodyPr->hasAttribute('tIns')) { - $oShape->setInsetTop((int)$bodyPr->getAttribute('tIns')); - } - if (($bodyPr instanceof \DOMElement) && $bodyPr->hasAttribute('rIns')) { - $oShape->setInsetRight((int)$bodyPr->getAttribute('rIns')); - } - if (($bodyPr instanceof \DOMElement) && $bodyPr->hasAttribute('bIns')) { - $oShape->setInsetBottom((int)$bodyPr->getAttribute('bIns')); - } - if (($bodyPr instanceof \DOMElement) && $bodyPr->hasAttribute('anchor')) { - $oShape->setVerticalAlignment($bodyPr->getAttribute('anchor')); - } - if (($bodyPr instanceof \DOMElement) && $bodyPr->hasAttribute('anchorCtr')) { - $oShape->setVerticalAlignCenter((int)$bodyPr->getAttribute('anchorCtr')); + if ($bodyPr instanceof DOMElement) { + if ($bodyPr->hasAttribute('lIns')) { + $oShape->setInsetLeft((int)$bodyPr->getAttribute('lIns')); + } + if ($bodyPr->hasAttribute('tIns')) { + $oShape->setInsetTop((int)$bodyPr->getAttribute('tIns')); + } + if ($bodyPr->hasAttribute('rIns')) { + $oShape->setInsetRight((int)$bodyPr->getAttribute('rIns')); + } + if ($bodyPr->hasAttribute('bIns')) { + $oShape->setInsetBottom((int)$bodyPr->getAttribute('bIns')); + } + if ($bodyPr->hasAttribute('anchor')) { + $oShape->setVerticalAlignment($bodyPr->getAttribute('anchor')); + } + if ($bodyPr->hasAttribute('anchorCtr')) { + $oShape->setVerticalAlignCenter((int)$bodyPr->getAttribute('anchorCtr')); + } } $arrayElements = $document->getElements('p:txBody/a:p', $node); @@ -1042,8 +1022,9 @@ protected function loadShapeRichText(XMLReader $document, DOMElement $node, $oSl $oElement = $document->getElement('p:spPr', $node); if ($oElement instanceof \DOMElement) { - $oFill = $this->loadStyleFill($document, $oElement); - $oShape->setFill($oFill); + $oShape->setFill( + $this->loadStyleFill($document, $oElement) + ); } if (count($oShape->getParagraphs()) > 0) { @@ -1316,11 +1297,7 @@ protected function loadParagraph(XMLReader $document, DOMElement $oElement, $oSh $oText->setLanguage($oElementrPr->getAttribute('lang')); } if ($oElementrPr->hasAttribute('baseline')) { - if ((int)$oElementrPr->getAttribute('baseline')>0) { - $oText->getFont()->setSuperScript((int)$oElementrPr->getAttribute('baseline')); - } else if ((int)$oElementrPr->getAttribute('baseline')<0) { - $oText->getFont()->setSubScript((int)$oElementrPr->getAttribute('baseline')); - } + $oText->getFont()->setBaseline((int) $oElementrPr->getAttribute('baseline')); } // Color $oElementSrgbClr = $document->getElement('a:solidFill/a:srgbClr', $oElementrPr); @@ -1358,28 +1335,20 @@ protected function loadParagraph(XMLReader $document, DOMElement $oElement, $oSh } // Font definition $oElementFont = $document->getElement('a:latin', $oElementrPr); - if (is_object($oElementFont) && $oElementFont->hasAttribute('typeface')) { - $oText->getFont()->setName($oElementFont->getAttribute('typeface')); - } - if (($oElementFont instanceof \DOMElement) && $oElementFont->hasAttribute('panose')) { - $oText->getFont()->setPanose($oElementFont->getAttribute('panose')); - } - if (($oElementFont instanceof \DOMElement) && $oElementFont->hasAttribute('pitchFamily')) { - $oText->getFont()->setPitchFamily($oElementFont->getAttribute('pitchFamily')); - } - if (($oElementFont instanceof \DOMElement) && $oElementFont->hasAttribute('charset')) { - $oText->getFont()->setCharset($oElementFont->getAttribute('charset')); - } - // Load shape effects - $oEffect = $document->getElement('a:effectLst', $oElementrPr); - if ($oEffect instanceof \DOMElement) { - $aEffect = $this->loadEffect($document, $oEffect); - if (isset($aEffect) && is_array($aEffect)) { - $oText->setEffectCollection($aEffect); - } + if ($oElementFont instanceof \DOMElement) { + if ($oElementFont->hasAttribute('typeface')) { + $oText->getFont()->setName($oElementFont->getAttribute('typeface')); + } + if ($oElementFont->hasAttribute('panose')) { + $oText->getFont()->setPanose($oElementFont->getAttribute('panose')); + } + if ($oElementFont->hasAttribute('pitchFamily')) { + $oText->getFont()->setPitchFamily((int) $oElementFont->getAttribute('pitchFamily')); + } + if ($oElementFont->hasAttribute('charset')) { + $oText->getFont()->setCharset((int) $oElementFont->getAttribute('charset')); + } } - //} else { - // $oText = $oParagraph->createText(); $oSubSubElement = $document->getElement('a:t', $oSubElement); $oText->setText($oSubSubElement->nodeValue); diff --git a/src/PhpPresentation/Reader/PowerPoint97.php b/src/PhpPresentation/Reader/PowerPoint97.php index 6189edb08..4ef8fc91c 100644 --- a/src/PhpPresentation/Reader/PowerPoint97.php +++ b/src/PhpPresentation/Reader/PowerPoint97.php @@ -1796,7 +1796,7 @@ private function readRecordOfficeArtSpgrContainer($stream, $pos, $bInGroup = fal $arrayIdxSlide = array_flip($this->arrayNotes); if ($this->currentNote > 0 && isset($arrayIdxSlide[$this->currentNote])) { $oSlide = $this->oPhpPresentation->getSlide($arrayIdxSlide[$this->currentNote]); - if (0 == $oSlide->getNote()->getShapeCollection()->count()) { + if (0 == count($oSlide->getNote()->getShapeCollection())) { $oSlide->getNote()->addShape($fileBlock['shape']); } } diff --git a/src/PhpPresentation/Reader/Serialized.php b/src/PhpPresentation/Reader/Serialized.php index 208c28325..e47a3939c 100644 --- a/src/PhpPresentation/Reader/Serialized.php +++ b/src/PhpPresentation/Reader/Serialized.php @@ -92,14 +92,13 @@ private function loadSerialized(string $pFilename): PhpPresentation // Update media links for ($i = 0; $i < $file->getSlideCount(); ++$i) { - for ($j = 0; $j < $file->getSlide($i)->getShapeCollection()->count(); ++$j) { - if ($file->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) { - $imgTemp = $file->getSlide($i)->getShapeCollection()->offsetGet($j); - $imgPath = 'zip://' . $pFilename . '#media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME); - if ($imgTemp instanceof DrawingFile) { - $imgTemp->setPath($imgPath, false); + foreach($file->getSlide($i)->getShapeCollection() as $shape) { + if ($shape instanceof AbstractDrawingAdapter) { + $imgPath = 'zip://' . $pFilename . '#media/' . $shape->getImageIndex() . '/' . pathinfo($shape->getPath(), PATHINFO_BASENAME); + if ($shape instanceof DrawingFile) { + $shape->setPath($imgPath, false); } else { - $imgTemp->setPath($imgPath); + $shape->setPath($imgPath); } } } diff --git a/src/PhpPresentation/Shape/AbstractGraphic.php b/src/PhpPresentation/Shape/AbstractGraphic.php index f8b464f51..064e32a9e 100644 --- a/src/PhpPresentation/Shape/AbstractGraphic.php +++ b/src/PhpPresentation/Shape/AbstractGraphic.php @@ -41,13 +41,6 @@ abstract class AbstractGraphic extends AbstractShape implements ComparableInterf */ private $imageIndex = 0; - /** - * Name. - * - * @var string - */ - protected $name; - /** * Description. * @@ -105,30 +98,6 @@ public function getImageIndex() return $this->imageIndex; } - /** - * Get Name. - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Set Name. - * - * @param string $pValue - * - * @return $this - */ - public function setName($pValue = '') - { - $this->name = $pValue; - - return $this; - } - /** * Get Description. * diff --git a/src/PhpPresentation/Shape/Drawing/Gd.php b/src/PhpPresentation/Shape/Drawing/Gd.php index 690966608..16fa6fe16 100644 --- a/src/PhpPresentation/Shape/Drawing/Gd.php +++ b/src/PhpPresentation/Shape/Drawing/Gd.php @@ -93,8 +93,8 @@ public function setImageResource($value = null) if (null !== $this->imageResource) { // Get width/height - $this->width = (int)@imagesx($this->imageResource); - $this->height = (int)@imagesy($this->imageResource); + $this->width = imagesx($this->imageResource); + $this->height = imagesy($this->imageResource); } return $this; diff --git a/src/PhpPresentation/Shape/Group.php b/src/PhpPresentation/Shape/Group.php index 4945f1561..17a424297 100644 --- a/src/PhpPresentation/Shape/Group.php +++ b/src/PhpPresentation/Shape/Group.php @@ -23,15 +23,11 @@ use PhpOffice\PhpPresentation\AbstractShape; use PhpOffice\PhpPresentation\GeometryCalculator; use PhpOffice\PhpPresentation\ShapeContainerInterface; +use PhpOffice\PhpPresentation\Traits\ShapeCollection; class Group extends AbstractShape implements ShapeContainerInterface { - /** - * Collection of shapes. - * - * @var array|ArrayObject - */ - private $shapeCollection; + use ShapeCollection; /** * Extent X. @@ -50,29 +46,6 @@ class Group extends AbstractShape implements ShapeContainerInterface public function __construct() { parent::__construct(); - - // Shape collection - $this->shapeCollection = new ArrayObject(); - } - - /** - * Get collection of shapes. - * - * @return array|ArrayObject - */ - public function getShapeCollection() - { - return $this->shapeCollection; - } - - /** - * Add shape to slide. - */ - public function addShape(AbstractShape $shape): AbstractShape - { - $shape->setContainer($this); - - return $shape; } /** diff --git a/src/PhpPresentation/Shape/RichText.php b/src/PhpPresentation/Shape/RichText.php index 86f75c924..31eeec222 100644 --- a/src/PhpPresentation/Shape/RichText.php +++ b/src/PhpPresentation/Shape/RichText.php @@ -21,6 +21,7 @@ use PhpOffice\PhpPresentation\AbstractShape; use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Exception\NotAllowedValueException; use PhpOffice\PhpPresentation\Exception\OutOfBoundsException; use PhpOffice\PhpPresentation\Shape\RichText\Paragraph; use PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface; @@ -45,20 +46,20 @@ class RichText extends AbstractShape implements ComparableInterface public const OVERFLOW_OVERFLOW = 'overflow'; /** Vertical alignment */ - const VALIGN_TOP = 't'; - const VALIGN_MIDDLE = 'ctr'; - const VALIGN_BOTTOM = 'b'; + public const VALIGN_TOP = 't'; + public const VALIGN_MIDDLE = 'ctr'; + public const VALIGN_BOTTOM = 'b'; /** Vertical alignment center */ - const VALIGN_CENTER = 1; - const VALIGN_NOTCENTER = 0; + public const VALIGN_CENTER = 1; + public const VALIGN_NOTCENTER = 0; /** * Rich text paragraphs. * * @var array */ - private $richTextParagraphs; + private $richTextParagraphs = []; /** * Active paragraph. @@ -184,6 +185,7 @@ class RichText extends AbstractShape implements ComparableInterface * @var string */ private $verticalAlign = self::VALIGN_TOP; + /** * Define vertical text center position into shape (center,not center) * @var int @@ -212,10 +214,9 @@ public function __clone() // Call perent clonage for heritage parent::__clone(); // Clone each paragraph - if (isset($this->richTextParagraphs)) { - foreach ($this->richTextParagraphs as &$paragraph) { + foreach ($this->richTextParagraphs as &$paragraph) { $paragraph = clone $paragraph; - }} + } } /** @@ -510,52 +511,53 @@ public function setVertical(bool $value = false): self /** * Define the vertical alignment * - * @param string|null $value top,center,bottom - * @return $this - * @see self::VALIGN_TOP, self::VALIGN_MIDLE, self::VALIGN_BOTTOM + * @param string $value top,center,bottom */ - public function setVerticalAlignment(?string $value) + public function setVerticalAlignment(string $value): self { - if (isset($value)) { + if (!in_array( + $value, + [self::VALIGN_TOP, self::VALIGN_MIDDLE, self::VALIGN_BOTTOM] + )) { + throw new NotAllowedValueException($value, [self::VALIGN_TOP, self::VALIGN_MIDDLE, self::VALIGN_BOTTOM]); + } + $this->verticalAlign = $value; - } else { - $this->verticalAlign = self::VALIGN_TOP; - } - return $this; + + return $this; } /** * Get the vertical alignment - * - * @return string - * @see self::VALIGN_TOP, self::VALIGN_MIDLE, self::VALIGN_BOTTOM */ - public function getVerticalAlignment():string + public function getVerticalAlignment(): string { - return $this->verticalAlign; + return $this->verticalAlign; } /** * Define the vertical alignment if centered or not - * @param int|null $value 1=center 0=not center - * @return $this + * @param int $value 1=center 0=not * @see self::VALIGN_CENTER, self::VALIGN_NOTCENTER */ - public function setVerticalAlignCenter(?int $value) + public function setVerticalAlignCenter(int $value): self { - if (isset($value)) { + if (!in_array( + $value, + [self::VALIGN_CENTER, self::VALIGN_NOTCENTER] + )) { + throw new NotAllowedValueException((string) $value, [(string) self::VALIGN_CENTER, (string) self::VALIGN_NOTCENTER]); + } + $this->verticalAlignCenter = $value; - } else { - $this->verticalAlignCenter = self::VALIGN_NOTCENTER; - } + return $this; } /** * Get the vertical alignment center - * @return int */ - public function getVerticalAlignCenter():int + public function getVerticalAlignCenter(): int { return $this->verticalAlignCenter; } @@ -730,6 +732,8 @@ public function getHashCode(): string . $this->leftInset . $this->rightInset . $this->topInset + . $this->verticalAlign + . $this->verticalAlignCenter . parent::getHashCode() . __CLASS__ ); diff --git a/src/PhpPresentation/Shape/RichText/Paragraph.php b/src/PhpPresentation/Shape/RichText/Paragraph.php index 95c9004ce..15e2cf2f0 100644 --- a/src/PhpPresentation/Shape/RichText/Paragraph.php +++ b/src/PhpPresentation/Shape/RichText/Paragraph.php @@ -65,12 +65,6 @@ class Paragraph implements ComparableInterface */ private $lineSpacing = 100; - /** - * List of effect apply to paragraph - * @var array \PhpOffice\PhpPresentation\Style\Effect[] - */ - protected ?array $effectCollection = null; - /** * Hash index * @@ -103,7 +97,6 @@ public function __construct() $this->alignment = new Alignment(); $this->font = new Font(); $this->bulletStyle = new Bullet(); - $this->effectCollection = null; } /** @@ -111,16 +104,10 @@ public function __construct() */ public function __clone() { - // Clone each text - if (isset($this->richTextElements)) { - foreach ($this->richTextElements as &$txtElt) { - $txtElt = clone $txtElt; - }} - // Clone each effect - if (isset($this->effectCollection)) { - foreach ($this->effectCollection as &$effect) { - $effect = clone $effect; - }} + // Clone each text + foreach ($this->richTextElements as &$rtElement) { + $rtElement = clone $rtElement; + } } /** @@ -282,46 +269,6 @@ public function setRichTextElements(array $pElements = []): self return $this; } - /** - * Add an effect to the shpae - * - * @param \PhpOffice\PhpPresentation\Style\Effect $effect - * @return $this - */ - public function addEffect(Shape\Effect $effect) - { - if (!isset($this->effectCollection)) { - $this->effectCollection = array(); - } - $this->effectCollection[] = $effect; - return $this; - } - - /** - * Get the effect collection - * - * @return array \PhpOffice\PhpPresentation\Style\Effect[] - */ - public function getEffectCollection():?array - { - return $this->effectCollection; - } - - /** - * Set the effect collection - * - * @param array \PhpOffice\PhpPresentation\Style\Effect $effectCollection - * @return $this - */ - public function setEffectCollection(array $effectCollection) - { - if ( isset($effectCollection) - && is_array($effectCollection)) { - $this->effectCollection = $effectCollection; - } - return $this; - } - /** * Get hash code * diff --git a/src/PhpPresentation/Shape/RichText/Run.php b/src/PhpPresentation/Shape/RichText/Run.php index e4528acb6..a9f99fe3e 100644 --- a/src/PhpPresentation/Shape/RichText/Run.php +++ b/src/PhpPresentation/Shape/RichText/Run.php @@ -29,27 +29,18 @@ class Run extends TextElement implements TextElementInterface /** * Font. * - * @var \PhpOffice\PhpPresentation\Style\Font + * @var Font */ private $font; /** - * List of effect apply to paragraph - * @var array \PhpOffice\PhpPresentation\Style\Effect[] - */ - protected ?array $effectCollection = null; - - /** - * Create a new \PhpOffice\PhpPresentation\Shape\RichText\Run instance - * * @param string $pText Text */ public function __construct($pText = '') { - // Initialise variables + // Initialize variables $this->setText($pText); $this->font = new Font(); - $this->effectCollection = null; } /** @@ -63,9 +54,7 @@ public function getFont(): Font /** * Set font. * - * @param null|Font $pFont Font - * - * @return \PhpOffice\PhpPresentation\Shape\RichText\TextElementInterface + * @return self */ public function setFont(?Font $pFont = null) { @@ -75,49 +64,7 @@ public function setFont(?Font $pFont = null) } /** - * Add an effect to the shpae - * - * @param \PhpOffice\PhpPresentation\Style\Effect $effect - * @return $this - */ - public function addEffect(Shape\Effect $effect) - { - if (!isset($this->effectCollection)) { - $this->effectCollection = array(); - } - $this->effectCollection[] = $effect; - return $this; - } - - /** - * Get the effect collection - * - * @return array \PhpOffice\PhpPresentation\Style\Effect[] - */ - public function getEffectCollection():?array - { - return $this->effectCollection; - } - - /** - * Set the effect collection - * - * @param array \PhpOffice\PhpPresentation\Style\Effect $effectCollection - * @return $this - */ - public function setEffectCollection(array $effectCollection) - { - if ( isset($effectCollection) - && is_array($effectCollection)) { - $this->effectCollection = $effectCollection; - } - return $this; - } - - /** - * Get hash code - * - * @return string Hash code + * Get hash code. */ public function getHashCode(): string { diff --git a/src/PhpPresentation/ShapeContainerInterface.php b/src/PhpPresentation/ShapeContainerInterface.php index a347c358e..52186b88d 100644 --- a/src/PhpPresentation/ShapeContainerInterface.php +++ b/src/PhpPresentation/ShapeContainerInterface.php @@ -35,10 +35,13 @@ public function getShapeCollection(); /** * Add shape to slide. - * - * @return AbstractShape */ - public function addShape(AbstractShape $shape); + public function addShape(AbstractShape $shape): self; + + /** + * Unset shape from the collection. + */ + public function unsetShape(int $key): self; /** * Get X Offset. diff --git a/src/PhpPresentation/Slide.php b/src/PhpPresentation/Slide.php index 150e14426..10d03ca8b 100644 --- a/src/PhpPresentation/Slide.php +++ b/src/PhpPresentation/Slide.php @@ -74,8 +74,6 @@ public function __construct(?PhpPresentation $pParent = null) { // Set parent $this->parent = $pParent; - // Shape collection - $this->shapeCollection = new ArrayObject(); // Set identifier $this->identifier = md5(mt_rand(0, mt_getrandmax()) . time()); // Set Slide Layout @@ -138,21 +136,15 @@ public function __clone() // Set parent $this->parent = clone $this->parent; // Shape collection - if (isset($this->shapeCollection)) { - $this->shapeCollection = clone $this->shapeCollection; - foreach ($this->shapeCollection as &$shape) { - $shape = clone $shape; + foreach ($this->shapeCollection as &$shape) { + $shape = clone $shape; } - } - // Transition object + // Transition if (isset($this->slideTransition)) { $this->slideTransition = clone $this->slideTransition; } - // Note object - if (isset($this->slideNote)) { + // Note $this->slideNote = clone $this->slideNote; - } - } /** diff --git a/src/PhpPresentation/Slide/AbstractSlide.php b/src/PhpPresentation/Slide/AbstractSlide.php index d67d51b16..e8a6398e1 100644 --- a/src/PhpPresentation/Slide/AbstractSlide.php +++ b/src/PhpPresentation/Slide/AbstractSlide.php @@ -30,10 +30,13 @@ use PhpOffice\PhpPresentation\Shape\Line; use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\Shape\Table; +use PhpOffice\PhpPresentation\Traits\ShapeCollection; use PhpOffice\PhpPresentation\ShapeContainerInterface; abstract class AbstractSlide implements ComparableInterface, ShapeContainerInterface { + use ShapeCollection; + /** * @var string */ @@ -44,13 +47,6 @@ abstract class AbstractSlide implements ComparableInterface, ShapeContainerInter */ protected $slideTransition; - /** - * Collection of shapes. - * - * @var array|ArrayObject - */ - protected $shapeCollection = []; - /** * Extent Y. * @@ -107,56 +103,6 @@ abstract class AbstractSlide implements ComparableInterface, ShapeContainerInter */ protected $background; - /** - * Get collection of shapes. - * - * @return array|ArrayObject - */ - public function getShapeCollection() - { - return $this->shapeCollection; - } - - /** - * Search into collection of shapes for a name (eventually filtered by type ex: RichText) - * - * @param string $name The name to find into the shape collection - * @param PhpOffice\PhpPresentation\Shape\RichText | PhpOffice\PhpPresentation\Shape\... $type Type of the class - * @return \ArrayObject|\PhpOffice\PhpPresentation\AbstractShape[] - */ - public function searchShapeByName(string $name, ?string $type=null) - { - if (isset($this->shapeCollection)) { - foreach ($this->shapeCollection as $shape) { - if ($shape->getName() == $name) { - if (!isset($type) || get_class($shape) == $type) { - return $shape; - }}}} - return null; - } - - /** - * Get collection of shapes - * - * @return AbstractSlide - */ - public function setShapeCollection($shapeCollection = []) - { - $this->shapeCollection = $shapeCollection; - - return $this; - } - - /** - * Add shape to slide. - */ - public function addShape(AbstractShape $shape): AbstractShape - { - $shape->setContainer($this); - - return $shape; - } - /** * Get X Offset. */ diff --git a/src/PhpPresentation/Slide/Animation.php b/src/PhpPresentation/Slide/Animation.php index f5215ac2a..ac41c7056 100644 --- a/src/PhpPresentation/Slide/Animation.php +++ b/src/PhpPresentation/Slide/Animation.php @@ -20,41 +20,9 @@ namespace PhpOffice\PhpPresentation\Slide; use PhpOffice\PhpPresentation\AbstractShape; +use PhpOffice\PhpPresentation\Traits\ShapeCollection; class Animation { - /** - * @var array - */ - protected $shapeCollection = []; - - /** - * @return Animation - */ - public function addShape(AbstractShape $shape) - { - $this->shapeCollection[] = $shape; - - return $this; - } - - /** - * @return array - */ - public function getShapeCollection(): array - { - return $this->shapeCollection; - } - - /** - * @param array $array - * - * @return Animation - */ - public function setShapeCollection(array $array = []) - { - $this->shapeCollection = $array; - - return $this; - } + use ShapeCollection; } diff --git a/src/PhpPresentation/Slide/Note.php b/src/PhpPresentation/Slide/Note.php index d60542813..219d77ba8 100644 --- a/src/PhpPresentation/Slide/Note.php +++ b/src/PhpPresentation/Slide/Note.php @@ -26,9 +26,12 @@ use PhpOffice\PhpPresentation\Shape\RichText; use PhpOffice\PhpPresentation\ShapeContainerInterface; use PhpOffice\PhpPresentation\Slide; +use PhpOffice\PhpPresentation\Traits\ShapeCollection; class Note implements ComparableInterface, ShapeContainerInterface { + use ShapeCollection; + /** * Parent slide. * @@ -36,13 +39,6 @@ class Note implements ComparableInterface, ShapeContainerInterface */ private $parent; - /** - * Collection of shapes. - * - * @var array|ArrayObject - */ - private $shapeCollection; - /** * Note identifier. * @@ -93,33 +89,10 @@ public function __construct(?Slide $pParent = null) // Set parent $this->parent = $pParent; - // Shape collection - $this->shapeCollection = new ArrayObject(); - // Set identifier $this->identifier = md5(mt_rand(0, 9999) . time()); } - /** - * Get collection of shapes. - * - * @return array|ArrayObject - */ - public function getShapeCollection() - { - return $this->shapeCollection; - } - - /** - * Add shape to slide. - */ - public function addShape(AbstractShape $shape): AbstractShape - { - $shape->setContainer($this); - - return $shape; - } - /** * Create rich text shape. */ diff --git a/src/PhpPresentation/Slide/SlideLayout.php b/src/PhpPresentation/Slide/SlideLayout.php index 6393ca7e2..b5c034a97 100644 --- a/src/PhpPresentation/Slide/SlideLayout.php +++ b/src/PhpPresentation/Slide/SlideLayout.php @@ -73,8 +73,6 @@ public function __construct(SlideMaster $pSlideMaster) { // Set parent $this->slideMaster = $pSlideMaster; - // Shape collection - $this->shapeCollection = new ArrayObject(); // Set identifier $this->identifier = md5(mt_rand(0, 9999) . time()); // Set a basic colorMap diff --git a/src/PhpPresentation/Slide/SlideMaster.php b/src/PhpPresentation/Slide/SlideMaster.php index 9d877d3f2..8957e6543 100644 --- a/src/PhpPresentation/Slide/SlideMaster.php +++ b/src/PhpPresentation/Slide/SlideMaster.php @@ -80,8 +80,6 @@ public function __construct(?PhpPresentation $pParent = null) { // Set parent $this->parent = $pParent; - // Shape collection - $this->shapeCollection = new ArrayObject(); // Set identifier $this->identifier = md5(mt_rand(0, 9999) . time()); // Set a basic colorMap diff --git a/src/PhpPresentation/Style/Effect.php b/src/PhpPresentation/Style/Effect.php deleted file mode 100644 index 5c25ca4f4..000000000 --- a/src/PhpPresentation/Style/Effect.php +++ /dev/null @@ -1,310 +0,0 @@ -effectType = $type; - $this->blurRadius = 6; - $this->distance = 2; - $this->direction = 0; - $this->alignment = self::SHADOW_BOTTOM_RIGHT; - $this->color = new Color(Color::COLOR_BLACK); - $this->alpha = 50; - } - - /** - * Define the type effect - * - * @param string $type - * @return $this - * @see self::EFFECT_SHADOW_INNER, self::EFFECT_SHADOW_OUTER, self::EFFECT_REFLECTION - */ - public function setEffectType(string $type) - { - $this->effectType = $type; - return $this; - } - - /** - * Get the effect type - * - * @return string - */ - public function getEffectType():string - { - return $this->effectType; - } - - /** - * Set the direction - * - * @param int $dir - * @return $this - */ - public function setDirection(?int $dir) - { - if (!isset($dir)) $dir = 0; - $this->direction = (int)$dir; - return $this; - } - - /** - * Get the direction - * - * @return int - */ - public function getDirection():int - { - return $this->direction; - } - - /** - * Set the blur radius - * - * @param int $radius - * @return $this - */ - public function setBlurRadius(?int $radius) - { - if (!isset($radius)) $radius = 6; - $this->blurRadius = $radius; - return $this; - } - - /** - * Get the blur radius - * - * @return int - */ - public function getBlurRadius():int - { - return $this->blurRadius; - } - - /** - * Get Shadow distance - * - * @return int - */ - public function getDistance():int - { - return $this->distance; - } - - /** - * Set Shadow distance - * - * @param int $distance - * @return $this - */ - public function setDistance(int $distance = 2) - { - $this->distance = $distance; - - return $this; - } - - /** - * Set the effect alignment - * - * @param string $align - * @return $this - */ - public function setAlignment(?string $align) - { - if (!isset($align)) $align = self::SHADOW_BOTTOM_RIGHT; - $this->align = $align; - return $this; - } - - /** - * Get the effect alignment - * - * @return string - */ - public function getAlignment():string - { - return $this->align; - } - - /** - * Set Color - * - * @param \PhpOffice\PhpPresentation\Style\Color $color - * @return $this - */ - public function setColor(Color $color = null) - { - $this->color = $color; - - return $this; - } - - /** - * Get Color - * - * @return \PhpOffice\PhpPresentation\Style\Color - */ - public function getColor() - { - return $this->color; - } - - /** - * Set Alpha - * - * @param int $alpha - * @return $this - */ - public function setAlpha(?int $alpha) - { - if (!isset($alpha)) $alpha = 0; - $this->alpha = $alpha; - - return $this; - } - - /** - * Get Alpha - * - * @return int - */ - public function getAlpha():int - { - return $this->alpha; - } - - /** - * Get hash code - * - * @return string Hash code - */ - public function getHashCode() - { - return md5($this->effectType . $this->blurRadius . $this->distance . $this->direction . $this->alignment . $this->color->getHashCode() . $this->alpha . __CLASS__); - } - - /** - * Get hash index - * - * Note that this index may vary during script execution! Only reliable moment is - * while doing a write of a workbook and when changes are not allowed. - * - * @return string Hash index - */ - public function getHashIndex() - { - return $this->hashIndex; - } - - /** - * Set hash index - * - * Note that this index may vary during script execution! Only reliable moment is - * while doing a write of a workbook and when changes are not allowed. - * - * @param string $value Hash index - */ - public function setHashIndex($value) - { - $this->hashIndex = $value; - } - -} diff --git a/src/PhpPresentation/Style/Font.php b/src/PhpPresentation/Style/Font.php index 474d3e0e3..3f54f1c29 100644 --- a/src/PhpPresentation/Style/Font.php +++ b/src/PhpPresentation/Style/Font.php @@ -20,6 +20,7 @@ namespace PhpOffice\PhpPresentation\Style; use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Exception\InvalidParameterException; use PhpOffice\PhpPresentation\Exception\NotAllowedValueException; /** @@ -27,6 +28,24 @@ */ class Font implements ComparableInterface { + // Capitalization type + public const CAPITALIZATION_NONE = 'none'; + public const CAPITALIZATION_SMALL = 'small'; + public const CAPITALIZATION_ALL = 'all'; + + // Charset type + public const CHARSET_DEFAULT = 0x01; + + // Format type + public const FORMAT_LATIN = 'latin'; + public const FORMAT_EAST_ASIAN = 'ea'; + public const FORMAT_COMPLEX_SCRIPT = 'cs'; + + // Strike type + public const STRIKE_NONE = 'noStrike'; + public const STRIKE_SINGLE = 'sngStrike'; + public const STRIKE_DOUBLE = 'dblStrike'; + // Underline types public const UNDERLINE_NONE = 'none'; public const UNDERLINE_DASH = 'dash'; @@ -46,23 +65,10 @@ class Font implements ComparableInterface public const UNDERLINE_WAVYDOUBLE = 'wavyDbl'; public const UNDERLINE_WAVYHEAVY = 'wavyHeavy'; public const UNDERLINE_WORDS = 'words'; - - /* Strike types */ - public const STRIKE_NONE = 'noStrike'; - public const STRIKE_SINGLE = 'sngStrike'; - public const STRIKE_DOUBLE = 'dblStrike'; - - public const FORMAT_LATIN = 'latin'; - public const FORMAT_EAST_ASIAN = 'ea'; - public const FORMAT_COMPLEX_SCRIPT = 'cs'; - - public const CAPITALIZATION_NONE = 'none'; - public const CAPITALIZATION_SMALL = 'small'; - public const CAPITALIZATION_ALL = 'all'; /* Script sub and super values */ - const SCRIPT_SUPER = 30000; - const SCRIPT_SUB = -25000; + public const BASELINE_SUPERSCRIPT = 300000; + public const BASELINE_SUBSCRIPT = -250000; /** * Name. @@ -72,23 +78,25 @@ class Font implements ComparableInterface private $name = 'Calibri'; /** - * panose + * Panose * * @var string */ - private $panose; + private $panose = ''; + /** - * pitchFamily + * Pitch Family * - * @var string + * @var int */ - private $pitchFamily; + private $pitchFamily = 0; + /** - * charset + * Charset * - * @var string + * @var int */ - private $charset; + private $charset = self::CHARSET_DEFAULT; /** * Font Size @@ -112,18 +120,11 @@ class Font implements ComparableInterface private $italic = false; /** - * Superscript. + * Baseline. * - * @var bool - */ - private $superScript = false; - - /** - * Subscript. - * - * @var bool + * @var int */ - private $subScript = false; + private $baseline = 0; /** * Capitalization. @@ -142,9 +143,9 @@ class Font implements ComparableInterface /** * Strikethrough. * - * @var bool + * @var string */ - private $strikethrough = false; + private $strikethrough = self::STRIKE_NONE; /** * Foreground color. @@ -177,9 +178,6 @@ class Font implements ComparableInterface public function __construct() { $this->color = new Color(Color::COLOR_BLACK); - $this->superScript = 0; - $this->subScript = 0; - $this->strikethrough = self::STRIKE_NONE; } /** @@ -203,51 +201,50 @@ public function setName(string $pValue = 'Calibri'): self } /** - * Get panose - * - * @return string + * Get panose. */ - public function getPanose() + public function getPanose(): string { return $this->panose; } /** - * Set panose - * - * @param string $pValue - * @return \PhpOffice\PhpPresentation\Style\Font + * Set panose. */ - public function setPanose($pValue) + public function setPanose(string $pValue): self { - if ($pValue == '') { - $pValue = ''; + if (mb_strlen($pValue) !== 10) { + throw new InvalidParameterException('pValue', $pValue, 'The length is not equals to 10'); } + + $allowedChars = ['0', '1', '2', '3', '4', '5', '6' ,'7' ,'8' ,'9' ,'A' ,'B' ,'C' ,'D' ,'E' ,'F']; + foreach(mb_str_split($pValue) as $char) { + if (!in_array($char, $allowedChars)) { + throw new InvalidParameterException( + 'pValue', + $pValue, + sprintf('The character "%s" is not allowed', $char) + ); + } + } + $this->panose = $pValue; return $this; } /** - * Get pitchFamily - * - * @return string + * Get pitchFamily. */ - public function getPitchFamily() + public function getPitchFamily(): int { return $this->pitchFamily; } /** - * Set pitchFamily - * - * @param string $pValue - * @return \PhpOffice\PhpPresentation\Style\Font + * Set pitchFamily. */ - public function setPitchFamily($pValue) + public function setPitchFamily(int $pValue): self { - if ($pValue == '') { - $pValue = ''; - } $this->pitchFamily = $pValue; return $this; @@ -255,24 +252,18 @@ public function setPitchFamily($pValue) /** * Get charset * - * @return string + * @return int */ - public function getCharset() + public function getCharset(): int { return $this->charset; } /** - * Set charset - * - * @param string $pValue - * @return \PhpOffice\PhpPresentation\Style\Font + * Set charset. */ - public function setCharset($pValue) + public function setCharset(int $pValue): self { - if ($pValue == '') { - $pValue = ''; - } $this->charset = $pValue; return $this; @@ -352,65 +343,57 @@ public function setItalic(bool $pValue = false): self } /** - * Get SuperScript. + * Set Baseline. */ - public function isSuperScript(): int + public function setBaseline(int $pValue): self { - return $this->superScript; + $this->baseline = $pValue; + + return $this; } /** - * Set SuperScript - * - * @param integer $pValue - * @return \PhpOffice\PhpPresentation\Style\Font + * Get Baseline. */ - public function setSuperScript($pValue = 0) + public function getBaseline(): int { - if ($pValue == '') { - $pValue = 0; - } - - $this->superScript = $pValue; + return $this->baseline; + } - // Set SubScript at false only if SuperScript is true - if ($pValue != 0) { - $this->subScript = 0; - } + /** + * Get SuperScript. + * @deprecated getBaseline() === self::BASELINE_SUPERSCRIPT + */ + public function isSuperScript(): bool + { + return $this->getBaseline() === self::BASELINE_SUPERSCRIPT; + } - return $this; + /** + * Set SuperScript + * @deprecated setBaseline(self::BASELINE_SUPERSCRIPT) + */ + public function setSuperScript(bool $pValue = false): self + { + return $this->setBaseline($pValue ? self::BASELINE_SUPERSCRIPT : ($this->getBaseline() == self::BASELINE_SUBSCRIPT ? $this->getBaseline() : 0)); } /** * Get SubScript - * - * @return integer + * @deprecated getBaseline() === self::BASELINE_SUBSCRIPT */ - public function isSubScript() + public function isSubScript(): bool { - return $this->subScript; + return $this->getBaseline() === self::BASELINE_SUBSCRIPT; } /** * Set SubScript - * - * @param integer $pValue - * @return \PhpOffice\PhpPresentation\Style\Font + * @deprecated setBaseline(self::BASELINE_SUBSCRIPT) */ - public function setSubScript($pValue = 0) + public function setSubScript(bool $pValue = false): self { - if ($pValue == '') { - $pValue = 0; - } - - $this->subScript = $pValue; - - // Set SuperScript at false only if SubScript is true - if ($pValue != 0) { - $this->superScript = 0; - } - - return $this; + return $this->setBaseline($pValue ? self::BASELINE_SUBSCRIPT : ($this->getBaseline() == self::BASELINE_SUPERSCRIPT ? $this->getBaseline() : 0)); } /** @@ -463,21 +446,40 @@ public function setUnderline(string $pValue = self::UNDERLINE_NONE): self /** * Get Strikethrough. + * @deprecated Use `getStrikethrough` */ public function isStrikethrough(): bool + { + return $this->strikethrough !== self::STRIKE_NONE; + } + + /** + * Get Strikethrough. + */ + public function getStrikethrough(): string { return $this->strikethrough; } /** * Set Strikethrough. + * + * @deprecated $pValue as boolean + * @param bool|string $pValue + * @return self */ - public function setStrikethrough($pValue = self::STRIKE_NONE) + public function setStrikethrough($pValue = false) { - if ($pValue == '') { - $pValue = self::STRIKE_NONE; + if (is_bool($pValue)) { + $pValue = $pValue ? self::STRIKE_SINGLE : self::STRIKE_NONE; + } + if (in_array($pValue, [ + self::STRIKE_NONE, + self::STRIKE_SINGLE, + self::STRIKE_DOUBLE + ])) { + $this->strikethrough = $pValue; } - $this->strikethrough = $pValue; return $this; } @@ -536,8 +538,7 @@ public function getHashCode(): string . $this->size . ($this->bold ? 't' : 'f') . ($this->italic ? 't' : 'f') - . ($this->superScript ? 't' : 'f') - . ($this->subScript ? 't' : 'f') + . $this->baseline . $this->underline . ($this->strikethrough ? 't' : 'f') . $this->format diff --git a/src/PhpPresentation/Style/Shadow.php b/src/PhpPresentation/Style/Shadow.php index bdbae7e04..2221a1461 100644 --- a/src/PhpPresentation/Style/Shadow.php +++ b/src/PhpPresentation/Style/Shadow.php @@ -20,12 +20,17 @@ namespace PhpOffice\PhpPresentation\Style; use PhpOffice\PhpPresentation\ComparableInterface; +use PhpOffice\PhpPresentation\Exception\NotAllowedValueException; /** * \PhpOffice\PhpPresentation\Style\Shadow. */ class Shadow implements ComparableInterface { + public const TYPE_SHADOW_INNER = 'innerShdw'; + public const TYPE_SHADOW_OUTER = 'outerShdw'; + public const TYPE_REFLECTION = 'reflection'; + // Shadow alignment public const SHADOW_BOTTOM = 'b'; public const SHADOW_BOTTOM_LEFT = 'bl'; @@ -81,6 +86,11 @@ class Shadow implements ComparableInterface */ private $alpha = 50; + /** + * @var string + */ + private $type = self::TYPE_SHADOW_OUTER; + /** * Hash index. * @@ -224,6 +234,31 @@ public function setAlpha(int $pValue = 0): self return $this; } + /** + * Get Type. + */ + public function getType(): string + { + return $this->type; + } + + /** + * Set Type. + */ + public function setType(string $pValue = self::TYPE_SHADOW_OUTER): self + { + if (!in_array( + $pValue, + [self::TYPE_REFLECTION, self::TYPE_SHADOW_INNER, self::TYPE_SHADOW_OUTER] + )) { + throw new NotAllowedValueException($pValue, [self::TYPE_REFLECTION, self::TYPE_SHADOW_INNER, self::TYPE_SHADOW_OUTER]); + } + + $this->type = $pValue; + + return $this; + } + /** * Get hash code. * @@ -231,7 +266,7 @@ public function setAlpha(int $pValue = 0): self */ public function getHashCode(): string { - return md5(($this->visible ? 't' : 'f') . $this->blurRadius . $this->distance . $this->direction . $this->alignment . $this->color->getHashCode() . $this->alpha . __CLASS__); + return md5(($this->visible ? 't' : 'f') . $this->blurRadius . $this->distance . $this->direction . $this->alignment . $this->type . $this->color->getHashCode() . $this->alpha . __CLASS__); } /** diff --git a/src/PhpPresentation/Traits/ShapeCollection.php b/src/PhpPresentation/Traits/ShapeCollection.php new file mode 100644 index 000000000..e5b3369d4 --- /dev/null +++ b/src/PhpPresentation/Traits/ShapeCollection.php @@ -0,0 +1,90 @@ + + */ + protected $shapeCollection = []; + + /** + * Get collection of shapes. + * + * @return array + */ + public function getShapeCollection(): array + { + return $this->shapeCollection; + } + + /** + * Search into collection of shapes for a name or/and a type. + * + * @return array + */ + public function searchShapes(?string $name = null, ?string $type = null): array + { + $found = []; + foreach ($this->getShapeCollection() as $shape) { + if ($name && $shape->getName() !== $name) { + continue; + } + if ($type && get_class($shape) !== $type) { + continue; + } + + $found[] = $shape; + } + + return $found; + } + + /** + * Get collection of shapes. + * + * @param array $shapeCollection + */ + public function setShapeCollection(array $shapeCollection = []): self + { + $this->shapeCollection = $shapeCollection; + + return $this; + } + + public function addShape(AbstractShape $shape): self + { + $this->shapeCollection[] = $shape; + + return $this; + } + + public function unsetShape(int $key): self + { + unset($this->shapeCollection[$key]); + + return $this; + } +} diff --git a/src/PhpPresentation/Writer/AbstractWriter.php b/src/PhpPresentation/Writer/AbstractWriter.php index 3d935c417..d389f6862 100644 --- a/src/PhpPresentation/Writer/AbstractWriter.php +++ b/src/PhpPresentation/Writer/AbstractWriter.php @@ -116,7 +116,7 @@ protected function allDrawings(): array // Loop through PhpPresentation foreach (array_merge($this->getPhpPresentation()->getAllSlides(), $aSlideMasters, $aSlideLayouts) as $oSlide) { - $arrayReturn = $this->iterateCollection($oSlide->getShapeCollection()->getIterator()); + $arrayReturn = $this->iterateCollection($oSlide->getShapeCollection()); $aDrawings = array_merge($aDrawings, $arrayReturn); } @@ -124,28 +124,23 @@ protected function allDrawings(): array } /** - * @param ArrayIterator $oIterator + * @param array $collection * * @return array */ - private function iterateCollection(ArrayIterator $oIterator): array + private function iterateCollection(array $collection): array { $arrayReturn = []; - if ($oIterator->count() <= 0) { - return $arrayReturn; - } - while ($oIterator->valid()) { - $oShape = $oIterator->current(); + foreach($collection as $oShape) { if ($oShape instanceof AbstractDrawingAdapter) { $arrayReturn[] = $oShape; } elseif ($oShape instanceof Chart) { $arrayReturn[] = $oShape; } elseif ($oShape instanceof Group) { - $arrayGroup = $this->iterateCollection($oShape->getShapeCollection()->getIterator()); + $arrayGroup = $this->iterateCollection($oShape->getShapeCollection()); $arrayReturn = array_merge($arrayReturn, $arrayGroup); } - $oIterator->next(); } return $arrayReturn; diff --git a/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php index 79ea0c22a..6565b3b5f 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/AbstractSlide.php @@ -49,6 +49,7 @@ use PhpOffice\PhpPresentation\Style\Border; use PhpOffice\PhpPresentation\Style\Bullet; use PhpOffice\PhpPresentation\Style\Color; +use PhpOffice\PhpPresentation\Style\Font; use PhpOffice\PhpPresentation\Style\Shadow; abstract class AbstractSlide extends AbstractDecoratorWriter @@ -58,60 +59,56 @@ abstract class AbstractSlide extends AbstractDecoratorWriter */ protected function writeDrawingRelations(AbstractSlideAlias $pSlideMaster, XMLWriter $objWriter, int $relId) { - if ($pSlideMaster->getShapeCollection()->count() > 0) { + if (count($pSlideMaster->getShapeCollection()) > 0) { // Loop trough images and write relationships - $iterator = $pSlideMaster->getShapeCollection()->getIterator(); - while ($iterator->valid()) { - if ($iterator->current() instanceof ShapeDrawingFile || $iterator->current() instanceof ShapeDrawingGd) { + foreach($pSlideMaster->getShapeCollection() as $shape) { + if ($shape instanceof ShapeDrawingFile || $shape instanceof ShapeDrawingGd) { // Write relationship for image drawing $this->writeRelationship( $objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', - '../media/' . str_replace(' ', '_', $iterator->current()->getIndexedFilename()) + '../media/' . str_replace(' ', '_', $shape->getIndexedFilename()) ); - $iterator->current()->relationId = 'rId' . $relId; + $shape->relationId = 'rId' . $relId; ++$relId; - } elseif ($iterator->current() instanceof ShapeChart) { + } elseif ($shape instanceof ShapeChart) { // Write relationship for chart drawing $this->writeRelationship( $objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', - '../charts/' . $iterator->current()->getIndexedFilename() + '../charts/' . $shape->getIndexedFilename() ); - $iterator->current()->relationId = 'rId' . $relId; + $shape->relationId = 'rId' . $relId; ++$relId; - } elseif ($iterator->current() instanceof Group) { - $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { - if ($iterator2->current() instanceof ShapeDrawingFile || - $iterator2->current() instanceof ShapeDrawingGd + } elseif ($shape instanceof Group) { + foreach($shape->getShapeCollection() as $subShape) { + if ($subShape instanceof ShapeDrawingFile || + $subShape instanceof ShapeDrawingGd ) { // Write relationship for image drawing $this->writeRelationship( $objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', - '../media/' . str_replace(' ', '_', $iterator2->current()->getIndexedFilename()) + '../media/' . str_replace(' ', '_', $subShape->getIndexedFilename()) ); - $iterator2->current()->relationId = 'rId' . $relId; + $subShape->relationId = 'rId' . $relId; ++$relId; - } elseif ($iterator2->current() instanceof ShapeChart) { + } elseif ($subShape instanceof ShapeChart) { // Write relationship for chart drawing $this->writeRelationship( $objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', - '../charts/' . $iterator2->current()->getIndexedFilename() + '../charts/' . $subShape->getIndexedFilename() ); - $iterator2->current()->relationId = 'rId' . $relId; + $subShape->relationId = 'rId' . $relId; ++$relId; } - $iterator2->next(); } } - $iterator->next(); } } @@ -228,7 +225,7 @@ protected function writeShapeText(XMLWriter $objWriter, RichText $shape, int $sh $this->writeFill($objWriter, $shape->getFill()); $this->writeBorder($objWriter, $shape->getBorder(), ''); - $this->writeEffect($objWriter, $shape->getEffectCollection()); + $this->writeShadow($objWriter, $shape->getShadow()); // > p:sp\p:spPr $objWriter->endElement(); @@ -267,7 +264,7 @@ protected function writeShapeText(XMLWriter $objWriter, RichText $shape, int $sh $objWriter->writeAttribute('tIns', CommonDrawing::pixelsToEmu($shape->getInsetTop())); // Vertical alignment $objWriter->writeAttribute('anchor', $shape->getVerticalAlignment()); - $objWriter->writeAttribute('anchorCtr', (int)$shape->getVerticalAlignCenter()); + $objWriter->writeAttribute('anchorCtr', $shape->getVerticalAlignCenter()); if ($shape->getColumns() <> 1) { $objWriter->writeAttribute('numCol', $shape->getColumns()); $objWriter->writeAttribute('spcCol', CommonDrawing::pixelsToEmu($shape->getColumnSpacing())); @@ -655,7 +652,9 @@ protected function writeRunStyles(XMLWriter $objWriter, RichText\Run $element): $objWriter->writeAttributeIf($element->getFont()->isBold(), 'b', '1'); $objWriter->writeAttributeIf($element->getFont()->isItalic(), 'i', '1'); - $objWriter->writeAttributeIf($element->getFont()->isStrikethrough(), 'strike', $element->getFont()->isStrikethrough()); + + // Strikethrough + $objWriter->writeAttribute('strike', $element->getFont()->getStrikethrough()); // Size $objWriter->writeAttribute('sz', ($element->getFont()->getSize() * 100)); @@ -669,30 +668,37 @@ protected function writeRunStyles(XMLWriter $objWriter, RichText\Run $element): // Capitalization $objWriter->writeAttribute('cap', $element->getFont()->getCapitalization()); - // Superscript / subscript - $objWriter->writeAttributeIf($element->getFont()->isSuperScript() != 0, 'baseline', (int)$element->getFont()->isSuperScript()); - $objWriter->writeAttributeIf($element->getFont()->isSubScript() != 0, 'baseline', (int)$element->getFont()->isSubScript()); + // Baseline + $objWriter->writeAttributeIf($element->getFont()->getBaseline() !== 0, 'baseline', $element->getFont()->getBaseline()); // Color - a:solidFill $objWriter->startElement('a:solidFill'); $this->writeColor($objWriter, $element->getFont()->getColor()); $objWriter->endElement(); - // Write Effects - $this->writeEffect($objWriter, $element->getEffectCollection(), 'srgbClr'); - // Font // - a:latin // - a:ea // - a:cs $objWriter->startElement('a:' . $element->getFont()->getFormat()); $objWriter->writeAttribute('typeface', $element->getFont()->getName()); - if ($element->getFont()->getPanose()!="") - $objWriter->writeAttribute('panose', $element->getFont()->getPanose()); - if ($element->getFont()->getPitchFamily()!="") - $objWriter->writeAttribute('pitchFamily', $element->getFont()->getPitchFamily()); - if ($element->getFont()->getCharset()!="") - $objWriter->writeAttribute('charset', $element->getFont()->getCharset()); + if ($element->getFont()->getPanose() !== '') { + $panose = array_map(function(string $value) { + return '0' . $value; + }, str_split($element->getFont()->getPanose())); + + $objWriter->writeAttribute('panose', implode('', $panose)); + } + $objWriter->writeAttributeIf( + $element->getFont()->getPitchFamily() !== 0, + 'pitchFamily', + $element->getFont()->getPitchFamily() + ); + $objWriter->writeAttributeIf( + $element->getFont()->getCharset() !== Font::CHARSET_DEFAULT, + 'charset', + dechex($element->getFont()->getCharset()) + ); $objWriter->endElement(); // a:hlinkClick @@ -806,7 +812,7 @@ protected function writeShadow(XMLWriter $objWriter, Shadow $oShadow): void $objWriter->startElement('a:effectLst'); // a:outerShdw - $objWriter->startElement('a:outerShdw'); + $objWriter->startElement('a:' . $oShadow->getType()); $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($oShadow->getBlurRadius())); $objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($oShadow->getDistance())); $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle((int) $oShadow->getDirection())); @@ -820,54 +826,6 @@ protected function writeShadow(XMLWriter $objWriter, Shadow $oShadow): void $objWriter->endElement(); } - /** - * Write Effect - * @param XMLWriter $objWriter - * @param array \PhpOffice\PhpPresentation\Style\Effect[] - * @param Shadow $oShadow - */ - protected function writeEffect(XMLWriter $objWriter, ?array $aEffect, ?string $tagClr=null) - { - // NO Effect => return - if (!isset($aEffect) && !is_array($aEffect)) { - return; - } - if (!isset($tagClr)) { - $tagClr = 'prstClr'; - } - - // a:effectLst - $objWriter->startElement('a:effectLst'); - // Write each effect - foreach($aEffect as $effect) { - // a: - if ( $effect->getEffectType() == 'outerShdw' - || $effect->getEffectType() == 'innerShdw') { -// @TODO || $effect->getEffectType() == 'reflection') { - - $objWriter->startElement('a:'.$effect->getEffectType()); - $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($effect->getBlurRadius())); - $objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($effect->getDistance())); - $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle($effect->getDirection())); - $objWriter->writeAttribute('algn', $effect->getAlignment()); - $objWriter->writeAttribute('rotWithShape', '0'); - - // a:prstClr - $objWriter->startElement('a:'.$tagClr); - $objWriter->writeAttribute('val', $effect->getColor()->getRGB()); - // a:alpha - $objWriter->startElement('a:alpha'); - $objWriter->writeAttribute('val', $effect->getAlpha() * 1000); - $objWriter->endElement(); - $objWriter->endElement(); - - $objWriter->endElement(); - } - } - - $objWriter->endElement(); - } - /** * Write hyperlink * @@ -1500,7 +1458,7 @@ protected function writeShapePic(XMLWriter $objWriter, AbstractGraphic $shape, i $this->writeFill($objWriter, $shape->getFill()); $this->writeBorder($objWriter, $shape->getBorder(), ''); - $this->writeEffect($objWriter, $shape->getEffectCollection()); + $this->writeShadow($objWriter, $shape->getShadow()); $objWriter->endElement(); diff --git a/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php b/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php index 541e8c1d1..aa8ea2a9d 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/ContentTypes.php @@ -85,7 +85,7 @@ public function render(): ZipInterface for ($i = 0; $i < $slideCount; ++$i) { $oSlide = $this->oPresentation->getSlide($i); $this->writeOverrideContentType($objWriter, '/ppt/slides/slide' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml'); - if ($oSlide->getNote()->getShapeCollection()->count() > 0) { + if (count($oSlide->getNote()->getShapeCollection()) > 0) { $this->writeOverrideContentType($objWriter, '/ppt/notesSlides/notesSlide' . ($i + 1) . '.xml', 'application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml'); } foreach ($oSlide->getShapeCollection() as $oShape) { diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php index 6df85cde0..d21f6863a 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsCore.php @@ -70,18 +70,17 @@ public function render(): ZipInterface // cp:keywords $objWriter->writeElement('cp:keywords', $this->oPresentation->getDocumentProperties()->getKeywords()); + // cp:category + $objWriter->writeElement('cp:category', $this->oPresentation->getDocumentProperties()->getCategory()); + // cp:revision $objWriter->writeElement('cp:revision', $this->oPresentation->getDocumentProperties()->getRevision()); // cp:contentStatus - $objWriter->writeElement('cp:contentStatus', $this->oPresentation->getDocumentProperties()->getStatus()); - - // cp:category - $objWriter->writeElement('cp:category', $this->oPresentation->getDocumentProperties()->getCategory()); - if ($this->oPresentation->getPresentationProperties()->isMarkedAsFinal()) { - // cp:contentStatus = Final $objWriter->writeElement('cp:contentStatus', 'Final'); + } else { + $objWriter->writeElement('cp:contentStatus', $this->oPresentation->getDocumentProperties()->getStatus()); } $objWriter->endElement(); diff --git a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php index 103309e5c..fde2f6f36 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/DocPropsThumbnail.php @@ -20,31 +20,15 @@ namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007; use PhpOffice\Common\Adapter\Zip\ZipInterface; +use PhpOffice\PhpPresentation\PresentationProperties; class DocPropsThumbnail extends AbstractDecoratorWriter { public function render(): ZipInterface { - $pathThumbnail = $this->getPresentation()->getPresentationProperties()->getThumbnailPath(); - $type = $this->getPresentation()->getPresentationProperties()->getThumbnailType(); - - // From local file - if ($pathThumbnail && $type == \PhpOffice\PhpPresentation\PresentationProperties::THUMBNAIL_FILE) { - $fileThumbnail = file_get_contents($pathThumbnail); - $gdImage = imagecreatefromstring($fileThumbnail); - if ($gdImage) { - ob_start(); - imagejpeg($gdImage); - $imageContents = ob_get_contents(); - ob_end_clean(); - imagedestroy($gdImage); - $this->getZip()->addFromString('docProps/thumbnail.jpeg', $imageContents); - } - } - - // From ZIP original file - if ($pathThumbnail && $type == \PhpOffice\PhpPresentation\PresentationProperties::THUMBNAIL_ZIP) { - $gdImage = imagecreatefromstring($this->getPresentation()->getPresentationProperties()->getThumbnail()); + $thumnbail = $this->getPresentation()->getPresentationProperties()->getThumbnail(); + if ($thumnbail) { + $gdImage = imagecreatefromstring($thumnbail); if ($gdImage) { ob_start(); imagejpeg($gdImage); diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php b/src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php index d2cd90410..c03d97621 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptCharts.php @@ -447,11 +447,10 @@ protected function writeTitle(XMLWriter $objWriter, Title $subject): void $objWriter->writeAttribute('dirty', '0'); $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $subject->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $subject->getFont()->getUnderline()); - $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($subject->getFont()->getBaseline() !== 0, 'baseline', $subject->getFont()->getBaseline()); $objWriter->writeAttribute('cap', $subject->getFont()->getCapitalization()); // Font - a:solidFill @@ -609,11 +608,10 @@ protected function writeLegend(XMLWriter $objWriter, Legend $subject): void $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $subject->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $subject->getFont()->getUnderline()); - $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($subject->getFont()->getBaseline() !== 0, 'baseline', $subject->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -895,11 +893,10 @@ protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, bool $includ $objWriter->startElement('a:defRPr'); $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // a:solidFill $objWriter->startElement('a:solidFill'); @@ -1102,11 +1099,10 @@ protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, bool $in $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -1311,11 +1307,10 @@ protected function writeTypeDoughnut(XMLWriter $objWriter, Doughnut $subject, bo $objWriter->startElement('a:defRPr'); $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:solidFill $objWriter->startElement('a:solidFill'); @@ -1454,11 +1449,10 @@ protected function writeTypePie(XMLWriter $objWriter, Pie $subject, bool $includ $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -1618,11 +1612,10 @@ protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, bool $in $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -1771,11 +1764,10 @@ protected function writeTypeLine(XMLWriter $objWriter, Line $subject, bool $incl $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -1947,11 +1939,10 @@ protected function writeTypeRadar(XMLWriter $objWriter, Radar $subject, bool $in $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '30000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-25000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -2113,11 +2104,10 @@ protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, bool $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $series->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $series->getFont()->getUnderline()); - $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($series->getFont()->getBaseline() !== 0, 'baseline', $series->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -2392,11 +2382,10 @@ protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, string $ty $objWriter->writeAttribute('b', ($oAxis->getFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($oAxis->getFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($oAxis->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $oAxis->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($oAxis->getFont()->getSize() * 100)); $objWriter->writeAttribute('u', $oAxis->getFont()->getUnderline()); - $objWriter->writeAttributeIf($oAxis->getFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($oAxis->getFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($oAxis->getFont()->getBaseline() !== 0, 'baseline', $oAxis->getFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); @@ -2496,11 +2485,10 @@ protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, string $ty $objWriter->startElement('a:defRPr'); $objWriter->writeAttribute('b', ($oAxis->getTickLabelFont()->isBold() ? 'true' : 'false')); $objWriter->writeAttribute('i', ($oAxis->getTickLabelFont()->isItalic() ? 'true' : 'false')); - $objWriter->writeAttribute('strike', ($oAxis->getTickLabelFont()->isStrikethrough() ? 'sngStrike' : 'noStrike')); + $objWriter->writeAttribute('strike', $oAxis->getFont()->getStrikethrough()); $objWriter->writeAttribute('sz', ($oAxis->getTickLabelFont()->getSize() * 100)); $objWriter->writeAttribute('u', $oAxis->getTickLabelFont()->getUnderline()); - $objWriter->writeAttributeIf($oAxis->getTickLabelFont()->isSuperScript(), 'baseline', '300000'); - $objWriter->writeAttributeIf($oAxis->getTickLabelFont()->isSubScript(), 'baseline', '-250000'); + $objWriter->writeAttributeIf($oAxis->getTickLabelFont()->getBaseline() !== 0, 'baseline', $oAxis->getTickLabelFont()->getBaseline()); // Font - a:solidFill $objWriter->startElement('a:solidFill'); diff --git a/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php b/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php index 00da6cbf8..0e0140151 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/PptSlides.php @@ -49,7 +49,7 @@ public function render(): ZipInterface // Add note slide if ($oSlide->getNote() instanceof Note) { - if ($oSlide->getNote()->getShapeCollection()->count() > 0) { + if (count($oSlide->getNote()->getShapeCollection()) > 0) { $this->oZip->addFromString('ppt/notesSlides/notesSlide' . ($idx + 1) . '.xml', $this->writeNote($oSlide->getNote())); } } @@ -96,16 +96,14 @@ protected function writeSlideRelationships(Slide $pSlide): string ++$relId; // Write drawing relationships? - if ($pSlide->getShapeCollection()->count() > 0) { - $collections = [$pSlide->getShapeCollection()->getIterator()]; + if (count($pSlide->getShapeCollection()) > 0) { + $collections = [$pSlide->getShapeCollection()]; // Loop trough images and write relationships while (count($collections)) { - $iterator = array_shift($collections); - - while ($iterator->valid()) { - $currentShape = $iterator->current(); + $collection = array_shift($collections); + foreach($collection as $currentShape) { if ($currentShape instanceof Media) { // Write relationship for image drawing $currentShape->relationId = 'rId' . $relId; @@ -124,10 +122,8 @@ protected function writeSlideRelationships(Slide $pSlide): string $currentShape->relationId = 'rId' . $relId; ++$relId; } elseif ($currentShape instanceof ShapeContainerInterface) { - $collections[] = $currentShape->getShapeCollection()->getIterator(); + $collections[] = $currentShape->getShapeCollection(); } - - $iterator->next(); } } } @@ -141,14 +137,13 @@ protected function writeSlideRelationships(Slide $pSlide): string } // Write hyperlink relationships? - if ($pSlide->getShapeCollection()->count() > 0) { + if (count($pSlide->getShapeCollection()) > 0) { // Loop trough hyperlinks and write relationships - $iterator = $pSlide->getShapeCollection()->getIterator(); - while ($iterator->valid()) { + foreach($pSlide->getShapeCollection() as $shape) { // Hyperlink on shape - if ($iterator->current()->hasHyperlink()) { + if ($shape->hasHyperlink()) { // Write relationship for hyperlink - $hyperlink = $iterator->current()->getHyperlink(); + $hyperlink = $shape->getHyperlink(); $hyperlink->relationId = 'rId' . $relId; if (!$hyperlink->isInternal()) { @@ -161,8 +156,8 @@ protected function writeSlideRelationships(Slide $pSlide): string } // Hyperlink on rich text run - if ($iterator->current() instanceof RichText) { - foreach ($iterator->current()->getParagraphs() as $paragraph) { + if ($shape instanceof RichText) { + foreach ($shape->getParagraphs() as $paragraph) { foreach ($paragraph->getRichTextElements() as $element) { if ($element instanceof Run || $element instanceof TextElement) { if ($element->hasHyperlink()) { @@ -184,14 +179,14 @@ protected function writeSlideRelationships(Slide $pSlide): string } // Hyperlink in table - if ($iterator->current() instanceof ShapeTable) { + if ($shape instanceof ShapeTable) { // Rows - $countRows = count($iterator->current()->getRows()); + $countRows = count($shape->getRows()); for ($row = 0; $row < $countRows; ++$row) { // Cells in rows - $countCells = count($iterator->current()->getRow($row)->getCells()); + $countCells = count($shape->getRow($row)->getCells()); for ($cell = 0; $cell < $countCells; ++$cell) { - $currentCell = $iterator->current()->getRow($row)->getCell($cell); + $currentCell = $shape->getRow($row)->getCell($cell); // Paragraphs in cell foreach ($currentCell->getParagraphs() as $paragraph) { // RichText in paragraph @@ -218,13 +213,12 @@ protected function writeSlideRelationships(Slide $pSlide): string } } - if ($iterator->current() instanceof Group) { - $iterator2 = $pSlide->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { + if ($shape instanceof Group) { + foreach($shape->getShapeCollection() as $subShape) { // Hyperlink on shape - if ($iterator2->current()->hasHyperlink()) { + if ($subShape->hasHyperlink()) { // Write relationship for hyperlink - $hyperlink = $iterator2->current()->getHyperlink(); + $hyperlink = $subShape->getHyperlink(); $hyperlink->relationId = 'rId' . $relId; if (!$hyperlink->isInternal()) { @@ -237,8 +231,8 @@ protected function writeSlideRelationships(Slide $pSlide): string } // Hyperlink on rich text run - if ($iterator2->current() instanceof RichText) { - foreach ($iterator2->current()->getParagraphs() as $paragraph) { + if ($subShape instanceof RichText) { + foreach ($subShape->getParagraphs() as $paragraph) { foreach ($paragraph->getRichTextElements() as $element) { if ($element instanceof Run || $element instanceof TextElement) { if ($element->hasHyperlink()) { @@ -260,14 +254,14 @@ protected function writeSlideRelationships(Slide $pSlide): string } // Hyperlink in table - if ($iterator2->current() instanceof ShapeTable) { + if ($subShape instanceof ShapeTable) { // Rows - $countRows = count($iterator2->current()->getRows()); + $countRows = count($subShape->getRows()); for ($row = 0; $row < $countRows; ++$row) { // Cells in rows - $countCells = count($iterator2->current()->getRow($row)->getCells()); + $countCells = count($subShape->getRow($row)->getCells()); for ($cell = 0; $cell < $countCells; ++$cell) { - $currentCell = $iterator2->current()->getRow($row)->getCell($cell); + $currentCell = $subShape->getRow($row)->getCell($cell); // Paragraphs in cell foreach ($currentCell->getParagraphs() as $paragraph) { // RichText in paragraph @@ -293,39 +287,30 @@ protected function writeSlideRelationships(Slide $pSlide): string } } } - - $iterator2->next(); } } - - $iterator->next(); } } // Write comment relationships - if ($pSlide->getShapeCollection()->count() > 0) { + if (count($pSlide->getShapeCollection()) > 0) { $hasSlideComment = false; // Loop trough images and write relationships - $iterator = $pSlide->getShapeCollection()->getIterator(); - while ($iterator->valid()) { - if ($iterator->current() instanceof Comment) { + foreach($pSlide->getShapeCollection() as $shape) { + if ($shape instanceof Comment) { $hasSlideComment = true; break; - } elseif ($iterator->current() instanceof Group) { - $iterator2 = $iterator->current()->getShapeCollection()->getIterator(); - while ($iterator2->valid()) { - if ($iterator2->current() instanceof Comment) { + } elseif ($shape instanceof Group) { + foreach($shape->getShapeCollection() as $subShape) { + if ($subShape instanceof Comment) { $hasSlideComment = true; break 2; } - $iterator2->next(); } } - - $iterator->next(); } if ($hasSlideComment) { @@ -334,7 +319,7 @@ protected function writeSlideRelationships(Slide $pSlide): string } } - if ($pSlide->getNote()->getShapeCollection()->count() > 0) { + if (count($pSlide->getNote()->getShapeCollection()) > 0) { $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide', '../notesSlides/notesSlide' . ($idxSlide + 1) . '.xml'); } diff --git a/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php b/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php index 804c175e2..7d652778c 100644 --- a/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php +++ b/src/PhpPresentation/Writer/PowerPoint2007/Relationships.php @@ -63,29 +63,18 @@ protected function writeRelationships(): string $this->writeRelationship($objWriter, 4, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties', 'docProps/custom.xml'); $idxRelation = 5; - // Thumbnail - $path = $this->getPresentation()->getPresentationProperties()->getThumbnailPath(); - $type = $this->getPresentation()->getPresentationProperties()->getThumbnailType(); - // From local file - if ($path && $type == \PhpOffice\PhpPresentation\PresentationProperties::THUMBNAIL_FILE) { - $pathThumbnail = file_get_contents($path); - $gdImage = imagecreatefromstring($pathThumbnail); - if ($gdImage) { - imagedestroy($gdImage); - // Relationship docProps/thumbnail.jpeg - $this->writeRelationship($objWriter, $idxRelation, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail', 'docProps/thumbnail.jpeg'); - } - } - // From ZIP original file - if ($path && $type == \PhpOffice\PhpPresentation\PresentationProperties::THUMBNAIL_ZIP) { - $gdImage = imagecreatefromstring($this->getPresentation()->getPresentationProperties()->getThumbnail()); + + // Relationship docProps/thumbnail.jpeg + $thumnbail = $this->getPresentation()->getPresentationProperties()->getThumbnail(); + if ($thumnbail) { + $gdImage = imagecreatefromstring($thumnbail); if ($gdImage) { imagedestroy($gdImage); // Relationship docProps/thumbnail.jpeg $this->writeRelationship($objWriter, $idxRelation, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail', 'docProps/thumbnail.jpeg'); + // $idxRelation++; } } - // ++$idxRelation $objWriter->endElement(); diff --git a/src/PhpPresentation/Writer/Serialized.php b/src/PhpPresentation/Writer/Serialized.php index 72ac2790a..02b7c4e1c 100644 --- a/src/PhpPresentation/Writer/Serialized.php +++ b/src/PhpPresentation/Writer/Serialized.php @@ -66,12 +66,11 @@ public function save(string $pFilename): void // Add media $slideCount = $oPresentation->getSlideCount(); for ($i = 0; $i < $slideCount; ++$i) { - for ($j = 0; $j < $oPresentation->getSlide($i)->getShapeCollection()->count(); ++$j) { - if ($oPresentation->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) { - $imgTemp = $oPresentation->getSlide($i)->getShapeCollection()->offsetGet($j); + foreach($oPresentation->getSlide($i)->getShapeCollection() as $shape) { + if ($shape instanceof AbstractDrawingAdapter) { $objZip->addFromString( - 'media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME), - file_get_contents($imgTemp->getPath()) + 'media/' . $shape->getImageIndex() . '/' . pathinfo($shape->getPath(), PATHINFO_BASENAME), + file_get_contents($shape->getPath()) ); } } @@ -99,14 +98,13 @@ protected function writeSerialized(?PhpPresentation $pPhpPresentation = null, $p // Update media links $slideCount = $pPhpPresentation->getSlideCount(); for ($i = 0; $i < $slideCount; ++$i) { - for ($j = 0; $j < $pPhpPresentation->getSlide($i)->getShapeCollection()->count(); ++$j) { - if ($pPhpPresentation->getSlide($i)->getShapeCollection()->offsetGet($j) instanceof AbstractDrawingAdapter) { - $imgTemp = $pPhpPresentation->getSlide($i)->getShapeCollection()->offsetGet($j); - $imgPath = 'zip://' . $pFilename . '#media/' . $imgTemp->getImageIndex() . '/' . pathinfo($imgTemp->getPath(), PATHINFO_BASENAME); - if ($imgTemp instanceof File) { - $imgTemp->setPath($imgPath, false); + foreach($pPhpPresentation->getSlide($i)->getShapeCollection() as $shape) { + if ($shape instanceof AbstractDrawingAdapter) { + $imgPath = 'zip://' . $pFilename . '#media/' . $shape->getImageIndex() . '/' . pathinfo($shape->getPath(), PATHINFO_BASENAME); + if ($shape instanceof File) { + $shape->setPath($imgPath, false); } else { - $imgTemp->setPath($imgPath); + $shape->setPath($imgPath); } } } diff --git a/tests/PhpPresentation/Tests/DocumentPropertiesTest.php b/tests/PhpPresentation/Tests/DocumentPropertiesTest.php index 5d0b37259..dd2a4d922 100644 --- a/tests/PhpPresentation/Tests/DocumentPropertiesTest.php +++ b/tests/PhpPresentation/Tests/DocumentPropertiesTest.php @@ -46,6 +46,8 @@ public function testGetSet(): void 'keywords' => '', 'category' => '', 'company' => '', + 'revision' => '', + 'status' => '', ]; foreach ($properties as $key => $val) { diff --git a/tests/PhpPresentation/Tests/PresentationPropertiesTest.php b/tests/PhpPresentation/Tests/PresentationPropertiesTest.php index db361430e..88b0920d7 100644 --- a/tests/PhpPresentation/Tests/PresentationPropertiesTest.php +++ b/tests/PhpPresentation/Tests/PresentationPropertiesTest.php @@ -130,14 +130,36 @@ public function testThumbnail(): void $object = new PresentationProperties(); self::assertNull($object->getThumbnailPath()); - self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath('AAAA')); + self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath('AAAA', PresentationProperties::THUMBNAIL_FILE)); self::assertNull($object->getThumbnailPath()); self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath()); self::assertNull($object->getThumbnailPath()); - self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath($imagePath)); + self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath($imagePath, PresentationProperties::THUMBNAIL_FILE)); self::assertEquals($imagePath, $object->getThumbnailPath()); + self::assertIsString($object->getThumbnail()); self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath()); self::assertEquals($imagePath, $object->getThumbnailPath()); + self::assertIsString($object->getThumbnail()); + } + + public function testThumbnailFileNotExisting(): void + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'NotExistingFile.png'; + + $object = new PresentationProperties(); + self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath($imagePath, PresentationProperties::THUMBNAIL_FILE)); + self::assertNull($object->getThumbnailPath()); + self::assertNull($object->getThumbnail()); + } + + public function testThumbnailFileData(): void + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'PhpPresentationLogo.png'; + + $object = new PresentationProperties(); + self::assertInstanceOf(PresentationProperties::class, $object->setThumbnailPath($imagePath, PresentationProperties::THUMBNAIL_DATA, file_get_contents($imagePath))); + self::assertEquals('', $object->getThumbnailPath()); + self::assertIsString($object->getThumbnail()); } public function testZoom(): void diff --git a/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php b/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php index 8d04038ef..51b6ac3bf 100644 --- a/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php +++ b/tests/PhpPresentation/Tests/Reader/PowerPoint2007Test.php @@ -101,6 +101,8 @@ public function testLoadFile01(): void self::assertEquals('Sample 02 Description', $oPhpPresentation->getDocumentProperties()->getDescription()); self::assertEquals('office 2007 openxml libreoffice odt php', $oPhpPresentation->getDocumentProperties()->getKeywords()); self::assertEquals('Sample Category', $oPhpPresentation->getDocumentProperties()->getCategory()); + self::assertEquals('', $oPhpPresentation->getDocumentProperties()->getRevision()); + self::assertEquals('', $oPhpPresentation->getDocumentProperties()->getStatus()); self::assertIsArray($oPhpPresentation->getDocumentProperties()->getCustomProperties()); self::assertCount(0, $oPhpPresentation->getDocumentProperties()->getCustomProperties()); diff --git a/tests/PhpPresentation/Tests/Shape/GroupTest.php b/tests/PhpPresentation/Tests/Shape/GroupTest.php index 93cbbf7d7..f5b0eefc5 100644 --- a/tests/PhpPresentation/Tests/Shape/GroupTest.php +++ b/tests/PhpPresentation/Tests/Shape/GroupTest.php @@ -38,7 +38,7 @@ public function testConstruct(): void self::assertEquals(0, $object->getOffsetY()); self::assertEquals(0, $object->getExtentX()); self::assertEquals(0, $object->getExtentY()); - self::assertEquals(0, $object->getShapeCollection()->count()); + self::assertCount(0, $object->getShapeCollection()); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Group', $object->setWidth(mt_rand(1, 100))); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Group', $object->setHeight(mt_rand(1, 100))); } @@ -52,7 +52,7 @@ public function testAdd(): void self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Line', $object->createLineShape(10, 10, 10, 10)); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText', $object->createRichTextShape()); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\Table', $object->createTableShape()); - self::assertEquals(5, $object->getShapeCollection()->count()); + self::assertCount(5, $object->getShapeCollection()); } public function testExtentX(): void diff --git a/tests/PhpPresentation/Tests/Shape/RichTextTest.php b/tests/PhpPresentation/Tests/Shape/RichTextTest.php index 4fd697574..ceefbe7d9 100644 --- a/tests/PhpPresentation/Tests/Shape/RichTextTest.php +++ b/tests/PhpPresentation/Tests/Shape/RichTextTest.php @@ -277,7 +277,7 @@ public function testHashCode(): void $object = new RichText(); $hash = $object->getActiveParagraph()->getHashCode(); - $hash .= RichText::WRAP_SQUARE . RichText::AUTOFIT_DEFAULT . RichText::OVERFLOW_OVERFLOW . RichText::OVERFLOW_OVERFLOW . '00104.89.69.64.8'; + $hash .= RichText::WRAP_SQUARE . RichText::AUTOFIT_DEFAULT . RichText::OVERFLOW_OVERFLOW . RichText::OVERFLOW_OVERFLOW . '00104.89.69.64.8t0'; $hash .= md5('00000' . $object->getFill()->getHashCode() . $object->getShadow()->getHashCode() . '' . get_parent_class($object)); $hash .= get_class($object); self::assertEquals(md5($hash), $object->getHashCode()); diff --git a/tests/PhpPresentation/Tests/Slide/AbstractSlideTest.php b/tests/PhpPresentation/Tests/Slide/AbstractSlideTest.php index d8d67a56f..bb93cc756 100644 --- a/tests/PhpPresentation/Tests/Slide/AbstractSlideTest.php +++ b/tests/PhpPresentation/Tests/Slide/AbstractSlideTest.php @@ -19,7 +19,9 @@ namespace PhpOffice\PhpPresentation\Tests\Slide; +use PhpOffice\PhpPresentation\Shape\Chart; use PhpOffice\PhpPresentation\Shape\RichText; +use PhpOffice\PhpPresentation\Shape\Table; use PhpOffice\PhpPresentation\Slide\AbstractSlide; use PHPUnit\Framework\TestCase; @@ -49,4 +51,36 @@ public function testCollection(): void self::assertIsArray($stub->getShapeCollection()); self::assertCount(count($array), $stub->getShapeCollection()); } + + public function testsearchShapes(): void + { + /** @var AbstractSlide $stub */ + $stub = $this->getMockForAbstractClass('PhpOffice\\PhpPresentation\\Slide\\AbstractSlide'); + + $array = [ + (new RichText())->setName('AAA'), + (new Table())->setName('BBB'), + (new Chart())->setName('AAA'), + ]; + self::assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\AbstractSlide', $stub->setShapeCollection($array)); + + // Search by Name + $result = $stub->searchShapes('AAA', null); + self::assertIsArray($result); + self::assertCount(2, $result); + self::assertInstanceOf(RichText::class, $result[0]); + self::assertInstanceOf(Chart::class, $result[1]); + + // Search by Name && Type + $result = $stub->searchShapes('AAA', Chart::class); + self::assertIsArray($result); + self::assertCount(1, $result); + self::assertInstanceOf(Chart::class, $result[0]); + + // Search by Type + $result = $stub->searchShapes(null, Table::class); + self::assertIsArray($result); + self::assertCount(1, $result); + self::assertInstanceOf(Table::class, $result[0]); + } } diff --git a/tests/PhpPresentation/Tests/Slide/NoteTest.php b/tests/PhpPresentation/Tests/Slide/NoteTest.php index 5cf4ce020..145b864a0 100644 --- a/tests/PhpPresentation/Tests/Slide/NoteTest.php +++ b/tests/PhpPresentation/Tests/Slide/NoteTest.php @@ -69,13 +69,13 @@ public function testOffset(): void public function testShape(): void { $object = new Note(); - self::assertEquals(0, $object->getShapeCollection()->count()); + self::assertCount(0, $object->getShapeCollection()); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText', $object->createRichTextShape()); - self::assertEquals(1, $object->getShapeCollection()->count()); + self::assertCount(1, $object->getShapeCollection()); $oRichText = new RichText(); - self::assertInstanceOf('PhpOffice\\PhpPresentation\\Shape\\RichText', $object->addShape($oRichText)); - self::assertEquals(2, $object->getShapeCollection()->count()); + self::assertInstanceOf(Note::class, $object->addShape($oRichText)); + self::assertCount(2, $object->getShapeCollection()); } /** diff --git a/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php b/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php index b3d23a42d..daea51b22 100644 --- a/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php +++ b/tests/PhpPresentation/Tests/Slide/SlideLayoutTest.php @@ -37,7 +37,7 @@ public function testBase(): void $object = new SlideLayout($mockSlideMaster); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\AbstractSlide', $object); - self::assertInstanceOf('\\ArrayObject', $object->getShapeCollection()); + self::assertIsArray($object->getShapeCollection()); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\ColorMap', $object->colorMap); } diff --git a/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php b/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php index e910ae79a..b79cc1c31 100644 --- a/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php +++ b/tests/PhpPresentation/Tests/Slide/SlideMasterTest.php @@ -38,7 +38,7 @@ public function testBase(): void $object = new SlideMaster(); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Slide\\AbstractSlide', $object); self::assertNull($object->getParent()); - self::assertInstanceOf('\\ArrayObject', $object->getShapeCollection()); + self::assertIsArray($object->getShapeCollection()); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\ColorMap', $object->colorMap); /** @var Color $background */ $background = $object->getBackground(); diff --git a/tests/PhpPresentation/Tests/Style/FontTest.php b/tests/PhpPresentation/Tests/Style/FontTest.php index 711c2da53..ae33838be 100644 --- a/tests/PhpPresentation/Tests/Style/FontTest.php +++ b/tests/PhpPresentation/Tests/Style/FontTest.php @@ -19,6 +19,7 @@ namespace PhpOffice\PhpPresentation\Tests\Style; +use PhpOffice\PhpPresentation\Exception\InvalidParameterException; use PhpOffice\PhpPresentation\Exception\NotAllowedValueException; use PhpOffice\PhpPresentation\Style\Color; use PhpOffice\PhpPresentation\Style\Font; @@ -49,6 +50,18 @@ public function testConstruct(): void self::assertEquals(Font::CAPITALIZATION_NONE, $object->getCapitalization()); self::assertInstanceOf('PhpOffice\\PhpPresentation\\Style\\Color', $object->getColor()); self::assertEquals(Color::COLOR_BLACK, $object->getColor()->getARGB()); + self::assertEquals(0, $object->getBaseline()); + } + + /** + * Test get/set Baseline. + */ + public function testBaseline(): void + { + $object = new Font(); + self::assertEquals(0, $object->getBaseline()); + self::assertInstanceOf(Font::class, $object->setBaseline(Font::BASELINE_SUBSCRIPT)); + self::assertEquals(Font::BASELINE_SUBSCRIPT, $object->getBaseline()); } /** @@ -76,6 +89,17 @@ public function testCapitalizationException(): void $object->setCapitalization('Invalid'); } + /** + * Test get/set charset. + */ + public function testCharset(): void + { + $object = new Font(); + self::assertEquals(Font::CHARSET_DEFAULT, $object->getCharset()); + self::assertInstanceOf(Font::class, $object->setCharset(12)); + self::assertEquals(12, $object->getCharset()); + } + /** * Test get/set Character Spacing. */ @@ -134,6 +158,52 @@ public function testName(): void self::assertEquals('Arial', $object->getName()); } + /** + * Test get/set panose. + */ + public function testPanose(): void + { + $object = new Font(); + self::assertEquals('', $object->getPanose()); + self::assertInstanceOf(Font::class, $object->setPanose('4494D72242')); + self::assertEquals('4494D72242', $object->getPanose()); + } + + /** + * Test get/set panose (Exception : Invalid Length). + */ + public function testPanoseExceptionInvalidLength(): void + { + $this->expectException(InvalidParameterException::class); + $this->expectExceptionMessage('The parameter pValue can\'t have the value "12345" (Validation: The length is not equals to 10)'); + + $object = new Font(); + $object->setPanose('12345'); + } + + /** + * Test get/set panose (Exception : Invalid Char). + */ + public function testPanoseExceptionInvalidChar(): void + { + $this->expectException(InvalidParameterException::class); + $this->expectExceptionMessage('The parameter pValue can\'t have the value "4494D7224X" (Validation: The character "X" is not allowed)'); + + $object = new Font(); + $object->setPanose('4494D7224X'); + } + + /** + * Test get/set pitch family. + */ + public function testPitchFamily(): void + { + $object = new Font(); + self::assertEquals(0, $object->getPitchFamily()); + self::assertInstanceOf(Font::class, $object->setPitchFamily(12)); + self::assertEquals(12, $object->getPitchFamily()); + } + /** * Test get/set size. */ @@ -197,10 +267,18 @@ public function testSetIsStriketrough(): void $object = new Font(); self::assertInstanceOf(Font::class, $object->setStrikethrough()); self::assertFalse($object->isStrikethrough()); + self::assertEquals(Font::STRIKE_NONE, $object->getStrikethrough()); + // boolean self::assertInstanceOf(Font::class, $object->setStrikethrough(false)); self::assertFalse($object->isStrikethrough()); + self::assertEquals(Font::STRIKE_NONE, $object->getStrikethrough()); self::assertInstanceOf(Font::class, $object->setStrikethrough(true)); self::assertTrue($object->isStrikethrough()); + self::assertEquals(Font::STRIKE_SINGLE, $object->getStrikethrough()); + // string + self::assertInstanceOf(Font::class, $object->setStrikethrough(Font::STRIKE_DOUBLE)); + self::assertTrue($object->isStrikethrough()); + self::assertEquals(Font::STRIKE_DOUBLE, $object->getStrikethrough()); } /** diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php index 3a87a2613..5c79a1ee8 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsCoreTest.php @@ -25,13 +25,6 @@ class DocPropsCoreTest extends PhpPresentationTestCase { protected $writerName = 'PowerPoint2007'; - public function testRender(): void - { - $this->assertZipFileExists('docProps/core.xml'); - $this->assertZipXmlElementNotExists('docProps/core.xml', '/cp:coreProperties/cp:contentStatus'); - $this->assertIsSchemaECMA376Valid(); - } - public function testDocumentProperties(): void { $expected = 'aAbBcDeE'; @@ -42,7 +35,9 @@ public function testDocumentProperties(): void ->setDescription($expected) ->setSubject($expected) ->setKeywords($expected) - ->setCategory($expected); + ->setCategory($expected) + ->setRevision($expected) + ->setStatus($expected); $this->assertZipFileExists('docProps/core.xml'); $this->assertZipXmlElementExists('docProps/core.xml', '/cp:coreProperties/dc:creator'); @@ -57,6 +52,10 @@ public function testDocumentProperties(): void $this->assertZipXmlElementEquals('docProps/core.xml', '/cp:coreProperties/cp:keywords', $expected); $this->assertZipXmlElementExists('docProps/core.xml', '/cp:coreProperties/cp:category'); $this->assertZipXmlElementEquals('docProps/core.xml', '/cp:coreProperties/cp:category', $expected); + $this->assertZipXmlElementExists('docProps/core.xml', '/cp:coreProperties/cp:revision'); + $this->assertZipXmlElementEquals('docProps/core.xml', '/cp:coreProperties/cp:revision', $expected); + $this->assertZipXmlElementExists('docProps/core.xml', '/cp:coreProperties/cp:contentStatus'); + $this->assertZipXmlElementEquals('docProps/core.xml', '/cp:coreProperties/cp:contentStatus', $expected); $this->assertIsSchemaECMA376Valid(); } @@ -73,7 +72,8 @@ public function testMarkAsFinalFalse(): void { $this->oPresentation->getPresentationProperties()->markAsFinal(false); - $this->assertZipXmlElementNotExists('docProps/core.xml', '/cp:coreProperties/cp:contentStatus'); + $this->assertZipXmlElementExists('docProps/core.xml', '/cp:coreProperties/cp:contentStatus'); + $this->assertZipXmlElementEquals('docProps/core.xml', '/cp:coreProperties/cp:contentStatus', ''); $this->assertIsSchemaECMA376Valid(); } } diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php index cd29a9733..ddab8a1cc 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/DocPropsThumbnailTest.php @@ -19,6 +19,7 @@ namespace PhpPresentation\Tests\Writer\PowerPoint2007; +use PhpOffice\PhpPresentation\PresentationProperties; use PhpOffice\PhpPresentation\Tests\PhpPresentationTestCase; /** @@ -31,15 +32,40 @@ class DocPropsThumbnailTest extends PhpPresentationTestCase public function testRender(): void { $this->assertZipFileNotExists('docProps/thumbnail.jpeg'); + $this->assertZipXmlElementNotExists('_rels/.rels', '/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]'); $this->assertIsSchemaECMA376Valid(); } - public function testFeatureThumbnail(): void + public function testFeatureThumbnailFile(): void { $imagePath = PHPPRESENTATION_TESTS_BASE_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'PhpPresentationLogo.png'; - $this->oPresentation->getPresentationProperties()->setThumbnailPath($imagePath); + $this->oPresentation->getPresentationProperties() + ->setThumbnailPath($imagePath, PresentationProperties::THUMBNAIL_FILE); $this->assertZipFileExists('docProps/thumbnail.jpeg'); + $this->assertZipXmlElementExists('_rels/.rels', '/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]'); + $this->assertIsSchemaECMA376Valid(); + } + + public function testFeatureThumbnailFileNotExisting(): void + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'NotExistingFile.png'; + + $this->oPresentation->getPresentationProperties() + ->setThumbnailPath($imagePath, PresentationProperties::THUMBNAIL_FILE); + $this->assertZipFileNotExists('docProps/thumbnail.jpeg'); + $this->assertZipXmlElementNotExists('_rels/.rels', '/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]'); + $this->assertIsSchemaECMA376Valid(); + } + + public function testFeatureThumbnailData(): void + { + $imagePath = PHPPRESENTATION_TESTS_BASE_DIR . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'images' . DIRECTORY_SEPARATOR . 'PhpPresentationLogo.png'; + + $this->oPresentation->getPresentationProperties() + ->setThumbnailPath('', PresentationProperties::THUMBNAIL_DATA, file_get_contents($imagePath)); + $this->assertZipFileExists('docProps/thumbnail.jpeg'); + $this->assertZipXmlElementExists('_rels/.rels', '/Relationships/Relationship[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]'); $this->assertIsSchemaECMA376Valid(); } } diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlideMastersTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlideMastersTest.php index c4374b6d1..8b57dc7b4 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlideMastersTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlideMastersTest.php @@ -62,7 +62,7 @@ public function testWriteSlideMasterRelationships(): void ->willReturn($layouts); /** @var ArrayObject $collection */ - $collection = new ArrayObject(); + $collection = array(); $collection[] = new ShapeDrawingFile(); $collection[] = new ShapeDrawingFile(); $collection[] = new ShapeDrawingFile(); diff --git a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php index 9e0640477..652ebd5b4 100644 --- a/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php +++ b/tests/PhpPresentation/Tests/Writer/PowerPoint2007/PptSlidesTest.php @@ -868,6 +868,53 @@ public function testRichTextHyperlink(): void $this->assertIsSchemaECMA376Valid(); } + public function testRichTextRunFontCharset(): void + { + $latinElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:latin'; + $eastAsianElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:ea'; + $complexScriptElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:cs'; + + $oSlide = $this->oPresentation->getActiveSlide(); + $oRichText = $oSlide->createRichTextShape(); + $oRun = $oRichText->createTextRun('MyText'); + $oRun->getFont()->setCharset(18); + + $oRun->getFont()->setFormat(Font::FORMAT_LATIN); + + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $latinElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $latinElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $latinElement, 'charset'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $latinElement, 'charset', '12'); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertIsSchemaECMA376Valid(); + + $oRun->getFont()->setFormat(Font::FORMAT_EAST_ASIAN); + $this->resetPresentationFile(); + + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $eastAsianElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $eastAsianElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $eastAsianElement, 'charset'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $eastAsianElement, 'charset', '12'); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertIsSchemaECMA376Valid(); + + $oRun->getFont()->setFormat(Font::FORMAT_COMPLEX_SCRIPT); + $this->resetPresentationFile(); + + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $complexScriptElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $complexScriptElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $complexScriptElement, 'charset'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $complexScriptElement, 'charset', '12'); + $this->assertIsSchemaECMA376Valid(); + } + public function testRichTextRunFontFormat(): void { $latinElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:latin'; @@ -907,6 +954,100 @@ public function testRichTextRunFontFormat(): void $this->assertIsSchemaECMA376Valid(); } + public function testRichTextRunFontPanose(): void + { + $latinElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:latin'; + $eastAsianElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:ea'; + $complexScriptElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:cs'; + + $oSlide = $this->oPresentation->getActiveSlide(); + $oRichText = $oSlide->createRichTextShape(); + $oRun = $oRichText->createTextRun('MyText'); + $oRun->getFont()->setPanose('4494D72242'); + + $oRun->getFont()->setFormat(Font::FORMAT_LATIN); + + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $latinElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $latinElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $latinElement, 'panose'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $latinElement, 'panose', '040409040D0702020402'); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertIsSchemaECMA376Valid(); + + $oRun->getFont()->setFormat(Font::FORMAT_EAST_ASIAN); + $this->resetPresentationFile(); + + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $eastAsianElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $eastAsianElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $eastAsianElement, 'panose'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $eastAsianElement, 'panose', '040409040D0702020402'); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertIsSchemaECMA376Valid(); + + $oRun->getFont()->setFormat(Font::FORMAT_COMPLEX_SCRIPT); + $this->resetPresentationFile(); + + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $complexScriptElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $complexScriptElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $complexScriptElement, 'panose'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $complexScriptElement, 'panose', '040409040D0702020402'); + $this->assertIsSchemaECMA376Valid(); + } + + public function testRichTextRunFontPitchFamily(): void + { + $latinElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:latin'; + $eastAsianElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:ea'; + $complexScriptElement = '/p:sld/p:cSld/p:spTree/p:sp/p:txBody/a:p/a:r/a:rPr/a:cs'; + + $oSlide = $this->oPresentation->getActiveSlide(); + $oRichText = $oSlide->createRichTextShape(); + $oRun = $oRichText->createTextRun('MyText'); + $oRun->getFont()->setPitchFamily(42); + + $oRun->getFont()->setFormat(Font::FORMAT_LATIN); + + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $latinElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $latinElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $latinElement, 'pitchFamily'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $latinElement, 'pitchFamily', '42'); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertIsSchemaECMA376Valid(); + + $oRun->getFont()->setFormat(Font::FORMAT_EAST_ASIAN); + $this->resetPresentationFile(); + + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $eastAsianElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $eastAsianElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $eastAsianElement, 'pitchFamily'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $eastAsianElement, 'pitchFamily', '42'); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertIsSchemaECMA376Valid(); + + $oRun->getFont()->setFormat(Font::FORMAT_COMPLEX_SCRIPT); + $this->resetPresentationFile(); + + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $latinElement); + $this->assertZipXmlElementNotExists('ppt/slides/slide1.xml', $eastAsianElement); + $this->assertZipXmlElementExists('ppt/slides/slide1.xml', $complexScriptElement); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $complexScriptElement, 'typeface'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $complexScriptElement, 'typeface', 'Calibri'); + $this->assertZipXmlAttributeExists('ppt/slides/slide1.xml', $complexScriptElement, 'pitchFamily'); + $this->assertZipXmlAttributeEquals('ppt/slides/slide1.xml', $complexScriptElement, 'pitchFamily', '42'); + $this->assertIsSchemaECMA376Valid(); + } + public function testRichTextRunLanguage(): void { $oSlide = $this->oPresentation->getActiveSlide(); diff --git a/tests/PhpPresentation/Tests/_includes/PhpPresentationTestCase.php b/tests/PhpPresentation/Tests/_includes/PhpPresentationTestCase.php index 0fc395d3a..120ffe0c9 100644 --- a/tests/PhpPresentation/Tests/_includes/PhpPresentationTestCase.php +++ b/tests/PhpPresentation/Tests/_includes/PhpPresentationTestCase.php @@ -320,6 +320,18 @@ public function assertZipXmlElementEquals($filePath, $xPath, $value): void self::assertEquals($nodeList->item(0)->nodeValue, $value); } + /** + * @param string $filePath + * @param string $xPath + * @param mixed $value + */ + public function assertZipXmlElementNotEquals($filePath, $xPath, $value): void + { + $this->writePresentationFile($this->oPresentation, $this->writerName); + $nodeList = $this->getXmlNodeList($filePath, $xPath); + self::assertNotEquals($nodeList->item(0)->nodeValue, $value); + } + /** * @param mixed $value */ diff --git a/tests/resources/files/serialized.phppt b/tests/resources/files/serialized.phppt index 828ad826e1d9b31608d88ea1e331f153903e5d42..5944909f1464f27e9e1a9eb7cf26efd07df75edd 100644 GIT binary patch delta 6974 zcmY+JS5(sr)a5}G6a>6i0jW_z0jbhLkBW#$6F~z42}MPE?~p%5nluHGPC%Ln0-@K? zq?ds79t=G|LPK53XRx1>U!v|QqH=~5|OiQN?AKczN! z%n9VAuTnjeb$s0!pLbnF%uM>dhDpl53RV^p<|?O)hi-tbiP;JZoza$i*@Q2*7tEAW zR#U6RTx1s4;GiVd^Jf-kE3^3Mskvw2mQpDK8siv~hr_#1>}lk1V>>b_okPq-Xp&}F zie@rsi$9YoLC)^Zu5#RTbl%8=m=6{d`Qm>VITPx`mv~fc<$_T|`J#JfTAzX$%M(IW zU)#lI*8K^KW3WPX7U1#_IO2SPS?VyMxgntW%-<-zh#RZ7HbSc%zx1kxY)=Vo4B zh)X!QmJx~2V({&6=dTf2=r|=7z-4Wn9U~rP#5O?_tK6EHU3yA-%ZeRa>?cSF@27@C zC9G>AMbhtOKGyg<(t=Q}Nt!^Y$06rf+?4Y#3&6qERs89|`sCzh@pVx5TSe^sfx?kh zKC$VO7$wuZQeFe((sa1zn}t@CFEM_`1l+ycE`sy-e%DN4T~XMi>64m&n* zqX9UcrDr6wsusr+Th}Y40vqnTMZ%hybQb5naZmq=&2yrsZM;y)r=;<337plNfpH?t zmeg8w;Ke5T#$xTa%QmMv0;)Mt6w0z%pSfVJ8596ha|BvC=|3bkrElpKI76j zp@${hrszIK(l18SmsP|4P>Mw+|)LQ12 zW|duVr*9Ni9yZgCg59|3*twI&t5RlQ;ed?Su>1FA`gC44%a)hu(H9(>3KsmU3$;+to)9z9*%a=qT$N=f{ECW7Ijkj+rBc!!C5sPvU{LAdazE*Dq^Z>-hfF{HfOq>WU0Dhk}z-bH4Q#fP6ygn=xvb7$rb@RlHBSO;)_+f8m&4! zTP~n~auz?fVfDOfw8ZLbjht1C(n%7tv9y;$jxG@msH|<#Q$~!iQcYZ*p8wlM)!T%$ z+4kD4=#1{+dBGatK?C=vMr>{$)NBca>C@5rqGi?XJP8LG2-8_%_`jDH`Yx#+E`{j( z`D)vnwQt{9AWD1oBS3&_j18i5-2j%H;!+44o;ubA~y&#ZV&7DX19GMF~f--HL(0RwE}r zXDcKzgo2`G@|>h!Zo2ekmU@X~PrHv^Cp+}K{4>q&$uJEljd$LO-+{ce_&Re?Y!P-- zK|z$Ao4ZT~X*_Cx6-xyNlZ0$5{)z1KQp|5g()6g$D-Hr8dSVflgh_Ru=dB*xG66Y8 zkJest`-f{~=?KV!7*^_*aHHf)%@U;&ZcqJuWx|+_1l5I3U~+|Du8%i89YVdWNSU2~ zN|o%lj1&ektw|t`(JLV?m(1w+Z?L}~oKmhpeGA5~R=r|^D`wa{uj4<;tB~wxu`H5> zXB~~Yfjz_Ntf2>8c4K@~FPa~vq@LC~i6Z%a%pG-0dka5AwY-@nLk5C*nZ>c!p?lKH}J{AQTMdrZ0 z<3sS%S|VC$E}~_rXt2Joe2s>9b|2oK&CZ7*D7E=Lc^_g-p(yb>R%Qkpd{ab;dub5C zY#Ko@(5C|}6}zrVOTH1d-l~_4=eiM0MrPCE4{Z-%rSA_QThp84QsXG0*3SuCq6k(5 zur($3mvFpXS+*g{_Q0T~NG?n3z!9+#^FUyfi|QGeGZVataKctgxu(z*s@vCAtY$Xg zxIkuf8(`BuPe+{w?VkJc^;i?Un1pE5|NgYA{(z0lXxcGEV0WARv=P(w5qnj^k%zzd%|BL={`JCS6c4p=*V_ z#b>pJuW|QvdCd2*;Z!<2t^rCcxc-f6HMxRW@=Ai2IdM)F{v~cX6r@@xGyLj1*HLao zgqI`&7vaz0`y23Y4~!L*1iODn_xjrkM^@g7q-NbYVbU~i$RK|NWY1e!)ZK`WSAJ^m z<5{MD4#cmzHXOrSQeLS70ybgET&&op%`4_g%}nM___4F;_`hxSOl-3-J)CMt-mwwN z_^L<8)b2As1(l|GboYc`nYAw$T7LG1Mm^`f=!`+CtZJ3q12)3n>|RUey?Yxg+GEkY zAJ?UsB4m6zRqu`itU@Udcj$e@vgG-iX8f(tay3I9mq-VL3G2G=CwMov9shB;=F{ve ze^+_VsKxZ@44==nx^U;7H2>Gt@BOU%BflHkBNS$z#4d;!5JXE64K>W8NgC^4N7NKa zud@ZKKc-&&>mc9LbRWv5e^J4vwS&*9(WfZNTS+xnufi2H0rd9$Fmw-MCaPx5DekV> zD;QT6>2%-nAn6Rr-#U(=-$?T}z6>RN&6AoopPGFVBG;ul*5PDJTZ`!tRm_+-; z?8Iix4-8MUT&KWI&TOu=n(6-!*9%t}r!#rxiWV1sFwKV4z*Pk0GYp)ivIX~r@+-~w zYr7rAVl*_rU*`&&Xu+0z(0E_Gw6yf~aDWB-ub-71u&=WsfwibL_!(7q@>32PM^cG@ zcyLW5Z+5;=`JtnXI`ZpB7RBp@$L3CX#@pSlmlTqEGCfC%aRPTY?C!>4s;2o?+ZaE8!j0hMYU4tQ2(>kjUjx_ol^OQ3XXYA}mk`5%W)55GRqHUA!NjI{(# zvIy#q)F5+H3_h5r8^?I@GWk@`{`G*V3}{6@Du@38)T56tz2VKtuNYknbJn-{jknqA z2;|&1(K0*ULv??QLtO-RidSeGeK#M1+EL!e=9(@4q94He?3FVhuQA+}=0kxXB^G%Q zQ0|sI>69yVttB{6Vm|xLT{iKL4$tEjv;}lqnb;WTa*CW}z-%O6@Nax1N)ek5Xk7^vYt9K6ca8J4IYT$17jLW0U& zg1pr)Q|8hfr*wu;Qx|xDir3UPx%~>F0z&HJ13g*w6y%(+NJfUvLy%UfcUDPaUL~%3 zw(r2XuQ^-gk2K_Fq1VVogVo8Wo{&Aw1DT&G=E#u+RD;?(TikSZ7!$7GLE%eDNM|1V zT8}up_Z7<4!K?K{R{yYMmgc|Y3Yv(?`x-U3Acu

o`LG+hGZ4ou}6V&m6%R2u^7S z%T}HgtSpjr#0%#anh?=zWxeQfIyVSGb)7P$Qrv1R)B35=Cz$l{3n;70FnPcBW;xM_ z^ZH&jC*#rk*Z=uKS7d2mc<{q~`c6zzORR$faoBo^d-u`j=J3U4L-q>QHaGPZRXsO> zdKt%B%6@;gw%5^}+0TfrB@9rXNTb}HH_uiwbRLie9Skfl-EAFAi5^9OF`f1*COcbn ztCoz?ug(^(kIxN@x9S(&o;0?Jz3Py8!t%?j(Lnxx!yP)7iMg$I`uN}u6kiUBUGHl^ zix(x>HXW=t1amI$siq7qLDprXGV9$G_skxXV2Zkbyo$edemU6!Y-skte>^`IghO{G zs_*V*ERApQ-kABgQteGb_*NU(e|bLs)l1*q z$W$WtJM`15B+K!Rsy~@F51Lf1N#=d`ai)i+Et#;sAoYCY=X$<-YnHEV4z==k zPfRdBTxq45=Rn{amzB>R_-hQM31oqdB=YBxo%#5Xb>@f5KTa`X4)Y$-**|s#ncD-d>rsNOH<*B+yIC4;rHh%_ZGk*X-ul=Z4KM ztT@tU7M=T!N~Y2u>mnx~3Y^N%i^tfp8vhQ7OLUBZ`?dI~tFMUt&WiSE#F$8er;Hwg zcsCc}$$S%6|AroGtN$>Exo~_AN)I%+@Jhr~>(ZG^q1VF#{H?J-f&c#e2YIzQv)&1@ zA2sftfb=MnO1@u3sB%=0_Q{{d{4ce?W!2-Q`^fttJznWP&pk}t`!ab;@Avw|xR^9g4*k^|T!t*JL1k{{ zIB>jSe;P=BF3)M(qO8AKOvZfvjzF)}!>+Gz>;#Q`V+Km(6!z-Bb(XVn5#Mm?J1L&iW=gpiF2FF$4it=-_>m~QnceewAE zs=dMR#p?mt5W?x%>tn5?2W7)r5Df*JN)BY(s+et8;FgaO zCMDjxLW_P)G*IHb+WW&kQ@B%jCZ?^06(HMV)K&1*H`C>g*-I2{n}Ku(yUYRdImfK6 z)J{9oi6rzCISroBDw(9io1M?Lq4Yp(*TwI*T35D?^!W;AC;$oJ)$b*d88)VP(TllIgv|vGb`vS7P zAwt}Vm#=cs89a5u>ljGHL($iUHvF*axYLXP{+z7r0QpwCo`A&!Mmm){l+=75t9i$< zKk~b8Jbdz9YhSTvm4XqXGlOdznUib3o(1V~r>PF_0o7U-vrLFlDd7aIdU&4det{om zmSA4ox$7wcHS_!$lUJ^_`}${EzErH3T}|RTQHaO{Hdex zhLfWh5LKzfk0~os`xrww_1{4#*tL3nM8;E|pae$p*;BN4Q61{>isgs2^dL?q3C1ry zSXHwvC4~1jn&&6QYR4vZ%#}Pw23s|f#2NmU1g-oo#P)Pg5?E_jvMaKiCDPYj~(LuW!UVFnSw@>lX}#od8f5 zw=WH5hZvrWbCrefNg3GRe1<4=)Kj0dx#ym&Sk>*%af1}!vjo~C*zJm%D5-_oHiJVa zWCmyI$3&nknH5!n1k_qU>9K$Vx+ner$q5sqzys7;uC=)$WYf+9%?`X!@9sl_-cnLn zr)LPvsIjRj4dNO?f*Yi>P{S@g6ZCO)n3vD*qY9ZnwJPA*5JRlIt($8Sa0R<0*{A@OZCLM;z9^rrf=q6r z)S5OicMPf1fW^Apxz-oDQq(6?j`<((b#h?qDV&bW?iv$t>*1QdB z{CA4VC&^ur=Xi<#)B3T^uL;*7^ipUQ1gh22X(yTz8AM<|T?CO^AmZIXf!)RZQ29nd zVdWMxkrW-~flfBp#TlilM}HFri-CZa->c}P@l}T6+YRE-a1{MW14nH2FHWubUp~2_?DODJSEJ#{%gaS-Pnc}^&C2=u+L?FT*Upu zSG6$Ayf2GV9oUM&G;nQ_oewAXu zsNZnLUJ8#%S37|+Eqv*QO|21o(+8;|C00_3#4fJ8uAPfLYW5}smQFnyPrs zR!(QciG!(azNo?Bkev87dB8M^K^a2jQbuQe9rzthyDVD;RfC1{~LxS4TGPt{r_QD Ol8_>pkqrKC<9`7zn|dw) delta 6917 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 LHwL~p00000Ruxc7