Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
73.35% covered (warning)
73.35%
589 / 803
37.93% covered (danger)
37.93%
11 / 29
CRAP
0.00% covered (danger)
0.00%
0 / 1
PowerPoint2007
73.35% covered (warning)
73.35%
589 / 803
37.93% covered (danger)
37.93%
11 / 29
2514.17
0.00% covered (danger)
0.00%
0 / 1
 canRead
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 fileSupportsUnserializePhpPresentation
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 load
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 loadFile
100.00% covered (success)
100.00%
26 / 26
100.00% covered (success)
100.00%
1 / 1
7
 loadDocumentLayout
69.23% covered (warning)
69.23%
9 / 13
0.00% covered (danger)
0.00%
0 / 1
7.05
 loadDocumentProperties
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
6
 loadThumbnailProperties
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
2
 loadCustomProperties
70.97% covered (warning)
70.97%
22 / 31
0.00% covered (danger)
0.00%
0 / 1
15.52
 loadPresentationProperties
20.00% covered (danger)
20.00%
4 / 20
0.00% covered (danger)
0.00%
0 / 1
32.09
 loadViewProperties
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 loadSlides
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
9.01
 loadMasterSlides
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
6.03
 loadSlide
32.65% covered (danger)
32.65%
16 / 49
0.00% covered (danger)
0.00%
0 / 1
73.87
 loadMasterSlide
97.65% covered (success)
97.65%
83 / 85
0.00% covered (danger)
0.00%
0 / 1
33
 loadLayoutSlide
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
8
 loadTheme
100.00% covered (success)
100.00%
14 / 14
100.00% covered (success)
100.00%
1 / 1
7
 loadSlideBackground
83.33% covered (warning)
83.33%
25 / 30
0.00% covered (danger)
0.00%
0 / 1
9.37
 loadSlideNote
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 loadShapeDrawing
95.16% covered (success)
95.16%
59 / 62
0.00% covered (danger)
0.00%
0 / 1
25
 loadShadow
77.78% covered (warning)
77.78%
21 / 27
0.00% covered (danger)
0.00%
0 / 1
16.15
 loadShapeRichText
100.00% covered (success)
100.00%
55 / 55
100.00% covered (success)
100.00%
1 / 1
25
 loadShapeTable
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 1
1482
 loadParagraph
80.17% covered (warning)
80.17%
97 / 121
0.00% covered (danger)
0.00%
0 / 1
76.75
 loadHyperlink
75.00% covered (warning)
75.00%
6 / 8
0.00% covered (danger)
0.00%
0 / 1
7.77
 loadStyleBorder
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
72
 loadStyleColor
71.43% covered (warning)
71.43%
5 / 7
0.00% covered (danger)
0.00%
0 / 1
3.21
 loadStyleFill
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
10
 loadRels
90.91% covered (success)
90.91%
10 / 11
0.00% covered (danger)
0.00%
0 / 1
5.02
 loadSlideShapes
73.33% covered (warning)
73.33%
11 / 15
0.00% covered (danger)
0.00%
0 / 1
10.54
1<?php
2/**
3 * This file is part of PHPPresentation - A pure PHP library for reading and writing
4 * presentations documents.
5 *
6 * PHPPresentation is free software distributed under the terms of the GNU Lesser
7 * General Public License version 3 as published by the Free Software Foundation.
8 *
9 * For the full copyright and license information, please read the LICENSE
10 * file that was distributed with this source code. For the full list of
11 * contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
12 *
13 * @see        https://github.com/PHPOffice/PHPPresentation
14 *
15 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16 */
17
18declare(strict_types=1);
19
20namespace PhpOffice\PhpPresentation\Reader;
21
22use DateTime;
23use DOMElement;
24use DOMNode;
25use DOMNodeList;
26use PhpOffice\Common\Drawing as CommonDrawing;
27use PhpOffice\Common\XMLReader;
28use PhpOffice\PhpPresentation\DocumentLayout;
29use PhpOffice\PhpPresentation\DocumentProperties;
30use PhpOffice\PhpPresentation\Exception\FeatureNotImplementedException;
31use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
32use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
33use PhpOffice\PhpPresentation\PhpPresentation;
34use PhpOffice\PhpPresentation\PresentationProperties;
35use PhpOffice\PhpPresentation\Shape\Drawing\Base64;
36use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
37use PhpOffice\PhpPresentation\Shape\Hyperlink;
38use PhpOffice\PhpPresentation\Shape\Placeholder;
39use PhpOffice\PhpPresentation\Shape\RichText;
40use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
41use PhpOffice\PhpPresentation\Shape\Table\Cell;
42use PhpOffice\PhpPresentation\Slide;
43use PhpOffice\PhpPresentation\Slide\AbstractSlide;
44use PhpOffice\PhpPresentation\Slide\Note;
45use PhpOffice\PhpPresentation\Slide\SlideLayout;
46use PhpOffice\PhpPresentation\Slide\SlideMaster;
47use PhpOffice\PhpPresentation\Style\Border;
48use PhpOffice\PhpPresentation\Style\Borders;
49use PhpOffice\PhpPresentation\Style\Bullet;
50use PhpOffice\PhpPresentation\Style\Color;
51use PhpOffice\PhpPresentation\Style\Fill;
52use PhpOffice\PhpPresentation\Style\Font;
53use PhpOffice\PhpPresentation\Style\SchemeColor;
54use PhpOffice\PhpPresentation\Style\Shadow;
55use PhpOffice\PhpPresentation\Style\TextStyle;
56use ZipArchive;
57
58/**
59 * Serialized format reader.
60 */
61class PowerPoint2007 implements ReaderInterface
62{
63    /**
64     * Output Object.
65     *
66     * @var PhpPresentation
67     */
68    protected $oPhpPresentation;
69
70    /**
71     * Output Object.
72     *
73     * @var ZipArchive
74     */
75    protected $oZip;
76
77    /**
78     * @var array<string, array<string, array<string, string>>>
79     */
80    protected $arrayRels = [];
81
82    /**
83     * @var SlideLayout[]
84     */
85    protected $arraySlideLayouts = [];
86
87    /**
88     * @var string
89     */
90    protected $filename;
91
92    /**
93     * @var string
94     */
95    protected $fileRels;
96
97    /**
98     * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
99     */
100    public function canRead(string $pFilename): bool
101    {
102        return $this->fileSupportsUnserializePhpPresentation($pFilename);
103    }
104
105    /**
106     * Does a file support UnserializePhpPresentation ?
107     */
108    public function fileSupportsUnserializePhpPresentation(string $pFilename = ''): bool
109    {
110        // Check if file exists
111        if (!file_exists($pFilename)) {
112            throw new FileNotFoundException($pFilename);
113        }
114
115        $oZip = new ZipArchive();
116        // Is it a zip ?
117        if (true === $oZip->open($pFilename)) {
118            // Is it an OpenXML Document ?
119            // Is it a Presentation ?
120            if (is_array($oZip->statName('[Content_Types].xml')) && is_array($oZip->statName('ppt/presentation.xml'))) {
121                return true;
122            }
123        }
124
125        return false;
126    }
127
128    /**
129     * Loads PhpPresentation Serialized file.
130     */
131    public function load(string $pFilename): PhpPresentation
132    {
133        // Unserialize... First make sure the file supports it!
134        if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
135            throw new InvalidFileFormatException($pFilename, self::class);
136        }
137
138        return $this->loadFile($pFilename);
139    }
140
141    /**
142     * Load PhpPresentation Serialized file.
143     */
144    protected function loadFile(string $pFilename): PhpPresentation
145    {
146        $this->oPhpPresentation = new PhpPresentation();
147        $this->oPhpPresentation->removeSlideByIndex();
148        $this->oPhpPresentation->setAllMasterSlides([]);
149        $this->filename = $pFilename;
150
151        $this->oZip = new ZipArchive();
152        $this->oZip->open($this->filename);
153        $docPropsCore = $this->oZip->getFromName('docProps/core.xml');
154        if (false !== $docPropsCore) {
155            $this->loadDocumentProperties($docPropsCore);
156        }
157
158        $docThumbnail = $this->oZip->getFromName('_rels/.rels');
159        if ($docThumbnail !== false) {
160            $this->loadThumbnailProperties($docThumbnail);
161        }
162
163        $docPropsCustom = $this->oZip->getFromName('docProps/custom.xml');
164        if (false !== $docPropsCustom) {
165            $this->loadCustomProperties($docPropsCustom);
166        }
167
168        $pptViewProps = $this->oZip->getFromName('ppt/viewProps.xml');
169        if (false !== $pptViewProps) {
170            $this->loadViewProperties($pptViewProps);
171        }
172
173        $pptPresentation = $this->oZip->getFromName('ppt/presentation.xml');
174        if (false !== $pptPresentation) {
175            $this->loadDocumentLayout($pptPresentation);
176            $this->loadSlides($pptPresentation);
177        }
178
179        $pptPresProps = $this->oZip->getFromName('ppt/presProps.xml');
180        if (false !== $pptPresProps) {
181            $this->loadPresentationProperties($pptPresentation);
182        }
183
184        return $this->oPhpPresentation;
185    }
186
187    /**
188     * Read Document Layout.
189     */
190    protected function loadDocumentLayout(string $sPart): void
191    {
192        $xmlReader = new XMLReader();
193        // @phpstan-ignore-next-line
194        if ($xmlReader->getDomFromString($sPart)) {
195            foreach ($xmlReader->getElements('/p:presentation/p:sldSz') as $oElement) {
196                if (!($oElement instanceof DOMElement)) {
197                    continue;
198                }
199                $type = $oElement->getAttribute('type');
200                $oLayout = $this->oPhpPresentation->getLayout();
201                if (DocumentLayout::LAYOUT_CUSTOM == $type) {
202                    $oLayout->setCX((float) $oElement->getAttribute('cx'));
203                    $oLayout->setCY((float) $oElement->getAttribute('cy'));
204                } else {
205                    $oLayout->setDocumentLayout($type, true);
206                    if ($oElement->getAttribute('cx') < $oElement->getAttribute('cy')) {
207                        $oLayout->setDocumentLayout($type, false);
208                    }
209                }
210            }
211        }
212    }
213
214    /**
215     * Read Document Properties.
216     */
217    protected function loadDocumentProperties(string $sPart): void
218    {
219        $xmlReader = new XMLReader();
220        // @phpstan-ignore-next-line
221        if ($xmlReader->getDomFromString($sPart)) {
222            $arrayProperties = [
223                '/cp:coreProperties/dc:creator' => 'setCreator',
224                '/cp:coreProperties/cp:lastModifiedBy' => 'setLastModifiedBy',
225                '/cp:coreProperties/dc:title' => 'setTitle',
226                '/cp:coreProperties/dc:description' => 'setDescription',
227                '/cp:coreProperties/dc:subject' => 'setSubject',
228                '/cp:coreProperties/cp:keywords' => 'setKeywords',
229                '/cp:coreProperties/cp:category' => 'setCategory',
230                '/cp:coreProperties/dcterms:created' => 'setCreated',
231                '/cp:coreProperties/dcterms:modified' => 'setModified',
232                '/cp:coreProperties/cp:revision' => 'setRevision',
233                '/cp:coreProperties/cp:contentStatus' => 'setStatus',
234            ];
235            $oProperties = $this->oPhpPresentation->getDocumentProperties();
236            foreach ($arrayProperties as $path => $property) {
237                $oElement = $xmlReader->getElement($path);
238                if ($oElement instanceof DOMElement) {
239                    if ($oElement->hasAttribute('xsi:type') && 'dcterms:W3CDTF' == $oElement->getAttribute('xsi:type')) {
240                        $dateTime = DateTime::createFromFormat(DateTime::W3C, $oElement->nodeValue);
241                        $oProperties->{$property}($dateTime->getTimestamp());
242                    } else {
243                        $oProperties->{$property}($oElement->nodeValue);
244                    }
245                }
246            }
247        }
248    }
249
250    /**
251     * Read information of the document thumbnail.
252     */
253    protected function loadThumbnailProperties(string $sPart): void
254    {
255        $xmlReader = new XMLReader();
256        $xmlReader->getDomFromString($sPart);
257
258        $oElement = $xmlReader->getElement('*[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]');
259        if ($oElement instanceof DOMElement) {
260            $path = $oElement->getAttribute('Target');
261            $this->oPhpPresentation
262                ->getPresentationProperties()
263                ->setThumbnailPath('', PresentationProperties::THUMBNAIL_DATA, $this->oZip->getFromName($path));
264        }
265    }
266
267    /**
268     * Read Custom Properties.
269     */
270    protected function loadCustomProperties(string $sPart): void
271    {
272        $xmlReader = new XMLReader();
273        $sPart = str_replace(' xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"', '', $sPart);
274        // @phpstan-ignore-next-line
275        if ($xmlReader->getDomFromString($sPart)) {
276            foreach ($xmlReader->getElements('/Properties/property[@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"]') as $element) {
277                if (!$element->hasAttribute('name')) {
278                    continue;
279                }
280                $propertyName = $element->getAttribute('name');
281                if ($propertyName == '_MarkAsFinal') {
282                    $attributeElement = $xmlReader->getElement('vt:bool', $element);
283                    if ($attributeElement && 'true' == $attributeElement->nodeValue) {
284                        $this->oPhpPresentation->getPresentationProperties()->markAsFinal(true);
285                    }
286                } else {
287                    $attributeTypeInt = $xmlReader->getElement('vt:i4', $element);
288                    $attributeTypeFloat = $xmlReader->getElement('vt:r8', $element);
289                    $attributeTypeBoolean = $xmlReader->getElement('vt:bool', $element);
290                    $attributeTypeDate = $xmlReader->getElement('vt:filetime', $element);
291                    $attributeTypeString = $xmlReader->getElement('vt:lpwstr', $element);
292
293                    if ($attributeTypeInt) {
294                        $propertyType = DocumentProperties::PROPERTY_TYPE_INTEGER;
295                        $propertyValue = (int) $attributeTypeInt->nodeValue;
296                    } elseif ($attributeTypeFloat) {
297                        $propertyType = DocumentProperties::PROPERTY_TYPE_FLOAT;
298                        $propertyValue = (float) $attributeTypeFloat->nodeValue;
299                    } elseif ($attributeTypeBoolean) {
300                        $propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
301                        $propertyValue = $attributeTypeBoolean->nodeValue == 'true' ? true : false;
302                    } elseif ($attributeTypeDate) {
303                        $propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
304                        $propertyValue = strtotime($attributeTypeDate->nodeValue);
305                    } else {
306                        $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
307                        $propertyValue = $attributeTypeString->nodeValue;
308                    }
309
310                    $this->oPhpPresentation->getDocumentProperties()->setCustomProperty($propertyName, $propertyValue, $propertyType);
311                }
312            }
313        }
314    }
315
316    /**
317     * Read Presentation Properties.
318     */
319    protected function loadPresentationProperties(string $sPart): void
320    {
321        $xmlReader = new XMLReader();
322        // @phpstan-ignore-next-line
323        if ($xmlReader->getDomFromString($sPart)) {
324            $element = $xmlReader->getElement('/p:presentationPr/p:showPr');
325            if ($element instanceof DOMElement) {
326                if ($element->hasAttribute('loop')) {
327                    $this->oPhpPresentation->getPresentationProperties()->setLoopContinuouslyUntilEsc(
328                        (bool) $element->getAttribute('loop')
329                    );
330                }
331                if (null !== $xmlReader->getElement('p:present', $element)) {
332                    $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
333                        PresentationProperties::SLIDESHOW_TYPE_PRESENT
334                    );
335                }
336                if (null !== $xmlReader->getElement('p:browse', $element)) {
337                    $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
338                        PresentationProperties::SLIDESHOW_TYPE_BROWSE
339                    );
340                }
341                if (null !== $xmlReader->getElement('p:kiosk', $element)) {
342                    $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
343                        PresentationProperties::SLIDESHOW_TYPE_KIOSK
344                    );
345                }
346            }
347        }
348    }
349
350    /**
351     * Read View Properties.
352     */
353    protected function loadViewProperties(string $sPart): void
354    {
355        $xmlReader = new XMLReader();
356        // @phpstan-ignore-next-line
357        if ($xmlReader->getDomFromString($sPart)) {
358            $pathZoom = '/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sx';
359            $oElement = $xmlReader->getElement($pathZoom);
360            if ($oElement instanceof DOMElement) {
361                if ($oElement->hasAttribute('d') && $oElement->hasAttribute('n')) {
362                    $this->oPhpPresentation->getPresentationProperties()->setZoom((int) $oElement->getAttribute('n') / (int) $oElement->getAttribute('d'));
363                }
364            }
365        }
366    }
367
368    /**
369     * Extract all slides.
370     */
371    protected function loadSlides(string $sPart): void
372    {
373        $xmlReader = new XMLReader();
374        // @phpstan-ignore-next-line
375        if ($xmlReader->getDomFromString($sPart)) {
376            $fileRels = 'ppt/_rels/presentation.xml.rels';
377            $this->loadRels($fileRels);
378            // Load the Masterslides
379            $this->loadMasterSlides($xmlReader, $fileRels);
380            // Continue with loading the slides
381            foreach ($xmlReader->getElements('/p:presentation/p:sldIdLst/p:sldId') as $oElement) {
382                if (!($oElement instanceof DOMElement)) {
383                    continue;
384                }
385                $rId = $oElement->getAttribute('r:id');
386                $pathSlide = isset($this->arrayRels[$fileRels][$rId]) ? $this->arrayRels[$fileRels][$rId]['Target'] : '';
387                if (!empty($pathSlide)) {
388                    $pptSlide = $this->oZip->getFromName('ppt/' . $pathSlide);
389                    if (false !== $pptSlide) {
390                        $slideRels = 'ppt/slides/_rels/' . basename($pathSlide) . '.rels';
391                        $this->loadRels($slideRels);
392                        $this->loadSlide($pptSlide, basename($pathSlide));
393                        foreach ($this->arrayRels[$slideRels] as $rel) {
394                            if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide' == $rel['Type']) {
395                                $this->loadSlideNote(basename($rel['Target']), $this->oPhpPresentation->getActiveSlide());
396                            }
397                        }
398                    }
399                }
400            }
401        }
402    }
403
404    /**
405     * Extract all MasterSlides.
406     */
407    protected function loadMasterSlides(XMLReader $xmlReader, string $fileRels): void
408    {
409        // Get all the MasterSlide Id's from the presentation.xml file
410        foreach ($xmlReader->getElements('/p:presentation/p:sldMasterIdLst/p:sldMasterId') as $oElement) {
411            if (!($oElement instanceof DOMElement)) {
412                continue;
413            }
414            $rId = $oElement->getAttribute('r:id');
415            // Get the path to the masterslide from the array with _rels files
416            $pathMasterSlide = isset($this->arrayRels[$fileRels][$rId]) ?
417                $this->arrayRels[$fileRels][$rId]['Target'] : '';
418            if (!empty($pathMasterSlide)) {
419                $pptMasterSlide = $this->oZip->getFromName('ppt/' . $pathMasterSlide);
420                if (false !== $pptMasterSlide) {
421                    $this->loadRels('ppt/slideMasters/_rels/' . basename($pathMasterSlide) . '.rels');
422                    $this->loadMasterSlide($pptMasterSlide, basename($pathMasterSlide));
423                }
424            }
425        }
426    }
427
428    /**
429     * Extract data from slide.
430     */
431    protected function loadSlide(string $sPart, string $baseFile): void
432    {
433        $xmlReader = new XMLReader();
434        // @phpstan-ignore-next-line
435        if ($xmlReader->getDomFromString($sPart)) {
436            // Core
437            $oSlide = $this->oPhpPresentation->createSlide();
438            $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);
439            $oSlide->setRelsIndex('ppt/slides/_rels/' . $baseFile . '.rels');
440
441            // Background
442            $oElement = $xmlReader->getElement('/p:sld/p:cSld/p:bg/p:bgPr');
443            if ($oElement instanceof DOMElement) {
444                $oElementColor = $xmlReader->getElement('a:solidFill/a:srgbClr', $oElement);
445                if ($oElementColor instanceof DOMElement) {
446                    // Color
447                    $oColor = new Color();
448                    $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
449                    // Background
450                    $oBackground = new Slide\Background\Color();
451                    $oBackground->setColor($oColor);
452                    // Slide Background
453                    $oSlide = $this->oPhpPresentation->getActiveSlide();
454                    $oSlide->setBackground($oBackground);
455                }
456                $oElementColor = $xmlReader->getElement('a:solidFill/a:schemeClr', $oElement);
457                if ($oElementColor instanceof DOMElement) {
458                    // Color
459                    $oColor = new SchemeColor();
460                    $oColor->setValue($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
461                    // Background
462                    $oBackground = new Slide\Background\SchemeColor();
463                    $oBackground->setSchemeColor($oColor);
464                    // Slide Background
465                    $oSlide = $this->oPhpPresentation->getActiveSlide();
466                    $oSlide->setBackground($oBackground);
467                }
468                $oElementImage = $xmlReader->getElement('a:blipFill/a:blip', $oElement);
469                if ($oElementImage instanceof DOMElement) {
470                    $relImg = $this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'][$oElementImage->getAttribute('r:embed')];
471                    if (is_array($relImg)) {
472                        // File
473                        $pathImage = 'ppt/slides/' . $relImg['Target'];
474                        $pathImage = explode('/', $pathImage);
475                        foreach ($pathImage as $key => $partPath) {
476                            if ('..' == $partPath) {
477                                unset($pathImage[$key - 1], $pathImage[$key]);
478                            }
479                        }
480                        $pathImage = implode('/', $pathImage);
481                        $contentImg = $this->oZip->getFromName($pathImage);
482
483                        $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderPpt2007Bkg');
484                        file_put_contents($tmpBkgImg, $contentImg);
485                        // Background
486                        $oBackground = new Slide\Background\Image();
487                        $oBackground->setPath($tmpBkgImg);
488                        // Slide Background
489                        $oSlide = $this->oPhpPresentation->getActiveSlide();
490                        $oSlide->setBackground($oBackground);
491                    }
492                }
493            }
494
495            // Shapes
496            $arrayElements = $xmlReader->getElements('/p:sld/p:cSld/p:spTree/*');
497            $this->loadSlideShapes($oSlide, $arrayElements, $xmlReader);
498
499            // Layout
500            $oSlide = $this->oPhpPresentation->getActiveSlide();
501            foreach ($this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'] as $valueRel) {
502                if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout' == $valueRel['Type']) {
503                    $layoutBasename = basename($valueRel['Target']);
504                    if (array_key_exists($layoutBasename, $this->arraySlideLayouts)) {
505                        $oSlide->setSlideLayout($this->arraySlideLayouts[$layoutBasename]);
506                    }
507
508                    break;
509                }
510            }
511        }
512    }
513
514    protected function loadMasterSlide(string $sPart, string $baseFile): void
515    {
516        $xmlReader = new XMLReader();
517        // @phpstan-ignore-next-line
518        if ($xmlReader->getDomFromString($sPart)) {
519            // Core
520            $oSlideMaster = $this->oPhpPresentation->createMasterSlide();
521            $oSlideMaster->setTextStyles(new TextStyle(false));
522            $oSlideMaster->setRelsIndex('ppt/slideMasters/_rels/' . $baseFile . '.rels');
523
524            // Background
525            $oElement = $xmlReader->getElement('/p:sldMaster/p:cSld/p:bg');
526            if ($oElement instanceof DOMElement) {
527                $this->loadSlideBackground($xmlReader, $oElement, $oSlideMaster);
528            }
529
530            // Shapes
531            $arrayElements = $xmlReader->getElements('/p:sldMaster/p:cSld/p:spTree/*');
532            $this->loadSlideShapes($oSlideMaster, $arrayElements, $xmlReader);
533
534            // Header & Footer
535
536            // ColorMapping
537            $colorMap = [];
538            $oElement = $xmlReader->getElement('/p:sldMaster/p:clrMap');
539            if ($oElement->hasAttributes()) {
540                foreach ($oElement->attributes as $attr) {
541                    $colorMap[$attr->nodeName] = $attr->nodeValue;
542                }
543                $oSlideMaster->colorMap->setMapping($colorMap);
544            }
545
546            // TextStyles
547            $arrayElementTxStyles = $xmlReader->getElements('/p:sldMaster/p:txStyles/*');
548            foreach ($arrayElementTxStyles as $oElementTxStyle) {
549                $arrayElementsLvl = $xmlReader->getElements('/p:sldMaster/p:txStyles/' . $oElementTxStyle->nodeName . '/*');
550                foreach ($arrayElementsLvl as $oElementLvl) {
551                    if (!($oElementLvl instanceof DOMElement) || 'a:extLst' == $oElementLvl->nodeName) {
552                        continue;
553                    }
554                    $oRTParagraph = new Paragraph();
555
556                    if ('a:defPPr' == $oElementLvl->nodeName) {
557                        $level = 0;
558                    } else {
559                        $level = str_replace('a:lvl', '', $oElementLvl->nodeName);
560                        $level = str_replace('pPr', '', $level);
561                        $level = (int) $level;
562                    }
563
564                    if ($oElementLvl->hasAttribute('algn')) {
565                        $oRTParagraph->getAlignment()->setHorizontal($oElementLvl->getAttribute('algn'));
566                    }
567                    if ($oElementLvl->hasAttribute('marL')) {
568                        $val = (int) $oElementLvl->getAttribute('marL');
569                        $val = CommonDrawing::emuToPixels((int) $val);
570                        $oRTParagraph->getAlignment()->setMarginLeft($val);
571                    }
572                    if ($oElementLvl->hasAttribute('marR')) {
573                        $val = (int) $oElementLvl->getAttribute('marR');
574                        $val = CommonDrawing::emuToPixels((int) $val);
575                        $oRTParagraph->getAlignment()->setMarginRight($val);
576                    }
577                    if ($oElementLvl->hasAttribute('indent')) {
578                        $val = (int) $oElementLvl->getAttribute('indent');
579                        $val = CommonDrawing::emuToPixels((int) $val);
580                        $oRTParagraph->getAlignment()->setIndent($val);
581                    }
582                    $oElementLvlDefRPR = $xmlReader->getElement('a:defRPr', $oElementLvl);
583                    if ($oElementLvlDefRPR instanceof DOMElement) {
584                        if ($oElementLvlDefRPR->hasAttribute('sz')) {
585                            $oRTParagraph->getFont()->setSize((int) ((int) $oElementLvlDefRPR->getAttribute('sz') / 100));
586                        }
587                        if ($oElementLvlDefRPR->hasAttribute('b') && 1 == $oElementLvlDefRPR->getAttribute('b')) {
588                            $oRTParagraph->getFont()->setBold(true);
589                        }
590                        if ($oElementLvlDefRPR->hasAttribute('i') && 1 == $oElementLvlDefRPR->getAttribute('i')) {
591                            $oRTParagraph->getFont()->setItalic(true);
592                        }
593                    }
594                    $oElementSchemeColor = $xmlReader->getElement('a:defRPr/a:solidFill/a:schemeClr', $oElementLvl);
595                    if ($oElementSchemeColor instanceof DOMElement) {
596                        if ($oElementSchemeColor->hasAttribute('val')) {
597                            $oSchemeColor = new SchemeColor();
598                            $oSchemeColor->setValue($oElementSchemeColor->getAttribute('val'));
599                            $oRTParagraph->getFont()->setColor($oSchemeColor);
600                        }
601                    }
602
603                    switch ($oElementTxStyle->nodeName) {
604                        case 'p:bodyStyle':
605                            $oSlideMaster->getTextStyles()->setBodyStyleAtLvl($oRTParagraph, $level);
606
607                            break;
608                        case 'p:otherStyle':
609                            $oSlideMaster->getTextStyles()->setOtherStyleAtLvl($oRTParagraph, $level);
610
611                            break;
612                        case 'p:titleStyle':
613                            $oSlideMaster->getTextStyles()->setTitleStyleAtLvl($oRTParagraph, $level);
614
615                            break;
616                    }
617                }
618            }
619
620            // Load the theme
621            foreach ($this->arrayRels[$oSlideMaster->getRelsIndex()] as $arrayRel) {
622                if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme' == $arrayRel['Type']) {
623                    $pptTheme = $this->oZip->getFromName('ppt/' . substr($arrayRel['Target'], strrpos($arrayRel['Target'], '../') + 3));
624                    if (false !== $pptTheme) {
625                        $this->loadTheme($pptTheme, $oSlideMaster);
626                    }
627
628                    break;
629                }
630            }
631
632            // Load the Layoutslide
633            foreach ($xmlReader->getElements('/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId') as $oElement) {
634                if (!($oElement instanceof DOMElement)) {
635                    continue;
636                }
637                $rId = $oElement->getAttribute('r:id');
638                // Get the path to the masterslide from the array with _rels files
639                $pathLayoutSlide = isset($this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]) ?
640                    $this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]['Target'] : '';
641                if (!empty($pathLayoutSlide)) {
642                    $pptLayoutSlide = $this->oZip->getFromName('ppt/' . substr($pathLayoutSlide, strrpos($pathLayoutSlide, '../') + 3));
643                    if (false !== $pptLayoutSlide) {
644                        $this->loadRels('ppt/slideLayouts/_rels/' . basename($pathLayoutSlide) . '.rels');
645                        $oSlideMaster->addSlideLayout(
646                            $this->loadLayoutSlide($pptLayoutSlide, basename($pathLayoutSlide), $oSlideMaster)
647                        );
648                    }
649                }
650            }
651        }
652    }
653
654    protected function loadLayoutSlide(string $sPart, string $baseFile, SlideMaster $oSlideMaster): ?SlideLayout
655    {
656        $xmlReader = new XMLReader();
657        // @phpstan-ignore-next-line
658        if ($xmlReader->getDomFromString($sPart)) {
659            // Core
660            $oSlideLayout = new SlideLayout($oSlideMaster);
661            $oSlideLayout->setRelsIndex('ppt/slideLayouts/_rels/' . $baseFile . '.rels');
662
663            // Name
664            $oElement = $xmlReader->getElement('/p:sldLayout/p:cSld');
665            if ($oElement instanceof DOMElement && $oElement->hasAttribute('name')) {
666                $oSlideLayout->setLayoutName($oElement->getAttribute('name'));
667            }
668
669            // Background
670            $oElement = $xmlReader->getElement('/p:sldLayout/p:cSld/p:bg');
671            if ($oElement instanceof DOMElement) {
672                $this->loadSlideBackground($xmlReader, $oElement, $oSlideLayout);
673            }
674
675            // ColorMapping
676            $oElement = $xmlReader->getElement('/p:sldLayout/p:clrMapOvr/a:overrideClrMapping');
677            if ($oElement instanceof DOMElement && $oElement->hasAttributes()) {
678                $colorMap = [];
679                foreach ($oElement->attributes as $attr) {
680                    $colorMap[$attr->nodeName] = $attr->nodeValue;
681                }
682                $oSlideLayout->colorMap->setMapping($colorMap);
683            }
684
685            // Shapes
686            $oElements = $xmlReader->getElements('/p:sldLayout/p:cSld/p:spTree/*');
687            $this->loadSlideShapes($oSlideLayout, $oElements, $xmlReader);
688            $this->arraySlideLayouts[$baseFile] = &$oSlideLayout;
689
690            return $oSlideLayout;
691        }
692
693        // @phpstan-ignore-next-line
694        return null;
695    }
696
697    protected function loadTheme(string $sPart, SlideMaster $oSlideMaster): void
698    {
699        $xmlReader = new XMLReader();
700        // @phpstan-ignore-next-line
701        if ($xmlReader->getDomFromString($sPart)) {
702            $oElements = $xmlReader->getElements('/a:theme/a:themeElements/a:clrScheme/*');
703            foreach ($oElements as $oElement) {
704                if ($oElement instanceof DOMElement) {
705                    $oSchemeColor = new SchemeColor();
706                    $oSchemeColor->setValue(str_replace('a:', '', $oElement->tagName));
707                    $colorElement = $xmlReader->getElement('*', $oElement);
708                    if ($colorElement instanceof DOMElement) {
709                        if ($colorElement->hasAttribute('lastClr')) {
710                            $oSchemeColor->setRGB($colorElement->getAttribute('lastClr'));
711                        } elseif ($colorElement->hasAttribute('val')) {
712                            $oSchemeColor->setRGB($colorElement->getAttribute('val'));
713                        }
714                    }
715                    $oSlideMaster->addSchemeColor($oSchemeColor);
716                }
717            }
718        }
719    }
720
721    protected function loadSlideBackground(XMLReader $xmlReader, DOMElement $oElement, AbstractSlide $oSlide): void
722    {
723        // Background color
724        $oElementColor = $xmlReader->getElement('p:bgPr/a:solidFill/a:srgbClr', $oElement);
725        if ($oElementColor instanceof DOMElement) {
726            // Color
727            $oColor = new Color();
728            $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
729            // Background
730            $oBackground = new Slide\Background\Color();
731            $oBackground->setColor($oColor);
732            // Slide Background
733            $oSlide->setBackground($oBackground);
734        }
735
736        // Background scheme color
737        $oElementSchemeColor = $xmlReader->getElement('p:bgRef/a:schemeClr', $oElement);
738        if ($oElementSchemeColor instanceof DOMElement) {
739            // Color
740            $oColor = new SchemeColor();
741            $oColor->setValue($oElementSchemeColor->hasAttribute('val') ? $oElementSchemeColor->getAttribute('val') : null);
742            // Background
743            $oBackground = new Slide\Background\SchemeColor();
744            $oBackground->setSchemeColor($oColor);
745            // Slide Background
746            $oSlide->setBackground($oBackground);
747        }
748
749        // Background image
750        $oElementImage = $xmlReader->getElement('p:bgPr/a:blipFill/a:blip', $oElement);
751        if ($oElementImage instanceof DOMElement) {
752            $relImg = $this->arrayRels[$oSlide->getRelsIndex()][$oElementImage->getAttribute('r:embed')];
753            if (is_array($relImg)) {
754                // File
755                $pathImage = 'ppt/slides/' . $relImg['Target'];
756                $pathImage = explode('/', $pathImage);
757                foreach ($pathImage as $key => $partPath) {
758                    if ('..' == $partPath) {
759                        unset($pathImage[$key - 1], $pathImage[$key]);
760                    }
761                }
762                $pathImage = implode('/', $pathImage);
763                $contentImg = $this->oZip->getFromName($pathImage);
764
765                $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderPpt2007Bkg');
766                file_put_contents($tmpBkgImg, $contentImg);
767                // Background
768                $oBackground = new Slide\Background\Image();
769                $oBackground->setPath($tmpBkgImg);
770                // Slide Background
771                $oSlide->setBackground($oBackground);
772            }
773        }
774    }
775
776    protected function loadSlideNote(string $baseFile, Slide $oSlide): void
777    {
778        $sPart = $this->oZip->getFromName('ppt/notesSlides/' . $baseFile);
779        $xmlReader = new XMLReader();
780        // @phpstan-ignore-next-line
781        if ($xmlReader->getDomFromString($sPart)) {
782            $oNote = $oSlide->getNote();
783
784            $arrayElements = $xmlReader->getElements('/p:notes/p:cSld/p:spTree/*');
785            $this->loadSlideShapes($oNote, $arrayElements, $xmlReader);
786        }
787    }
788
789    protected function loadShapeDrawing(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
790    {
791        // Core
792        $document->registerNamespace('asvg', 'http://schemas.microsoft.com/office/drawing/2016/SVG/main');
793        if ($document->getElement('p:blipFill/a:blip/a:extLst/a:ext/asvg:svgBlip', $node)) {
794            $oShape = new Base64();
795        } else {
796            $oShape = new Gd();
797        }
798        $oShape->getShadow()->setVisible(false);
799        // Variables
800        $fileRels = $oSlide->getRelsIndex();
801
802        $oElement = $document->getElement('p:nvPicPr/p:cNvPr', $node);
803        if ($oElement instanceof DOMElement) {
804            $oShape->setName($oElement->hasAttribute('name') ? $oElement->getAttribute('name') : '');
805            $oShape->setDescription($oElement->hasAttribute('descr') ? $oElement->getAttribute('descr') : '');
806
807            // Hyperlink
808            $oElementHlinkClick = $document->getElement('a:hlinkClick', $oElement);
809            if (is_object($oElementHlinkClick)) {
810                $oShape->setHyperlink(
811                    $this->loadHyperlink($document, $oElementHlinkClick, $oShape->getHyperlink())
812                );
813            }
814        }
815
816        $oElement = $document->getElement('p:blipFill/a:blip', $node);
817        if ($oElement instanceof DOMElement) {
818            if ($oElement->hasAttribute('r:embed') && isset($this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'])) {
819                $pathImage = 'ppt/slides/' . $this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'];
820                $pathImage = explode('/', $pathImage);
821                foreach ($pathImage as $key => $partPath) {
822                    if ('..' == $partPath) {
823                        unset($pathImage[$key - 1], $pathImage[$key]);
824                    }
825                }
826                $pathImage = implode('/', $pathImage);
827                $imageFile = $this->oZip->getFromName($pathImage);
828                if (!empty($imageFile)) {
829                    if ($oShape instanceof Gd) {
830                        $info = getimagesizefromstring($imageFile);
831                        $oShape->setMimeType($info['mime']);
832                        $oShape->setRenderingFunction(str_replace('/', '', $info['mime']));
833                        $image = @imagecreatefromstring($imageFile);
834                        if (!$image) {
835                            return;
836                        }
837                        $oShape->setImageResource($image);
838                    } elseif ($oShape instanceof Base64) {
839                        $oShape->setData('data:image/svg+xml;base64,' . base64_encode($imageFile));
840                    }
841                }
842            }
843        }
844
845        $oElement = $document->getElement('p:spPr', $node);
846        if ($oElement instanceof DOMElement) {
847            $oFill = $this->loadStyleFill($document, $oElement);
848            $oShape->setFill($oFill);
849        }
850
851        $oElement = $document->getElement('p:spPr/a:xfrm', $node);
852        if ($oElement instanceof DOMElement) {
853            if ($oElement->hasAttribute('rot')) {
854                $oShape->setRotation((int) CommonDrawing::angleToDegrees((int) $oElement->getAttribute('rot')));
855            }
856        }
857
858        $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
859        if ($oElement instanceof DOMElement) {
860            if ($oElement->hasAttribute('x')) {
861                $oShape->setOffsetX(CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
862            }
863            if ($oElement->hasAttribute('y')) {
864                $oShape->setOffsetY(CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
865            }
866        }
867
868        $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
869        if ($oElement instanceof DOMElement) {
870            if ($oElement->hasAttribute('cx')) {
871                $oShape->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
872            }
873            if ($oElement->hasAttribute('cy')) {
874                $oShape->setHeight(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
875            }
876        }
877        // Load shape effects
878        $oElement = $document->getElement('p:spPr/a:effectLst', $node);
879        if ($oElement instanceof DOMElement) {
880            $oShape->setShadow(
881                $this->loadShadow($document, $oElement)
882            );
883        }
884        $oSlide->addShape($oShape);
885    }
886
887    /**
888     * Load Shadow for shape or paragraph.
889     */
890    protected function loadShadow(XMLReader $document, DOMElement $node): ?Shadow
891    {
892        if ($node instanceof DOMElement) {
893            $aNodes = $document->getElements('*', $node);
894            foreach ($aNodes as $nodeShadow) {
895                $type = explode(':', $nodeShadow->tagName);
896                $type = array_pop($type);
897                if ($type == Shadow::TYPE_SHADOW_INNER || $type == Shadow::TYPE_SHADOW_OUTER || $type == Shadow::TYPE_REFLECTION) {
898                    $oShadow = new Shadow();
899                    $oShadow->setVisible(true);
900                    $oShadow->setType($type);
901                    if ($nodeShadow->hasAttribute('blurRad')) {
902                        $oShadow->setBlurRadius(CommonDrawing::emuToPixels((int) $nodeShadow->getAttribute('blurRad')));
903                    }
904                    if ($nodeShadow->hasAttribute('dist')) {
905                        $oShadow->setDistance(CommonDrawing::emuToPixels((int) $nodeShadow->getAttribute('dist')));
906                    }
907                    if ($nodeShadow->hasAttribute('dir')) {
908                        $oShadow->setDirection((int) CommonDrawing::angleToDegrees((int) $nodeShadow->getAttribute('dir')));
909                    }
910                    if ($nodeShadow->hasAttribute('algn')) {
911                        $oShadow->setAlignment($node->getAttribute('algn'));
912                    }
913
914                    // Get color define by prstClr
915                    $oSubElement = $document->getElement('a:prstClr', $nodeShadow);
916                    if ($oSubElement instanceof DOMElement && $oSubElement->hasAttribute('val')) {
917                        $oColor = new Color();
918                        $oColor->setRGB($oSubElement->getAttribute('val'));
919
920                        $oSubElt = $document->getElement('a:alpha', $oSubElement);
921                        if ($oSubElt instanceof DOMElement && $oSubElt->hasAttribute('val')) {
922                            $oColor->setAlpha((int) $oSubElt->getAttribute('val') / 1000);
923                        }
924
925                        $oShadow->setColor($oColor);
926                    }
927
928                    return $oShadow;
929                }
930            }
931        }
932
933        return null;
934    }
935
936    /**
937     * @param AbstractSlide|Note $oSlide
938     */
939    protected function loadShapeRichText(XMLReader $document, DOMElement $node, $oSlide): void
940    {
941        // Core
942        $oShape = $oSlide->createRichTextShape();
943        $oShape->setParagraphs([]);
944        // Variables
945        if ($oSlide instanceof AbstractSlide) {
946            $this->fileRels = $oSlide->getRelsIndex();
947        }
948
949        $oElement = $document->getElement('p:nvSpPr/p:cNvPr', $node);
950        if ($oElement instanceof DOMElement) {
951            $oShape->setName($oElement->hasAttribute('name') ? $oElement->getAttribute('name') : '');
952        }
953
954        $oElement = $document->getElement('p:spPr/a:xfrm', $node);
955        if ($oElement instanceof DOMElement && $oElement->hasAttribute('rot')) {
956            $oShape->setRotation((int) CommonDrawing::angleToDegrees((int) $oElement->getAttribute('rot')));
957        }
958
959        $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
960        if ($oElement instanceof DOMElement) {
961            if ($oElement->hasAttribute('x')) {
962                $oShape->setOffsetX(CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
963            }
964            if ($oElement->hasAttribute('y')) {
965                $oShape->setOffsetY(CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
966            }
967        }
968
969        $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
970        if ($oElement instanceof DOMElement) {
971            if ($oElement->hasAttribute('cx')) {
972                $oShape->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
973            }
974            if ($oElement->hasAttribute('cy')) {
975                $oShape->setHeight(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
976            }
977        }
978
979        $oElement = $document->getElement('p:nvSpPr/p:nvPr/p:ph', $node);
980        if ($oElement instanceof DOMElement) {
981            if ($oElement->hasAttribute('type')) {
982                $placeholder = new Placeholder($oElement->getAttribute('type'));
983                $oShape->setPlaceHolder($placeholder);
984            }
985        }
986
987        // Load shape effects
988        $oElement = $document->getElement('p:spPr/a:effectLst', $node);
989        if ($oElement instanceof DOMElement) {
990            $oShape->setShadow(
991                $this->loadShadow($document, $oElement)
992            );
993        }
994
995        // FBU-20210202+ Read body definitions
996        $bodyPr = $document->getElement('p:txBody/a:bodyPr', $node);
997        if ($bodyPr instanceof DOMElement) {
998            if ($bodyPr->hasAttribute('lIns')) {
999                $oShape->setInsetLeft((int) $bodyPr->getAttribute('lIns'));
1000            }
1001            if ($bodyPr->hasAttribute('tIns')) {
1002                $oShape->setInsetTop((int) $bodyPr->getAttribute('tIns'));
1003            }
1004            if ($bodyPr->hasAttribute('rIns')) {
1005                $oShape->setInsetRight((int) $bodyPr->getAttribute('rIns'));
1006            }
1007            if ($bodyPr->hasAttribute('bIns')) {
1008                $oShape->setInsetBottom((int) $bodyPr->getAttribute('bIns'));
1009            }
1010            if ($bodyPr->hasAttribute('anchorCtr')) {
1011                $oShape->setVerticalAlignCenter((int) $bodyPr->getAttribute('anchorCtr'));
1012            }
1013        }
1014
1015        $arrayElements = $document->getElements('p:txBody/a:p', $node);
1016        foreach ($arrayElements as $oElement) {
1017            if ($oElement instanceof DOMElement) {
1018                $this->loadParagraph($document, $oElement, $oShape);
1019            }
1020        }
1021
1022        $oElement = $document->getElement('p:spPr', $node);
1023        if ($oElement instanceof DOMElement) {
1024            $oShape->setFill(
1025                $this->loadStyleFill($document, $oElement)
1026            );
1027        }
1028
1029        if (count($oShape->getParagraphs()) > 0) {
1030            $oShape->setActiveParagraph(0);
1031        }
1032    }
1033
1034    protected function loadShapeTable(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
1035    {
1036        $this->fileRels = $oSlide->getRelsIndex();
1037
1038        $oShape = $oSlide->createTableShape();
1039
1040        $oElement = $document->getElement('p:cNvPr', $node);
1041        if ($oElement instanceof DOMElement) {
1042            if ($oElement->hasAttribute('name')) {
1043                $oShape->setName($oElement->getAttribute('name'));
1044            }
1045            if ($oElement->hasAttribute('descr')) {
1046                $oShape->setDescription($oElement->getAttribute('descr'));
1047            }
1048        }
1049
1050        $oElement = $document->getElement('p:xfrm/a:off', $node);
1051        if ($oElement instanceof DOMElement) {
1052            if ($oElement->hasAttribute('x')) {
1053                $oShape->setOffsetX(CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
1054            }
1055            if ($oElement->hasAttribute('y')) {
1056                $oShape->setOffsetY(CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
1057            }
1058        }
1059
1060        $oElement = $document->getElement('p:xfrm/a:ext', $node);
1061        if ($oElement instanceof DOMElement) {
1062            if ($oElement->hasAttribute('cx')) {
1063                $oShape->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
1064            }
1065            if ($oElement->hasAttribute('cy')) {
1066                $oShape->setHeight(CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
1067            }
1068        }
1069
1070        $arrayElements = $document->getElements('a:graphic/a:graphicData/a:tbl/a:tblGrid/a:gridCol', $node);
1071        $oShape->setNumColumns($arrayElements->length);
1072        $oShape->createRow();
1073        foreach ($arrayElements as $key => $oElement) {
1074            if ($oElement instanceof DOMElement && $oElement->getAttribute('w')) {
1075                $oShape->getRow(0)->getCell($key)->setWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('w')));
1076            }
1077        }
1078
1079        $arrayElements = $document->getElements('a:graphic/a:graphicData/a:tbl/a:tr', $node);
1080        foreach ($arrayElements as $keyRow => $oElementRow) {
1081            if (!($oElementRow instanceof DOMElement)) {
1082                continue;
1083            }
1084            if ($oShape->hasRow($keyRow)) {
1085                $oRow = $oShape->getRow($keyRow);
1086            } else {
1087                $oRow = $oShape->createRow();
1088            }
1089            if ($oElementRow->hasAttribute('h')) {
1090                $oRow->setHeight(CommonDrawing::emuToPixels((int) $oElementRow->getAttribute('h')));
1091            }
1092            $arrayElementsCell = $document->getElements('a:tc', $oElementRow);
1093            foreach ($arrayElementsCell as $keyCell => $oElementCell) {
1094                if (!($oElementCell instanceof DOMElement)) {
1095                    continue;
1096                }
1097                $oCell = $oRow->getCell($keyCell);
1098                $oCell->setParagraphs([]);
1099                if ($oElementCell->hasAttribute('gridSpan')) {
1100                    $oCell->setColSpan((int) $oElementCell->getAttribute('gridSpan'));
1101                }
1102                if ($oElementCell->hasAttribute('rowSpan')) {
1103                    $oCell->setRowSpan((int) $oElementCell->getAttribute('rowSpan'));
1104                }
1105
1106                foreach ($document->getElements('a:txBody/a:p', $oElementCell) as $oElementPara) {
1107                    if ($oElementPara instanceof DOMElement) {
1108                        $this->loadParagraph($document, $oElementPara, $oCell);
1109                    }
1110                }
1111
1112                $oElementTcPr = $document->getElement('a:tcPr', $oElementCell);
1113                if ($oElementTcPr instanceof DOMElement) {
1114                    $numParagraphs = count($oCell->getParagraphs());
1115                    if ($numParagraphs > 0) {
1116                        if ($oElementTcPr->hasAttribute('vert')) {
1117                            $oCell->getParagraph(0)->getAlignment()->setTextDirection($oElementTcPr->getAttribute('vert'));
1118                        }
1119                        if ($oElementTcPr->hasAttribute('anchor')) {
1120                            $oCell->getParagraph(0)->getAlignment()->setVertical($oElementTcPr->getAttribute('anchor'));
1121                        }
1122                        if ($oElementTcPr->hasAttribute('marB')) {
1123                            $oCell->getParagraph(0)->getAlignment()->setMarginBottom((int) $oElementTcPr->getAttribute('marB'));
1124                        }
1125                        if ($oElementTcPr->hasAttribute('marL')) {
1126                            $oCell->getParagraph(0)->getAlignment()->setMarginLeft((int) $oElementTcPr->getAttribute('marL'));
1127                        }
1128                        if ($oElementTcPr->hasAttribute('marR')) {
1129                            $oCell->getParagraph(0)->getAlignment()->setMarginRight((int) $oElementTcPr->getAttribute('marR'));
1130                        }
1131                        if ($oElementTcPr->hasAttribute('marT')) {
1132                            $oCell->getParagraph(0)->getAlignment()->setMarginTop((int) $oElementTcPr->getAttribute('marT'));
1133                        }
1134                    }
1135
1136                    $oFill = $this->loadStyleFill($document, $oElementTcPr);
1137                    if ($oFill instanceof Fill) {
1138                        $oCell->setFill($oFill);
1139                    }
1140
1141                    $oBorders = new Borders();
1142                    $oElementBorderL = $document->getElement('a:lnL', $oElementTcPr);
1143                    if ($oElementBorderL instanceof DOMElement) {
1144                        $this->loadStyleBorder($document, $oElementBorderL, $oBorders->getLeft());
1145                    }
1146                    $oElementBorderR = $document->getElement('a:lnR', $oElementTcPr);
1147                    if ($oElementBorderR instanceof DOMElement) {
1148                        $this->loadStyleBorder($document, $oElementBorderR, $oBorders->getRight());
1149                    }
1150                    $oElementBorderT = $document->getElement('a:lnT', $oElementTcPr);
1151                    if ($oElementBorderT instanceof DOMElement) {
1152                        $this->loadStyleBorder($document, $oElementBorderT, $oBorders->getTop());
1153                    }
1154                    $oElementBorderB = $document->getElement('a:lnB', $oElementTcPr);
1155                    if ($oElementBorderB instanceof DOMElement) {
1156                        $this->loadStyleBorder($document, $oElementBorderB, $oBorders->getBottom());
1157                    }
1158                    $oElementBorderDiagDown = $document->getElement('a:lnTlToBr', $oElementTcPr);
1159                    if ($oElementBorderDiagDown instanceof DOMElement) {
1160                        $this->loadStyleBorder($document, $oElementBorderDiagDown, $oBorders->getDiagonalDown());
1161                    }
1162                    $oElementBorderDiagUp = $document->getElement('a:lnBlToTr', $oElementTcPr);
1163                    if ($oElementBorderDiagUp instanceof DOMElement) {
1164                        $this->loadStyleBorder($document, $oElementBorderDiagUp, $oBorders->getDiagonalUp());
1165                    }
1166                    $oCell->setBorders($oBorders);
1167                }
1168            }
1169        }
1170    }
1171
1172    /**
1173     * @param Cell|RichText $oShape
1174     */
1175    protected function loadParagraph(XMLReader $document, DOMElement $oElement, $oShape): void
1176    {
1177        // Core
1178        $oParagraph = $oShape->createParagraph();
1179        $oParagraph->setRichTextElements([]);
1180
1181        $oSubElement = $document->getElement('a:pPr', $oElement);
1182        if ($oSubElement instanceof DOMElement) {
1183            if ($oSubElement->hasAttribute('algn')) {
1184                $oParagraph->getAlignment()->setHorizontal($oSubElement->getAttribute('algn'));
1185            }
1186            if ($oSubElement->hasAttribute('fontAlgn')) {
1187                $oParagraph->getAlignment()->setVertical($oSubElement->getAttribute('fontAlgn'));
1188            }
1189            if ($oSubElement->hasAttribute('marL')) {
1190                $oParagraph->getAlignment()->setMarginLeft(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('marL')));
1191            }
1192            if ($oSubElement->hasAttribute('marR')) {
1193                $oParagraph->getAlignment()->setMarginRight(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('marR')));
1194            }
1195            if ($oSubElement->hasAttribute('indent')) {
1196                $oParagraph->getAlignment()->setIndent(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('indent')));
1197            }
1198            if ($oSubElement->hasAttribute('lvl')) {
1199                $oParagraph->getAlignment()->setLevel((int) $oSubElement->getAttribute('lvl'));
1200            }
1201            if ($oSubElement->hasAttribute('rtl')) {
1202                $oParagraph->getAlignment()->setIsRTL((bool) $oSubElement->getAttribute('rtl'));
1203            }
1204
1205            $oElementLineSpacingPoints = $document->getElement('a:lnSpc/a:spcPts', $oSubElement);
1206            if ($oElementLineSpacingPoints instanceof DOMElement) {
1207                $oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_POINT);
1208                $oParagraph->setLineSpacing((int) $oElementLineSpacingPoints->getAttribute('val') / 100);
1209            }
1210            $oElementLineSpacingPercent = $document->getElement('a:lnSpc/a:spcPct', $oSubElement);
1211            if ($oElementLineSpacingPercent instanceof DOMElement) {
1212                $oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_PERCENT);
1213                $oParagraph->setLineSpacing((int) $oElementLineSpacingPercent->getAttribute('val') / 1000);
1214            }
1215            $oElementSpacingBefore = $document->getElement('a:spcBef/a:spcPts', $oSubElement);
1216            if ($oElementSpacingBefore instanceof DOMElement) {
1217                $oParagraph->setSpacingBefore((int) $oElementSpacingBefore->getAttribute('val') / 100);
1218            }
1219            $oElementSpacingAfter = $document->getElement('a:spcAft/a:spcPts', $oSubElement);
1220            if ($oElementSpacingAfter instanceof DOMElement) {
1221                $oParagraph->setSpacingAfter((int) $oElementSpacingAfter->getAttribute('val') / 100);
1222            }
1223
1224            $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NONE);
1225
1226            $oElementBuFont = $document->getElement('a:buFont', $oSubElement);
1227            if ($oElementBuFont instanceof DOMElement) {
1228                if ($oElementBuFont->hasAttribute('typeface')) {
1229                    $oParagraph->getBulletStyle()->setBulletFont($oElementBuFont->getAttribute('typeface'));
1230                }
1231            }
1232            $oElementBuChar = $document->getElement('a:buChar', $oSubElement);
1233            if ($oElementBuChar instanceof DOMElement) {
1234                $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
1235                if ($oElementBuChar->hasAttribute('char')) {
1236                    $oParagraph->getBulletStyle()->setBulletChar($oElementBuChar->getAttribute('char'));
1237                }
1238            }
1239            $oElementBuAutoNum = $document->getElement('a:buAutoNum', $oSubElement);
1240            if ($oElementBuAutoNum instanceof DOMElement) {
1241                $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NUMERIC);
1242                if ($oElementBuAutoNum->hasAttribute('type')) {
1243                    $oParagraph->getBulletStyle()->setBulletNumericStyle($oElementBuAutoNum->getAttribute('type'));
1244                }
1245                if ($oElementBuAutoNum->hasAttribute('startAt') && 1 != $oElementBuAutoNum->getAttribute('startAt')) {
1246                    $oParagraph->getBulletStyle()->setBulletNumericStartAt($oElementBuAutoNum->getAttribute('startAt'));
1247                }
1248            }
1249            $oElementBuClr = $document->getElement('a:buClr', $oSubElement);
1250            if ($oElementBuClr instanceof DOMElement) {
1251                $oColor = new Color();
1252                /**
1253                 * @todo Create protected for reading Color
1254                 */
1255                $oElementColor = $document->getElement('a:srgbClr', $oElementBuClr);
1256                if ($oElementColor instanceof DOMElement) {
1257                    $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
1258                }
1259                $oParagraph->getBulletStyle()->setBulletColor($oColor);
1260            }
1261        }
1262        $arraySubElements = $document->getElements('(a:r|a:br)', $oElement);
1263        foreach ($arraySubElements as $oSubElement) {
1264            if (!($oSubElement instanceof DOMElement)) {
1265                continue;
1266            }
1267            if ('a:br' == $oSubElement->tagName) {
1268                $oParagraph->createBreak();
1269            }
1270            if ('a:r' == $oSubElement->tagName) {
1271                $oElementrPr = $document->getElement('a:rPr', $oSubElement);
1272                if (is_object($oElementrPr)) {
1273                    $oText = $oParagraph->createTextRun();
1274
1275                    if ($oElementrPr->hasAttribute('b')) {
1276                        $att = $oElementrPr->getAttribute('b');
1277                        $oText->getFont()->setBold('true' == $att || '1' == $att ? true : false);
1278                    }
1279                    if ($oElementrPr->hasAttribute('i')) {
1280                        $att = $oElementrPr->getAttribute('i');
1281                        $oText->getFont()->setItalic('true' == $att || '1' == $att ? true : false);
1282                    }
1283                    if ($oElementrPr->hasAttribute('strike')) {
1284                        $oText->getFont()->setStrikethrough($oElementrPr->getAttribute('strike'));
1285                    }
1286                    if ($oElementrPr->hasAttribute('sz')) {
1287                        $oText->getFont()->setSize((int) ((int) $oElementrPr->getAttribute('sz') / 100));
1288                    }
1289                    if ($oElementrPr->hasAttribute('u')) {
1290                        $oText->getFont()->setUnderline($oElementrPr->getAttribute('u'));
1291                    }
1292                    if ($oElementrPr->hasAttribute('cap')) {
1293                        $oText->getFont()->setCapitalization($oElementrPr->getAttribute('cap'));
1294                    }
1295                    if ($oElementrPr->hasAttribute('lang')) {
1296                        $oText->setLanguage($oElementrPr->getAttribute('lang'));
1297                    }
1298                    if ($oElementrPr->hasAttribute('baseline')) {
1299                        $oText->getFont()->setBaseline((int) $oElementrPr->getAttribute('baseline'));
1300                    }
1301                    // Color
1302                    $oElementSrgbClr = $document->getElement('a:solidFill/a:srgbClr', $oElementrPr);
1303                    if (is_object($oElementSrgbClr) && $oElementSrgbClr->hasAttribute('val')) {
1304                        $oColor = new Color();
1305                        $oColor->setRGB($oElementSrgbClr->getAttribute('val'));
1306                        $oText->getFont()->setColor($oColor);
1307                    }
1308                    // Hyperlink
1309                    $oElementHlinkClick = $document->getElement('a:hlinkClick', $oElementrPr);
1310                    if (is_object($oElementHlinkClick)) {
1311                        $oText->setHyperlink(
1312                            $this->loadHyperlink($document, $oElementHlinkClick, $oText->getHyperlink())
1313                        );
1314                    }
1315                    // Font
1316                    $oElementFontFormat = null;
1317                    $oElementFontFormatLatin = $document->getElement('a:latin', $oElementrPr);
1318                    if (is_object($oElementFontFormatLatin)) {
1319                        $oText->getFont()->setFormat(Font::FORMAT_LATIN);
1320                        $oElementFontFormat = $oElementFontFormatLatin;
1321                    }
1322                    $oElementFontFormatEastAsian = $document->getElement('a:ea', $oElementrPr);
1323                    if (is_object($oElementFontFormatEastAsian)) {
1324                        $oText->getFont()->setFormat(Font::FORMAT_EAST_ASIAN);
1325                        $oElementFontFormat = $oElementFontFormatEastAsian;
1326                    }
1327                    $oElementFontFormatComplexScript = $document->getElement('a:cs', $oElementrPr);
1328                    if (is_object($oElementFontFormatComplexScript)) {
1329                        $oText->getFont()->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
1330                        $oElementFontFormat = $oElementFontFormatComplexScript;
1331                    }
1332                    if (is_object($oElementFontFormat) && $oElementFontFormat->hasAttribute('typeface')) {
1333                        $oText->getFont()->setName($oElementFontFormat->getAttribute('typeface'));
1334                    }
1335                    // Font definition
1336                    $oElementFont = $document->getElement('a:latin', $oElementrPr);
1337                    if ($oElementFont instanceof DOMElement) {
1338                        if ($oElementFont->hasAttribute('typeface')) {
1339                            $oText->getFont()->setName($oElementFont->getAttribute('typeface'));
1340                        }
1341                        if ($oElementFont->hasAttribute('panose')) {
1342                            $oText->getFont()->setPanose($oElementFont->getAttribute('panose'));
1343                        }
1344                        if ($oElementFont->hasAttribute('pitchFamily')) {
1345                            $oText->getFont()->setPitchFamily((int) $oElementFont->getAttribute('pitchFamily'));
1346                        }
1347                        if ($oElementFont->hasAttribute('charset')) {
1348                            $oText->getFont()->setCharset((int) $oElementFont->getAttribute('charset'));
1349                        }
1350                    }
1351
1352                    $oSubSubElement = $document->getElement('a:t', $oSubElement);
1353                    $oText->setText($oSubSubElement->nodeValue);
1354                }
1355            }
1356        }
1357    }
1358
1359    protected function loadHyperlink(XMLReader $xmlReader, DOMElement $element, Hyperlink $hyperlink): Hyperlink
1360    {
1361        if ($element->hasAttribute('tooltip')) {
1362            $hyperlink->setTooltip($element->getAttribute('tooltip'));
1363        }
1364        if ($element->hasAttribute('r:id') && isset($this->arrayRels[$this->fileRels][$element->getAttribute('r:id')]['Target'])) {
1365            $hyperlink->setUrl($this->arrayRels[$this->fileRels][$element->getAttribute('r:id')]['Target']);
1366        }
1367        if ($subElementExt = $xmlReader->getElement('a:extLst/a:ext', $element)) {
1368            if ($subElementExt->hasAttribute('uri') && $subElementExt->getAttribute('uri') == '{A12FA001-AC4F-418D-AE19-62706E023703}') {
1369                $hyperlink->setIsTextColorUsed(true);
1370            }
1371        }
1372
1373        return $hyperlink;
1374    }
1375
1376    protected function loadStyleBorder(XMLReader $xmlReader, DOMElement $oElement, Border $oBorder): void
1377    {
1378        if ($oElement->hasAttribute('w')) {
1379            $oBorder->setLineWidth((int) ((int) $oElement->getAttribute('w') / 12700));
1380        }
1381        if ($oElement->hasAttribute('cmpd')) {
1382            $oBorder->setLineStyle($oElement->getAttribute('cmpd'));
1383        }
1384
1385        $oElementNoFill = $xmlReader->getElement('a:noFill', $oElement);
1386        if ($oElementNoFill instanceof DOMElement && Border::LINE_SINGLE == $oBorder->getLineStyle()) {
1387            $oBorder->setLineStyle(Border::LINE_NONE);
1388        }
1389
1390        $oElementColor = $xmlReader->getElement('a:solidFill/a:srgbClr', $oElement);
1391        if ($oElementColor instanceof DOMElement) {
1392            $oBorder->setColor($this->loadStyleColor($xmlReader, $oElementColor));
1393        }
1394
1395        $oElementDashStyle = $xmlReader->getElement('a:prstDash', $oElement);
1396        if ($oElementDashStyle instanceof DOMElement && $oElementDashStyle->hasAttribute('val')) {
1397            $oBorder->setDashStyle($oElementDashStyle->getAttribute('val'));
1398        }
1399    }
1400
1401    protected function loadStyleColor(XMLReader $xmlReader, DOMElement $oElement): Color
1402    {
1403        $oColor = new Color();
1404        $oColor->setRGB($oElement->getAttribute('val'));
1405        $oElementAlpha = $xmlReader->getElement('a:alpha', $oElement);
1406        if ($oElementAlpha instanceof DOMElement && $oElementAlpha->hasAttribute('val')) {
1407            $alpha = strtoupper(dechex((((int) $oElementAlpha->getAttribute('val') / 1000) / 100) * 255));
1408            $oColor->setRGB($oElement->getAttribute('val'), $alpha);
1409        }
1410
1411        return $oColor;
1412    }
1413
1414    protected function loadStyleFill(XMLReader $xmlReader, DOMElement $oElement): ?Fill
1415    {
1416        // Gradient fill
1417        $oElementFill = $xmlReader->getElement('a:gradFill', $oElement);
1418        if ($oElementFill instanceof DOMElement) {
1419            $oFill = new Fill();
1420            $oFill->setFillType(Fill::FILL_GRADIENT_LINEAR);
1421
1422            $oElementColor = $xmlReader->getElement('a:gsLst/a:gs[@pos="0"]/a:srgbClr', $oElementFill);
1423            if ($oElementColor instanceof DOMElement && $oElementColor->hasAttribute('val')) {
1424                $oFill->setStartColor($this->loadStyleColor($xmlReader, $oElementColor));
1425            }
1426
1427            $oElementColor = $xmlReader->getElement('a:gsLst/a:gs[@pos="100000"]/a:srgbClr', $oElementFill);
1428            if ($oElementColor instanceof DOMElement && $oElementColor->hasAttribute('val')) {
1429                $oFill->setEndColor($this->loadStyleColor($xmlReader, $oElementColor));
1430            }
1431
1432            $oRotation = $xmlReader->getElement('a:lin', $oElementFill);
1433            if ($oRotation instanceof DOMElement && $oRotation->hasAttribute('ang')) {
1434                $oFill->setRotation(CommonDrawing::angleToDegrees((int) $oRotation->getAttribute('ang')));
1435            }
1436
1437            return $oFill;
1438        }
1439
1440        // Solid fill
1441        $oElementFill = $xmlReader->getElement('a:solidFill', $oElement);
1442        if ($oElementFill instanceof DOMElement) {
1443            $oFill = new Fill();
1444            $oFill->setFillType(Fill::FILL_SOLID);
1445
1446            $oElementColor = $xmlReader->getElement('a:srgbClr', $oElementFill);
1447            if ($oElementColor instanceof DOMElement) {
1448                $oFill->setStartColor($this->loadStyleColor($xmlReader, $oElementColor));
1449            }
1450
1451            return $oFill;
1452        }
1453
1454        return null;
1455    }
1456
1457    protected function loadRels(string $fileRels): void
1458    {
1459        $sPart = $this->oZip->getFromName($fileRels);
1460        if (false !== $sPart) {
1461            $xmlReader = new XMLReader();
1462            // @phpstan-ignore-next-line
1463            if ($xmlReader->getDomFromString($sPart)) {
1464                foreach ($xmlReader->getElements('*') as $oNode) {
1465                    if (!($oNode instanceof DOMElement)) {
1466                        continue;
1467                    }
1468                    $this->arrayRels[$fileRels][$oNode->getAttribute('Id')] = [
1469                        'Target' => $oNode->getAttribute('Target'),
1470                        'Type' => $oNode->getAttribute('Type'),
1471                    ];
1472                }
1473            }
1474        }
1475    }
1476
1477    /**
1478     * @param AbstractSlide|Note $oSlide
1479     * @param DOMNodeList<DOMNode> $oElements
1480     *
1481     * @internal param $baseFile
1482     */
1483    protected function loadSlideShapes($oSlide, DOMNodeList $oElements, XMLReader $xmlReader): void
1484    {
1485        foreach ($oElements as $oNode) {
1486            if (!($oNode instanceof DOMElement)) {
1487                continue;
1488            }
1489            switch ($oNode->tagName) {
1490                case 'p:graphicFrame':
1491                    if ($oSlide instanceof AbstractSlide) {
1492                        $this->loadShapeTable($xmlReader, $oNode, $oSlide);
1493                    }
1494
1495                    break;
1496                case 'p:pic':
1497                    if ($oSlide instanceof AbstractSlide) {
1498                        $this->loadShapeDrawing($xmlReader, $oNode, $oSlide);
1499                    }
1500
1501                    break;
1502                case 'p:sp':
1503                    $this->loadShapeRichText($xmlReader, $oNode, $oSlide);
1504
1505                    break;
1506                default:
1507                    //throw new FeatureNotImplementedException();
1508            }
1509        }
1510    }
1511}