• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPOffice / PHPPresentation / 13659058762

04 Mar 2025 05:08PM UTC coverage: 91.639% (+1.1%) from 90.516%
13659058762

push

github

web-flow
PowerPoint2007 Reader : Support for BarChart (#856)

188 of 199 new or added lines in 9 files covered. (94.47%)

9908 of 10812 relevant lines covered (91.64%)

43.19 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.51
/src/PhpPresentation/Reader/PowerPoint2007.php
1
<?php
2

3
/**
4
 * This file is part of PHPPresentation - A pure PHP library for reading and writing
5
 * presentations documents.
6
 *
7
 * PHPPresentation is free software distributed under the terms of the GNU Lesser
8
 * General Public License version 3 as published by the Free Software Foundation.
9
 *
10
 * For the full copyright and license information, please read the LICENSE
11
 * file that was distributed with this source code. For the full list of
12
 * contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
13
 *
14
 * @see        https://github.com/PHPOffice/PHPPresentation
15
 *
16
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
17
 */
18

19
declare(strict_types=1);
20

21
namespace PhpOffice\PhpPresentation\Reader;
22

23
use DateTime;
24
use DOMElement;
25
use DOMNode;
26
use DOMNodeList;
27
use PhpOffice\Common\Drawing as CommonDrawing;
28
use PhpOffice\Common\XMLReader;
29
use PhpOffice\PhpPresentation\DocumentLayout;
30
use PhpOffice\PhpPresentation\DocumentProperties;
31
use PhpOffice\PhpPresentation\Exception\FeatureNotImplementedException;
32
use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
33
use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
34
use PhpOffice\PhpPresentation\PhpPresentation;
35
use PhpOffice\PhpPresentation\PresentationProperties;
36
use PhpOffice\PhpPresentation\Shape\Chart;
37
use PhpOffice\PhpPresentation\Shape\Drawing\Base64;
38
use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
39
use PhpOffice\PhpPresentation\Shape\Hyperlink;
40
use PhpOffice\PhpPresentation\Shape\Placeholder;
41
use PhpOffice\PhpPresentation\Shape\RichText;
42
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
43
use PhpOffice\PhpPresentation\Shape\Table\Cell;
44
use PhpOffice\PhpPresentation\Slide;
45
use PhpOffice\PhpPresentation\Slide\AbstractSlide;
46
use PhpOffice\PhpPresentation\Slide\Note;
47
use PhpOffice\PhpPresentation\Slide\SlideLayout;
48
use PhpOffice\PhpPresentation\Slide\SlideMaster;
49
use PhpOffice\PhpPresentation\Style\Border;
50
use PhpOffice\PhpPresentation\Style\Borders;
51
use PhpOffice\PhpPresentation\Style\Bullet;
52
use PhpOffice\PhpPresentation\Style\Color;
53
use PhpOffice\PhpPresentation\Style\Fill;
54
use PhpOffice\PhpPresentation\Style\Font;
55
use PhpOffice\PhpPresentation\Style\Outline;
56
use PhpOffice\PhpPresentation\Style\SchemeColor;
57
use PhpOffice\PhpPresentation\Style\Shadow;
58
use PhpOffice\PhpPresentation\Style\TextStyle;
59
use ZipArchive;
60

61
/**
62
 * Serialized format reader.
63
 */
64
class PowerPoint2007 implements ReaderInterface
65
{
66
    /**
67
     * Output Object.
68
     *
69
     * @var PhpPresentation
70
     */
71
    protected $oPhpPresentation;
72

73
    /**
74
     * Output Object.
75
     *
76
     * @var ZipArchive
77
     */
78
    protected $oZip;
79

80
    /**
81
     * @var array<string, array<string, array<string, string>>>
82
     */
83
    protected $arrayRels = [];
84

85
    /**
86
     * @var SlideLayout[]
87
     */
88
    protected $arraySlideLayouts = [];
89

90
    /**
91
     * @var string
92
     */
93
    protected $filename;
94

95
    /**
96
     * @var string
97
     */
98
    protected $fileRels;
99

100
    /**
101
     * @var bool
102
     */
103
    protected $loadImages = true;
104

105
    /**
106
     * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
107
     */
108
    public function canRead(string $pFilename): bool
109
    {
110
        return $this->fileSupportsUnserializePhpPresentation($pFilename);
2✔
111
    }
112

113
    /**
114
     * Does a file support UnserializePhpPresentation ?
115
     */
116
    public function fileSupportsUnserializePhpPresentation(string $pFilename = ''): bool
117
    {
118
        // Check if file exists
119
        if (!file_exists($pFilename)) {
13✔
120
            throw new FileNotFoundException($pFilename);
2✔
121
        }
122

123
        $oZip = new ZipArchive();
11✔
124
        // Is it a zip ?
125
        if (true === $oZip->open($pFilename)) {
11✔
126
            // Is it an OpenXML Document ?
127
            // Is it a Presentation ?
128
            if (is_array($oZip->statName('[Content_Types].xml')) && is_array($oZip->statName('ppt/presentation.xml'))) {
9✔
129
                return true;
9✔
130
            }
131
        }
132

133
        return false;
3✔
134
    }
135

136
    /**
137
     * Loads PhpPresentation Serialized file.
138
     */
139
    public function load(string $pFilename, int $flags = 0): PhpPresentation
140
    {
141
        // Unserialize... First make sure the file supports it!
142
        if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
10✔
143
            throw new InvalidFileFormatException($pFilename, self::class);
1✔
144
        }
145

146
        $this->loadImages = !((bool) ($flags & self::SKIP_IMAGES));
8✔
147

148
        return $this->loadFile($pFilename);
8✔
149
    }
150

151
    /**
152
     * Load PhpPresentation Serialized file.
153
     */
154
    protected function loadFile(string $pFilename): PhpPresentation
155
    {
156
        $this->oPhpPresentation = new PhpPresentation();
8✔
157
        $this->oPhpPresentation->removeSlideByIndex();
8✔
158
        $this->oPhpPresentation->setAllMasterSlides([]);
8✔
159
        $this->filename = $pFilename;
8✔
160

161
        $this->oZip = new ZipArchive();
8✔
162
        $this->oZip->open($this->filename);
8✔
163
        $docPropsCore = $this->oZip->getFromName('docProps/core.xml');
8✔
164
        if (false !== $docPropsCore) {
8✔
165
            $this->loadDocumentProperties($docPropsCore);
8✔
166
        }
167

168
        $docThumbnail = $this->oZip->getFromName('_rels/.rels');
8✔
169
        if ($docThumbnail !== false) {
8✔
170
            $this->loadThumbnailProperties($docThumbnail);
8✔
171
        }
172

173
        $docPropsCustom = $this->oZip->getFromName('docProps/custom.xml');
8✔
174
        if (false !== $docPropsCustom) {
8✔
175
            $this->loadCustomProperties($docPropsCustom);
2✔
176
        }
177

178
        $pptViewProps = $this->oZip->getFromName('ppt/viewProps.xml');
8✔
179
        if (false !== $pptViewProps) {
8✔
180
            $this->loadViewProperties($pptViewProps);
7✔
181
        }
182

183
        $pptPresentation = $this->oZip->getFromName('ppt/presentation.xml');
8✔
184
        if (false !== $pptPresentation) {
8✔
185
            $this->loadDocumentLayout($pptPresentation);
8✔
186
            $this->loadSlides($pptPresentation);
8✔
187
        }
188

189
        $pptPresProps = $this->oZip->getFromName('ppt/presProps.xml');
8✔
190
        if (false !== $pptPresProps) {
8✔
191
            $this->loadPresentationProperties($pptPresentation);
8✔
192
        }
193

194
        return $this->oPhpPresentation;
8✔
195
    }
196

197
    /**
198
     * Read Document Layout.
199
     */
200
    protected function loadDocumentLayout(string $sPart): void
201
    {
202
        $xmlReader = new XMLReader();
8✔
203
        // @phpstan-ignore-next-line
204
        if ($xmlReader->getDomFromString($sPart)) {
8✔
205
            foreach ($xmlReader->getElements('/p:presentation/p:sldSz') as $oElement) {
8✔
206
                if (!($oElement instanceof DOMElement)) {
8✔
207
                    continue;
×
208
                }
209
                $type = $oElement->getAttribute('type');
8✔
210
                $oLayout = $this->oPhpPresentation->getLayout();
8✔
211
                if (DocumentLayout::LAYOUT_CUSTOM == $type) {
8✔
212
                    $oLayout->setCX((float) $oElement->getAttribute('cx'));
1✔
213
                    $oLayout->setCY((float) $oElement->getAttribute('cy'));
1✔
214
                } else {
215
                    $oLayout->setDocumentLayout($type, true);
7✔
216
                    if ($oElement->getAttribute('cx') < $oElement->getAttribute('cy')) {
7✔
217
                        $oLayout->setDocumentLayout($type, false);
×
218
                    }
219
                }
220
            }
221
        }
222
    }
223

224
    /**
225
     * Read Document Properties.
226
     */
227
    protected function loadDocumentProperties(string $sPart): void
228
    {
229
        $xmlReader = new XMLReader();
8✔
230
        // @phpstan-ignore-next-line
231
        if ($xmlReader->getDomFromString($sPart)) {
8✔
232
            $arrayProperties = [
8✔
233
                '/cp:coreProperties/dc:creator' => 'setCreator',
8✔
234
                '/cp:coreProperties/cp:lastModifiedBy' => 'setLastModifiedBy',
8✔
235
                '/cp:coreProperties/dc:title' => 'setTitle',
8✔
236
                '/cp:coreProperties/dc:description' => 'setDescription',
8✔
237
                '/cp:coreProperties/dc:subject' => 'setSubject',
8✔
238
                '/cp:coreProperties/cp:keywords' => 'setKeywords',
8✔
239
                '/cp:coreProperties/cp:category' => 'setCategory',
8✔
240
                '/cp:coreProperties/dcterms:created' => 'setCreated',
8✔
241
                '/cp:coreProperties/dcterms:modified' => 'setModified',
8✔
242
                '/cp:coreProperties/cp:revision' => 'setRevision',
8✔
243
                '/cp:coreProperties/cp:contentStatus' => 'setStatus',
8✔
244
            ];
8✔
245
            $oProperties = $this->oPhpPresentation->getDocumentProperties();
8✔
246
            foreach ($arrayProperties as $path => $property) {
8✔
247
                $oElement = $xmlReader->getElement($path);
8✔
248
                if ($oElement instanceof DOMElement) {
8✔
249
                    if ($oElement->hasAttribute('xsi:type') && 'dcterms:W3CDTF' == $oElement->getAttribute('xsi:type')) {
8✔
250
                        $dateTime = DateTime::createFromFormat(DateTime::W3C, $oElement->nodeValue);
8✔
251
                        $oProperties->{$property}($dateTime->getTimestamp());
8✔
252
                    } else {
253
                        $oProperties->{$property}($oElement->nodeValue);
8✔
254
                    }
255
                }
256
            }
257
        }
258
    }
259

260
    /**
261
     * Read information of the document thumbnail.
262
     */
263
    protected function loadThumbnailProperties(string $sPart): void
264
    {
265
        $xmlReader = new XMLReader();
8✔
266
        $xmlReader->getDomFromString($sPart);
8✔
267

268
        $oElement = $xmlReader->getElement('*[@Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"]');
8✔
269
        if ($oElement instanceof DOMElement) {
8✔
270
            $path = $oElement->getAttribute('Target');
3✔
271
            $this->oPhpPresentation
3✔
272
                ->getPresentationProperties()
3✔
273
                ->setThumbnailPath('', PresentationProperties::THUMBNAIL_DATA, $this->oZip->getFromName($path));
3✔
274
        }
275
    }
276

277
    /**
278
     * Read Custom Properties.
279
     */
280
    protected function loadCustomProperties(string $sPart): void
281
    {
282
        $xmlReader = new XMLReader();
2✔
283
        $sPart = str_replace(' xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties"', '', $sPart);
2✔
284
        // @phpstan-ignore-next-line
285
        if ($xmlReader->getDomFromString($sPart)) {
2✔
286
            foreach ($xmlReader->getElements('/Properties/property[@fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"]') as $element) {
2✔
287
                if (!$element->hasAttribute('name')) {
2✔
288
                    continue;
×
289
                }
290
                $propertyName = $element->getAttribute('name');
2✔
291
                if ($propertyName == '_MarkAsFinal') {
2✔
292
                    $attributeElement = $xmlReader->getElement('vt:bool', $element);
1✔
293
                    if ($attributeElement && 'true' == $attributeElement->nodeValue) {
1✔
294
                        $this->oPhpPresentation->getPresentationProperties()->markAsFinal(true);
1✔
295
                    }
296
                } else {
297
                    $attributeTypeInt = $xmlReader->getElement('vt:i4', $element);
1✔
298
                    $attributeTypeFloat = $xmlReader->getElement('vt:r8', $element);
1✔
299
                    $attributeTypeBoolean = $xmlReader->getElement('vt:bool', $element);
1✔
300
                    $attributeTypeDate = $xmlReader->getElement('vt:filetime', $element);
1✔
301
                    $attributeTypeString = $xmlReader->getElement('vt:lpwstr', $element);
1✔
302

303
                    if ($attributeTypeInt) {
1✔
304
                        $propertyType = DocumentProperties::PROPERTY_TYPE_INTEGER;
×
305
                        $propertyValue = (int) $attributeTypeInt->nodeValue;
×
306
                    } elseif ($attributeTypeFloat) {
1✔
307
                        $propertyType = DocumentProperties::PROPERTY_TYPE_FLOAT;
×
308
                        $propertyValue = (float) $attributeTypeFloat->nodeValue;
×
309
                    } elseif ($attributeTypeBoolean) {
1✔
310
                        $propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
×
311
                        $propertyValue = $attributeTypeBoolean->nodeValue == 'true' ? true : false;
×
312
                    } elseif ($attributeTypeDate) {
1✔
313
                        $propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
×
314
                        $propertyValue = strtotime($attributeTypeDate->nodeValue);
×
315
                    } else {
316
                        $propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
1✔
317
                        $propertyValue = $attributeTypeString->nodeValue;
1✔
318
                    }
319

320
                    $this->oPhpPresentation->getDocumentProperties()->setCustomProperty($propertyName, $propertyValue, $propertyType);
1✔
321
                }
322
            }
323
        }
324
    }
325

326
    /**
327
     * Read Presentation Properties.
328
     */
329
    protected function loadPresentationProperties(string $sPart): void
330
    {
331
        $xmlReader = new XMLReader();
8✔
332
        // @phpstan-ignore-next-line
333
        if ($xmlReader->getDomFromString($sPart)) {
8✔
334
            $element = $xmlReader->getElement('/p:presentationPr/p:showPr');
8✔
335
            if ($element instanceof DOMElement) {
8✔
336
                if ($element->hasAttribute('loop')) {
×
337
                    $this->oPhpPresentation->getPresentationProperties()->setLoopContinuouslyUntilEsc(
×
338
                        (bool) $element->getAttribute('loop')
×
339
                    );
×
340
                }
341
                if (null !== $xmlReader->getElement('p:present', $element)) {
×
342
                    $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
×
343
                        PresentationProperties::SLIDESHOW_TYPE_PRESENT
×
344
                    );
×
345
                }
346
                if (null !== $xmlReader->getElement('p:browse', $element)) {
×
347
                    $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
×
348
                        PresentationProperties::SLIDESHOW_TYPE_BROWSE
×
349
                    );
×
350
                }
351
                if (null !== $xmlReader->getElement('p:kiosk', $element)) {
×
352
                    $this->oPhpPresentation->getPresentationProperties()->setSlideshowType(
×
353
                        PresentationProperties::SLIDESHOW_TYPE_KIOSK
×
354
                    );
×
355
                }
356
            }
357
        }
358
    }
359

360
    /**
361
     * Read View Properties.
362
     */
363
    protected function loadViewProperties(string $sPart): void
364
    {
365
        $xmlReader = new XMLReader();
7✔
366
        // @phpstan-ignore-next-line
367
        if ($xmlReader->getDomFromString($sPart)) {
7✔
368
            $pathZoom = '/p:viewPr/p:slideViewPr/p:cSldViewPr/p:cViewPr/p:scale/a:sx';
7✔
369
            $oElement = $xmlReader->getElement($pathZoom);
7✔
370
            if ($oElement instanceof DOMElement) {
7✔
371
                if ($oElement->hasAttribute('d') && $oElement->hasAttribute('n')) {
5✔
372
                    $this->oPhpPresentation->getPresentationProperties()->setZoom((int) $oElement->getAttribute('n') / (int) $oElement->getAttribute('d'));
5✔
373
                }
374
            }
375
        }
376
    }
377

378
    /**
379
     * Extract all slides.
380
     */
381
    protected function loadSlides(string $sPart): void
382
    {
383
        $xmlReader = new XMLReader();
8✔
384
        // @phpstan-ignore-next-line
385
        if ($xmlReader->getDomFromString($sPart)) {
8✔
386
            $fileRels = 'ppt/_rels/presentation.xml.rels';
8✔
387
            $this->loadRels($fileRels);
8✔
388
            // Load the Masterslides
389
            $this->loadMasterSlides($xmlReader, $fileRels);
8✔
390
            // Continue with loading the slides
391
            foreach ($xmlReader->getElements('/p:presentation/p:sldIdLst/p:sldId') as $oElement) {
8✔
392
                if (!($oElement instanceof DOMElement)) {
8✔
393
                    continue;
×
394
                }
395
                $rId = $oElement->getAttribute('r:id');
8✔
396
                $pathSlide = isset($this->arrayRels[$fileRels][$rId]) ? $this->arrayRels[$fileRels][$rId]['Target'] : '';
8✔
397
                if (!empty($pathSlide)) {
8✔
398
                    $pptSlide = $this->oZip->getFromName('ppt/' . $pathSlide);
8✔
399
                    if (false !== $pptSlide) {
8✔
400
                        $slideRels = 'ppt/slides/_rels/' . basename($pathSlide) . '.rels';
8✔
401
                        $this->loadRels($slideRels);
8✔
402
                        $this->loadSlide($pptSlide, basename($pathSlide));
8✔
403
                        foreach ($this->arrayRels[$slideRels] as $rel) {
8✔
404
                            if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide' == $rel['Type']) {
8✔
405
                                $this->loadSlideNote(basename($rel['Target']), $this->oPhpPresentation->getActiveSlide());
1✔
406
                            }
407
                        }
408
                    }
409
                }
410
            }
411
        }
412
    }
413

414
    /**
415
     * Extract all MasterSlides.
416
     */
417
    protected function loadMasterSlides(XMLReader $xmlReader, string $fileRels): void
418
    {
419
        // Get all the MasterSlide Id's from the presentation.xml file
420
        foreach ($xmlReader->getElements('/p:presentation/p:sldMasterIdLst/p:sldMasterId') as $oElement) {
8✔
421
            if (!($oElement instanceof DOMElement)) {
8✔
422
                continue;
×
423
            }
424
            $rId = $oElement->getAttribute('r:id');
8✔
425
            // Get the path to the masterslide from the array with _rels files
426
            $pathMasterSlide = isset($this->arrayRels[$fileRels][$rId]) ?
8✔
427
                $this->arrayRels[$fileRels][$rId]['Target'] : '';
8✔
428
            if (!empty($pathMasterSlide)) {
8✔
429
                $pptMasterSlide = $this->oZip->getFromName('ppt/' . $pathMasterSlide);
8✔
430
                if (false !== $pptMasterSlide) {
8✔
431
                    $this->loadRels('ppt/slideMasters/_rels/' . basename($pathMasterSlide) . '.rels');
8✔
432
                    $this->loadMasterSlide($pptMasterSlide, basename($pathMasterSlide));
8✔
433
                }
434
            }
435
        }
436
    }
437

438
    /**
439
     * Extract data from slide.
440
     */
441
    protected function loadSlide(string $sPart, string $baseFile): void
442
    {
443
        $xmlReader = new XMLReader();
8✔
444
        // @phpstan-ignore-next-line
445
        if ($xmlReader->getDomFromString($sPart)) {
8✔
446
            $xmlReader->registerNamespace('c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
8✔
447
            // Core
448
            $oSlide = $this->oPhpPresentation->createSlide();
8✔
449
            $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);
8✔
450
            $oSlide->setRelsIndex('ppt/slides/_rels/' . $baseFile . '.rels');
8✔
451

452
            // Background
453
            $oElement = $xmlReader->getElement('/p:sld/p:cSld/p:bg/p:bgPr');
8✔
454
            if ($oElement instanceof DOMElement) {
8✔
455
                $oElementColor = $xmlReader->getElement('a:solidFill/a:srgbClr', $oElement);
1✔
456
                if ($oElementColor instanceof DOMElement) {
1✔
457
                    // Color
458
                    $oColor = new Color();
1✔
459
                    $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
1✔
460
                    // Background
461
                    $oBackground = new Slide\Background\Color();
1✔
462
                    $oBackground->setColor($oColor);
1✔
463
                    // Slide Background
464
                    $oSlide = $this->oPhpPresentation->getActiveSlide();
1✔
465
                    $oSlide->setBackground($oBackground);
1✔
466
                }
467
                $oElementColor = $xmlReader->getElement('a:solidFill/a:schemeClr', $oElement);
1✔
468
                if ($oElementColor instanceof DOMElement) {
1✔
469
                    // Color
470
                    $oColor = new SchemeColor();
×
471
                    $oColor->setValue($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
×
472
                    // Background
473
                    $oBackground = new Slide\Background\SchemeColor();
×
474
                    $oBackground->setSchemeColor($oColor);
×
475
                    // Slide Background
476
                    $oSlide = $this->oPhpPresentation->getActiveSlide();
×
477
                    $oSlide->setBackground($oBackground);
×
478
                }
479
                $oElementImage = $xmlReader->getElement('a:blipFill/a:blip', $oElement);
1✔
480
                if ($oElementImage instanceof DOMElement) {
1✔
481
                    $relImg = $this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'][$oElementImage->getAttribute('r:embed')];
×
482
                    if (is_array($relImg)) {
×
483
                        // File
484
                        $pathImage = 'ppt/slides/' . $relImg['Target'];
×
485
                        $pathImage = explode('/', $pathImage);
×
486
                        foreach ($pathImage as $key => $partPath) {
×
487
                            if ('..' == $partPath) {
×
488
                                unset($pathImage[$key - 1], $pathImage[$key]);
×
489
                            }
490
                        }
491
                        $pathImage = implode('/', $pathImage);
×
492
                        $contentImg = $this->oZip->getFromName($pathImage);
×
493

494
                        $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderPpt2007Bkg');
×
495
                        file_put_contents($tmpBkgImg, $contentImg);
×
496
                        // Background
497
                        $oBackground = new Slide\Background\Image();
×
498
                        $oBackground
×
499
                            ->setPath($tmpBkgImg)
×
500
                            ->setExtension(pathinfo($pathImage, PATHINFO_EXTENSION));
×
501
                        // Slide Background
502
                        $oSlide = $this->oPhpPresentation->getActiveSlide();
×
503
                        $oSlide->setBackground($oBackground);
×
504
                    }
505
                }
506
            }
507

508
            // Shapes
509
            $arrayElements = $xmlReader->getElements('/p:sld/p:cSld/p:spTree/*');
8✔
510
            $this->loadSlideShapes($xmlReader, $oSlide, $arrayElements, $xmlReader);
8✔
511

512
            // Layout
513
            $oSlide = $this->oPhpPresentation->getActiveSlide();
8✔
514
            foreach ($this->arrayRels['ppt/slides/_rels/' . $baseFile . '.rels'] as $valueRel) {
8✔
515
                if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout' == $valueRel['Type']) {
8✔
516
                    $layoutBasename = basename($valueRel['Target']);
8✔
517
                    if (array_key_exists($layoutBasename, $this->arraySlideLayouts)) {
8✔
518
                        $oSlide->setSlideLayout($this->arraySlideLayouts[$layoutBasename]);
8✔
519
                    }
520

521
                    break;
8✔
522
                }
523
            }
524
        }
525
    }
526

527
    protected function loadMasterSlide(string $sPart, string $baseFile): void
528
    {
529
        $xmlReader = new XMLReader();
8✔
530
        // @phpstan-ignore-next-line
531
        if ($xmlReader->getDomFromString($sPart)) {
8✔
532
            // Core
533
            $oSlideMaster = $this->oPhpPresentation->createMasterSlide();
8✔
534
            $oSlideMaster->setTextStyles(new TextStyle(false));
8✔
535
            $oSlideMaster->setRelsIndex('ppt/slideMasters/_rels/' . $baseFile . '.rels');
8✔
536

537
            // Background
538
            $oElement = $xmlReader->getElement('/p:sldMaster/p:cSld/p:bg');
8✔
539
            if ($oElement instanceof DOMElement) {
8✔
540
                $this->loadSlideBackground($xmlReader, $oElement, $oSlideMaster);
8✔
541
            }
542

543
            // Shapes
544
            $arrayElements = $xmlReader->getElements('/p:sldMaster/p:cSld/p:spTree/*');
8✔
545
            $this->loadSlideShapes($xmlReader, $oSlideMaster, $arrayElements, $xmlReader);
8✔
546

547
            // Header & Footer
548

549
            // ColorMapping
550
            $colorMap = [];
8✔
551
            $oElement = $xmlReader->getElement('/p:sldMaster/p:clrMap');
8✔
552
            if ($oElement->hasAttributes()) {
8✔
553
                foreach ($oElement->attributes as $attr) {
8✔
554
                    $colorMap[$attr->nodeName] = $attr->nodeValue;
8✔
555
                }
556
                $oSlideMaster->colorMap->setMapping($colorMap);
8✔
557
            }
558

559
            // TextStyles
560
            $arrayElementTxStyles = $xmlReader->getElements('/p:sldMaster/p:txStyles/*');
8✔
561
            foreach ($arrayElementTxStyles as $oElementTxStyle) {
8✔
562
                $arrayElementsLvl = $xmlReader->getElements('/p:sldMaster/p:txStyles/' . $oElementTxStyle->nodeName . '/*');
7✔
563
                foreach ($arrayElementsLvl as $oElementLvl) {
7✔
564
                    if (!($oElementLvl instanceof DOMElement) || 'a:extLst' == $oElementLvl->nodeName) {
7✔
565
                        continue;
1✔
566
                    }
567
                    $oRTParagraph = new Paragraph();
7✔
568

569
                    if ('a:defPPr' == $oElementLvl->nodeName) {
7✔
570
                        $level = 0;
6✔
571
                    } else {
572
                        $level = str_replace('a:lvl', '', $oElementLvl->nodeName);
7✔
573
                        $level = str_replace('pPr', '', $level);
7✔
574
                        $level = (int) $level;
7✔
575
                    }
576

577
                    if ($oElementLvl->hasAttribute('algn')) {
7✔
578
                        $oRTParagraph->getAlignment()->setHorizontal($oElementLvl->getAttribute('algn'));
7✔
579
                    }
580
                    if ($oElementLvl->hasAttribute('marL')) {
7✔
581
                        $val = (int) $oElementLvl->getAttribute('marL');
5✔
582
                        $val = (int) CommonDrawing::emuToPixels((int) $val);
5✔
583
                        $oRTParagraph->getAlignment()->setMarginLeft($val);
5✔
584
                    }
585
                    if ($oElementLvl->hasAttribute('marR')) {
7✔
586
                        $val = (int) $oElementLvl->getAttribute('marR');
2✔
587
                        $val = (int) CommonDrawing::emuToPixels((int) $val);
2✔
588
                        $oRTParagraph->getAlignment()->setMarginRight($val);
2✔
589
                    }
590
                    if ($oElementLvl->hasAttribute('indent')) {
7✔
591
                        $val = (int) $oElementLvl->getAttribute('indent');
5✔
592
                        $val = (int) CommonDrawing::emuToPixels((int) $val);
5✔
593
                        $oRTParagraph->getAlignment()->setIndent($val);
5✔
594
                    }
595
                    $oElementLvlDefRPR = $xmlReader->getElement('a:defRPr', $oElementLvl);
7✔
596
                    if ($oElementLvlDefRPR instanceof DOMElement) {
7✔
597
                        if ($oElementLvlDefRPR->hasAttribute('sz')) {
7✔
598
                            $oRTParagraph->getFont()->setSize((int) ((int) $oElementLvlDefRPR->getAttribute('sz') / 100));
7✔
599
                        }
600
                        if ($oElementLvlDefRPR->hasAttribute('b') && 1 == $oElementLvlDefRPR->getAttribute('b')) {
7✔
601
                            $oRTParagraph->getFont()->setBold(true);
1✔
602
                        }
603
                        if ($oElementLvlDefRPR->hasAttribute('i') && 1 == $oElementLvlDefRPR->getAttribute('i')) {
7✔
604
                            $oRTParagraph->getFont()->setItalic(true);
×
605
                        }
606
                    }
607
                    $oElementSchemeColor = $xmlReader->getElement('a:defRPr/a:solidFill/a:schemeClr', $oElementLvl);
7✔
608
                    if ($oElementSchemeColor instanceof DOMElement) {
7✔
609
                        if ($oElementSchemeColor->hasAttribute('val')) {
5✔
610
                            $oSchemeColor = new SchemeColor();
5✔
611
                            $oSchemeColor->setValue($oElementSchemeColor->getAttribute('val'));
5✔
612
                            $oRTParagraph->getFont()->setColor($oSchemeColor);
5✔
613
                        }
614
                    }
615

616
                    switch ($oElementTxStyle->nodeName) {
7✔
617
                        case 'p:bodyStyle':
7✔
618
                            $oSlideMaster->getTextStyles()->setBodyStyleAtLvl($oRTParagraph, $level);
7✔
619

620
                            break;
7✔
621
                        case 'p:otherStyle':
7✔
622
                            $oSlideMaster->getTextStyles()->setOtherStyleAtLvl($oRTParagraph, $level);
7✔
623

624
                            break;
7✔
625
                        case 'p:titleStyle':
7✔
626
                            $oSlideMaster->getTextStyles()->setTitleStyleAtLvl($oRTParagraph, $level);
7✔
627

628
                            break;
7✔
629
                    }
630
                }
631
            }
632

633
            // Load the theme
634
            foreach ($this->arrayRels[$oSlideMaster->getRelsIndex()] as $arrayRel) {
8✔
635
                if ('http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme' == $arrayRel['Type']) {
8✔
636
                    $pptTheme = $this->oZip->getFromName('ppt/' . substr($arrayRel['Target'], strrpos($arrayRel['Target'], '../') + 3));
8✔
637
                    if (false !== $pptTheme) {
8✔
638
                        $this->loadTheme($pptTheme, $oSlideMaster);
8✔
639
                    }
640

641
                    break;
8✔
642
                }
643
            }
644

645
            // Load the Layoutslide
646
            foreach ($xmlReader->getElements('/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId') as $oElement) {
8✔
647
                if (!($oElement instanceof DOMElement)) {
8✔
648
                    continue;
×
649
                }
650
                $rId = $oElement->getAttribute('r:id');
8✔
651
                // Get the path to the masterslide from the array with _rels files
652
                $pathLayoutSlide = isset($this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]) ?
8✔
653
                    $this->arrayRels[$oSlideMaster->getRelsIndex()][$rId]['Target'] : '';
8✔
654
                if (!empty($pathLayoutSlide)) {
8✔
655
                    $pptLayoutSlide = $this->oZip->getFromName('ppt/' . substr($pathLayoutSlide, strrpos($pathLayoutSlide, '../') + 3));
8✔
656
                    if (false !== $pptLayoutSlide) {
8✔
657
                        $this->loadRels('ppt/slideLayouts/_rels/' . basename($pathLayoutSlide) . '.rels');
8✔
658
                        $oSlideMaster->addSlideLayout(
8✔
659
                            $this->loadLayoutSlide($pptLayoutSlide, basename($pathLayoutSlide), $oSlideMaster)
8✔
660
                        );
8✔
661
                    }
662
                }
663
            }
664
        }
665
    }
666

667
    protected function loadLayoutSlide(string $sPart, string $baseFile, SlideMaster $oSlideMaster): ?SlideLayout
668
    {
669
        $xmlReader = new XMLReader();
8✔
670
        // @phpstan-ignore-next-line
671
        if ($xmlReader->getDomFromString($sPart)) {
8✔
672
            // Core
673
            $oSlideLayout = new SlideLayout($oSlideMaster);
8✔
674
            $oSlideLayout->setRelsIndex('ppt/slideLayouts/_rels/' . $baseFile . '.rels');
8✔
675

676
            // Name
677
            $oElement = $xmlReader->getElement('/p:sldLayout/p:cSld');
8✔
678
            if ($oElement instanceof DOMElement && $oElement->hasAttribute('name')) {
8✔
679
                $oSlideLayout->setLayoutName($oElement->getAttribute('name'));
8✔
680
            }
681

682
            // Background
683
            $oElement = $xmlReader->getElement('/p:sldLayout/p:cSld/p:bg');
8✔
684
            if ($oElement instanceof DOMElement) {
8✔
685
                $this->loadSlideBackground($xmlReader, $oElement, $oSlideLayout);
2✔
686
            }
687

688
            // ColorMapping
689
            $oElement = $xmlReader->getElement('/p:sldLayout/p:clrMapOvr/a:overrideClrMapping');
8✔
690
            if ($oElement instanceof DOMElement && $oElement->hasAttributes()) {
8✔
691
                $colorMap = [];
1✔
692
                foreach ($oElement->attributes as $attr) {
1✔
693
                    $colorMap[$attr->nodeName] = $attr->nodeValue;
1✔
694
                }
695
                $oSlideLayout->colorMap->setMapping($colorMap);
1✔
696
            }
697

698
            // Shapes
699
            $oElements = $xmlReader->getElements('/p:sldLayout/p:cSld/p:spTree/*');
8✔
700
            $this->loadSlideShapes($xmlReader, $oSlideLayout, $oElements, $xmlReader);
8✔
701
            $this->arraySlideLayouts[$baseFile] = &$oSlideLayout;
8✔
702

703
            return $oSlideLayout;
8✔
704
        }
705

706
        // @phpstan-ignore-next-line
707
        return null;
×
708
    }
709

710
    protected function loadTheme(string $sPart, SlideMaster $oSlideMaster): void
711
    {
712
        $xmlReader = new XMLReader();
8✔
713
        // @phpstan-ignore-next-line
714
        if ($xmlReader->getDomFromString($sPart)) {
8✔
715
            $oElements = $xmlReader->getElements('/a:theme/a:themeElements/a:clrScheme/*');
8✔
716
            foreach ($oElements as $oElement) {
8✔
717
                if ($oElement instanceof DOMElement) {
8✔
718
                    $oSchemeColor = new SchemeColor();
8✔
719
                    $oSchemeColor->setValue(str_replace('a:', '', $oElement->tagName));
8✔
720
                    $colorElement = $xmlReader->getElement('*', $oElement);
8✔
721
                    if ($colorElement instanceof DOMElement) {
8✔
722
                        if ($colorElement->hasAttribute('lastClr')) {
8✔
723
                            $oSchemeColor->setRGB($colorElement->getAttribute('lastClr'));
5✔
724
                        } elseif ($colorElement->hasAttribute('val')) {
8✔
725
                            $oSchemeColor->setRGB($colorElement->getAttribute('val'));
8✔
726
                        }
727
                    }
728
                    $oSlideMaster->addSchemeColor($oSchemeColor);
8✔
729
                }
730
            }
731
        }
732
    }
733

734
    protected function loadSlideBackground(XMLReader $xmlReader, DOMElement $oElement, AbstractSlide $oSlide): void
735
    {
736
        // Background color
737
        $oElementColor = $xmlReader->getElement('p:bgPr/a:solidFill/a:srgbClr', $oElement);
8✔
738
        if ($oElementColor instanceof DOMElement) {
8✔
739
            // Color
740
            $oColor = new Color();
1✔
741
            $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
1✔
742
            // Background
743
            $oBackground = new Slide\Background\Color();
1✔
744
            $oBackground->setColor($oColor);
1✔
745
            // Slide Background
746
            $oSlide->setBackground($oBackground);
1✔
747
        }
748

749
        // Background scheme color
750
        $oElementSchemeColor = $xmlReader->getElement('p:bgRef/a:schemeClr', $oElement);
8✔
751
        if ($oElementSchemeColor instanceof DOMElement) {
8✔
752
            // Color
753
            $oColor = new SchemeColor();
5✔
754
            $oColor->setValue($oElementSchemeColor->hasAttribute('val') ? $oElementSchemeColor->getAttribute('val') : null);
5✔
755
            // Background
756
            $oBackground = new Slide\Background\SchemeColor();
5✔
757
            $oBackground->setSchemeColor($oColor);
5✔
758
            // Slide Background
759
            $oSlide->setBackground($oBackground);
5✔
760
        }
761

762
        // Background image
763
        $oElementImage = $xmlReader->getElement('p:bgPr/a:blipFill/a:blip', $oElement);
8✔
764
        if ($oElementImage instanceof DOMElement) {
8✔
765
            $relImg = $this->arrayRels[$oSlide->getRelsIndex()][$oElementImage->getAttribute('r:embed')];
1✔
766
            if (is_array($relImg)) {
1✔
767
                // File
768
                $pathImage = 'ppt/slides/' . $relImg['Target'];
1✔
769
                $pathImage = explode('/', $pathImage);
1✔
770
                foreach ($pathImage as $key => $partPath) {
1✔
771
                    if ('..' == $partPath) {
1✔
772
                        unset($pathImage[$key - 1], $pathImage[$key]);
1✔
773
                    }
774
                }
775
                $pathImage = implode('/', $pathImage);
1✔
776
                $contentImg = $this->oZip->getFromName($pathImage);
1✔
777

778
                $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderPpt2007Bkg');
1✔
779
                file_put_contents($tmpBkgImg, $contentImg);
1✔
780
                // Background
781
                $oBackground = new Slide\Background\Image();
1✔
782
                $oBackground->setPath($tmpBkgImg);
1✔
783
                // Slide Background
784
                $oSlide->setBackground($oBackground);
1✔
785
            }
786
        }
787
    }
788

789
    protected function loadSlideNote(string $baseFile, Slide $oSlide): void
790
    {
791
        $sPart = $this->oZip->getFromName('ppt/notesSlides/' . $baseFile);
1✔
792
        $xmlReader = new XMLReader();
1✔
793
        // @phpstan-ignore-next-line
794
        if ($xmlReader->getDomFromString($sPart)) {
1✔
795
            $oNote = $oSlide->getNote();
1✔
796

797
            $arrayElements = $xmlReader->getElements('/p:notes/p:cSld/p:spTree/*');
1✔
798
            $this->loadSlideShapes($xmlReader, $oNote, $arrayElements, $xmlReader);
1✔
799
        }
800
    }
801

802
    protected function loadShapeDrawing(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
803
    {
804
        // Core
805
        $document->registerNamespace('asvg', 'http://schemas.microsoft.com/office/drawing/2016/SVG/main');
5✔
806
        if ($document->getElement('p:blipFill/a:blip/a:extLst/a:ext/asvg:svgBlip', $node)) {
5✔
807
            $oShape = new Base64();
×
808
        } else {
809
            $oShape = new Gd();
5✔
810
        }
811
        $oShape->getShadow()->setVisible(false);
5✔
812
        // Variables
813
        $fileRels = $oSlide->getRelsIndex();
5✔
814

815
        $oElement = $document->getElement('p:nvPicPr/p:cNvPr', $node);
5✔
816
        if ($oElement instanceof DOMElement) {
5✔
817
            $oShape->setName($oElement->hasAttribute('name') ? $oElement->getAttribute('name') : '');
5✔
818
            $oShape->setDescription($oElement->hasAttribute('descr') ? $oElement->getAttribute('descr') : '');
5✔
819

820
            // Hyperlink
821
            $oElementHlinkClick = $document->getElement('a:hlinkClick', $oElement);
5✔
822
            if (is_object($oElementHlinkClick)) {
5✔
823
                $oShape->setHyperlink(
1✔
824
                    $this->loadHyperlink($document, $oElementHlinkClick, $oShape->getHyperlink())
1✔
825
                );
1✔
826
            }
827
        }
828

829
        $oElement = $document->getElement('p:blipFill/a:blip', $node);
5✔
830
        if ($oElement instanceof DOMElement) {
5✔
831
            if ($oElement->hasAttribute('r:embed') && isset($this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'])) {
5✔
832
                $pathImage = 'ppt/slides/' . $this->arrayRels[$fileRels][$oElement->getAttribute('r:embed')]['Target'];
5✔
833
                $pathImage = explode('/', $pathImage);
5✔
834
                foreach ($pathImage as $key => $partPath) {
5✔
835
                    if ('..' == $partPath) {
5✔
836
                        unset($pathImage[$key - 1], $pathImage[$key]);
5✔
837
                    }
838
                }
839
                $pathImage = implode('/', $pathImage);
5✔
840
                $imageFile = $this->oZip->getFromName($pathImage);
5✔
841
                if (!empty($imageFile)) {
5✔
842
                    if ($oShape instanceof Gd) {
5✔
843
                        $info = getimagesizefromstring($imageFile);
5✔
844
                        if (!$info) {
5✔
845
                            return;
×
846
                        }
847
                        $oShape->setMimeType($info['mime']);
5✔
848
                        $oShape->setRenderingFunction(str_replace('/', '', $info['mime']));
5✔
849
                        $image = @imagecreatefromstring($imageFile);
5✔
850
                        if (!$image) {
5✔
851
                            return;
1✔
852
                        }
853
                        $oShape->setImageResource($image);
5✔
854
                    } elseif ($oShape instanceof Base64) {
×
855
                        $oShape->setData('data:image/svg+xml;base64,' . base64_encode($imageFile));
×
856
                    }
857
                }
858
            }
859
        }
860

861
        $oElement = $document->getElement('p:spPr', $node);
5✔
862
        if ($oElement instanceof DOMElement) {
5✔
863
            $oFill = $this->loadStyleFill($document, $oElement);
5✔
864
            $oShape->setFill($oFill);
5✔
865
        }
866

867
        $oElement = $document->getElement('p:spPr/a:xfrm', $node);
5✔
868
        if ($oElement instanceof DOMElement) {
5✔
869
            if ($oElement->hasAttribute('rot')) {
5✔
870
                $oShape->setRotation((int) CommonDrawing::angleToDegrees((int) $oElement->getAttribute('rot')));
4✔
871
            }
872
        }
873

874
        $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
5✔
875
        if ($oElement instanceof DOMElement) {
5✔
876
            if ($oElement->hasAttribute('x')) {
5✔
877
                $oShape->setOffsetX((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
5✔
878
            }
879
            if ($oElement->hasAttribute('y')) {
5✔
880
                $oShape->setOffsetY((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
5✔
881
            }
882
        }
883

884
        $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
5✔
885
        if ($oElement instanceof DOMElement) {
5✔
886
            if ($oElement->hasAttribute('cx')) {
5✔
887
                $oShape->setWidth((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
5✔
888
            }
889
            if ($oElement->hasAttribute('cy')) {
5✔
890
                $oShape->setHeight((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
5✔
891
            }
892
        }
893
        // Load shape effects
894
        $oElement = $document->getElement('p:spPr/a:effectLst', $node);
5✔
895
        if ($oElement instanceof DOMElement) {
5✔
896
            $oShape->setShadow(
3✔
897
                $this->loadShadow($document, $oElement)
3✔
898
            );
3✔
899
        }
900
        $oSlide->addShape($oShape);
5✔
901
    }
902

903
    /**
904
     * Load Shadow for shape or paragraph.
905
     */
906
    protected function loadShadow(XMLReader $document, DOMElement $node): ?Shadow
907
    {
908
        if ($node instanceof DOMElement) {
5✔
909
            $aNodes = $document->getElements('*', $node);
5✔
910
            foreach ($aNodes as $nodeShadow) {
5✔
911
                $type = explode(':', $nodeShadow->tagName);
4✔
912
                $type = array_pop($type);
4✔
913
                if ($type == Shadow::TYPE_SHADOW_INNER || $type == Shadow::TYPE_SHADOW_OUTER || $type == Shadow::TYPE_REFLECTION) {
4✔
914
                    $oShadow = new Shadow();
4✔
915
                    $oShadow->setVisible(true);
4✔
916
                    $oShadow->setType($type);
4✔
917
                    if ($nodeShadow->hasAttribute('blurRad')) {
4✔
918
                        $oShadow->setBlurRadius((int) CommonDrawing::emuToPixels((int) $nodeShadow->getAttribute('blurRad')));
4✔
919
                    }
920
                    if ($nodeShadow->hasAttribute('dist')) {
4✔
921
                        $oShadow->setDistance((int) CommonDrawing::emuToPixels((int) $nodeShadow->getAttribute('dist')));
4✔
922
                    }
923
                    if ($nodeShadow->hasAttribute('dir')) {
4✔
924
                        $oShadow->setDirection((int) CommonDrawing::angleToDegrees((int) $nodeShadow->getAttribute('dir')));
4✔
925
                    }
926
                    if ($nodeShadow->hasAttribute('algn')) {
4✔
927
                        $oShadow->setAlignment($node->getAttribute('algn'));
4✔
928
                    }
929

930
                    // Get color define by prstClr
931
                    $oSubElement = $document->getElement('a:prstClr', $nodeShadow);
4✔
932
                    if ($oSubElement instanceof DOMElement && $oSubElement->hasAttribute('val')) {
4✔
933
                        $oColor = new Color();
×
934
                        $oColor->setRGB($oSubElement->getAttribute('val'));
×
935

936
                        $oSubElt = $document->getElement('a:alpha', $oSubElement);
×
937
                        if ($oSubElt instanceof DOMElement && $oSubElt->hasAttribute('val')) {
×
938
                            $oColor->setAlpha((int) $oSubElt->getAttribute('val') / 1000);
×
939
                        }
940

941
                        $oShadow->setColor($oColor);
×
942
                    }
943

944
                    return $oShadow;
4✔
945
                }
946
            }
947
        }
948

949
        return null;
2✔
950
    }
951

952
    /**
953
     * @param AbstractSlide|Note $oSlide
954
     */
955
    protected function loadShapeRichText(XMLReader $document, DOMElement $node, $oSlide): void
956
    {
957
        // Core
958
        $oShape = $oSlide->createRichTextShape();
8✔
959
        $oShape->setParagraphs([]);
8✔
960
        // Variables
961
        if ($oSlide instanceof AbstractSlide) {
8✔
962
            $this->fileRels = $oSlide->getRelsIndex();
8✔
963
        }
964

965
        $oElement = $document->getElement('p:nvSpPr/p:cNvPr', $node);
8✔
966
        if ($oElement instanceof DOMElement) {
8✔
967
            $oShape->setName($oElement->hasAttribute('name') ? $oElement->getAttribute('name') : '');
8✔
968
        }
969

970
        $oElement = $document->getElement('p:spPr/a:xfrm', $node);
8✔
971
        if ($oElement instanceof DOMElement && $oElement->hasAttribute('rot')) {
8✔
972
            $oShape->setRotation((int) CommonDrawing::angleToDegrees((int) $oElement->getAttribute('rot')));
6✔
973
        }
974

975
        $oElement = $document->getElement('p:spPr/a:xfrm/a:off', $node);
8✔
976
        if ($oElement instanceof DOMElement) {
8✔
977
            if ($oElement->hasAttribute('x')) {
8✔
978
                $oShape->setOffsetX((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
8✔
979
            }
980
            if ($oElement->hasAttribute('y')) {
8✔
981
                $oShape->setOffsetY((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
8✔
982
            }
983
        }
984

985
        $oElement = $document->getElement('p:spPr/a:xfrm/a:ext', $node);
8✔
986
        if ($oElement instanceof DOMElement) {
8✔
987
            if ($oElement->hasAttribute('cx')) {
8✔
988
                $oShape->setWidth((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
8✔
989
            }
990
            if ($oElement->hasAttribute('cy')) {
8✔
991
                $oShape->setHeight((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
8✔
992
            }
993
        }
994

995
        $oElement = $document->getElement('p:nvSpPr/p:nvPr/p:ph', $node);
8✔
996
        if ($oElement instanceof DOMElement) {
8✔
997
            if ($oElement->hasAttribute('type')) {
8✔
998
                $placeholder = new Placeholder($oElement->getAttribute('type'));
8✔
999
                $oShape->setPlaceHolder($placeholder);
8✔
1000
            }
1001
        }
1002

1003
        // Load shape effects
1004
        $oElement = $document->getElement('p:spPr/a:effectLst', $node);
8✔
1005
        if ($oElement instanceof DOMElement) {
8✔
1006
            $oShape->setShadow(
2✔
1007
                $this->loadShadow($document, $oElement)
2✔
1008
            );
2✔
1009
        }
1010

1011
        // FBU-20210202+ Read body definitions
1012
        $bodyPr = $document->getElement('p:txBody/a:bodyPr', $node);
8✔
1013
        if ($bodyPr instanceof DOMElement) {
8✔
1014
            if ($bodyPr->hasAttribute('lIns')) {
8✔
1015
                $oShape->setInsetLeft((int) $bodyPr->getAttribute('lIns'));
8✔
1016
            }
1017
            if ($bodyPr->hasAttribute('tIns')) {
8✔
1018
                $oShape->setInsetTop((int) $bodyPr->getAttribute('tIns'));
8✔
1019
            }
1020
            if ($bodyPr->hasAttribute('rIns')) {
8✔
1021
                $oShape->setInsetRight((int) $bodyPr->getAttribute('rIns'));
8✔
1022
            }
1023
            if ($bodyPr->hasAttribute('bIns')) {
8✔
1024
                $oShape->setInsetBottom((int) $bodyPr->getAttribute('bIns'));
8✔
1025
            }
1026
            if ($bodyPr->hasAttribute('anchorCtr')) {
8✔
1027
                $oShape->setVerticalAlignCenter((int) $bodyPr->getAttribute('anchorCtr'));
3✔
1028
            }
1029
        }
1030

1031
        $arrayElements = $document->getElements('p:txBody/a:p', $node);
8✔
1032
        foreach ($arrayElements as $oElement) {
8✔
1033
            if ($oElement instanceof DOMElement) {
8✔
1034
                $this->loadParagraph($document, $oElement, $oShape);
8✔
1035
            }
1036
        }
1037

1038
        $oElement = $document->getElement('p:spPr', $node);
8✔
1039
        if ($oElement instanceof DOMElement) {
8✔
1040
            $oShape->setFill(
8✔
1041
                $this->loadStyleFill($document, $oElement)
8✔
1042
            );
8✔
1043
        }
1044

1045
        if (count($oShape->getParagraphs()) > 0) {
8✔
1046
            $oShape->setActiveParagraph(0);
8✔
1047
        }
1048
    }
1049

1050
    protected function loadShapeTable(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
1051
    {
1052
        $this->fileRels = $oSlide->getRelsIndex();
1✔
1053

1054
        $oShape = $oSlide->createTableShape();
1✔
1055

1056
        $oElement = $document->getElement('p:cNvPr', $node);
1✔
1057
        if ($oElement instanceof DOMElement) {
1✔
1058
            if ($oElement->hasAttribute('name')) {
×
1059
                $oShape->setName($oElement->getAttribute('name'));
×
1060
            }
1061
            if ($oElement->hasAttribute('descr')) {
×
1062
                $oShape->setDescription($oElement->getAttribute('descr'));
×
1063
            }
1064
        }
1065

1066
        $oElement = $document->getElement('p:xfrm/a:off', $node);
1✔
1067
        if ($oElement instanceof DOMElement) {
1✔
1068
            if ($oElement->hasAttribute('x')) {
1✔
1069
                $oShape->setOffsetX((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
1✔
1070
            }
1071
            if ($oElement->hasAttribute('y')) {
1✔
1072
                $oShape->setOffsetY((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
1✔
1073
            }
1074
        }
1075

1076
        $oElement = $document->getElement('p:xfrm/a:ext', $node);
1✔
1077
        if ($oElement instanceof DOMElement) {
1✔
1078
            if ($oElement->hasAttribute('cx')) {
1✔
1079
                $oShape->setWidth((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
1✔
1080
            }
1081
            if ($oElement->hasAttribute('cy')) {
1✔
1082
                $oShape->setHeight((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
1✔
1083
            }
1084
        }
1085

1086
        $arrayElements = $document->getElements('a:graphic/a:graphicData/a:tbl/a:tblGrid/a:gridCol', $node);
1✔
1087
        $oShape->setNumColumns($arrayElements->length);
1✔
1088
        $oShape->createRow();
1✔
1089
        foreach ($arrayElements as $key => $oElement) {
1✔
1090
            if ($oElement instanceof DOMElement && $oElement->getAttribute('w')) {
1✔
1091
                $oShape->getRow(0)->getCell($key)->setWidth((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('w')));
1✔
1092
            }
1093
        }
1094

1095
        $arrayElements = $document->getElements('a:graphic/a:graphicData/a:tbl/a:tr', $node);
1✔
1096
        foreach ($arrayElements as $keyRow => $oElementRow) {
1✔
1097
            if (!($oElementRow instanceof DOMElement)) {
1✔
1098
                continue;
×
1099
            }
1100
            if ($oShape->hasRow($keyRow)) {
1✔
1101
                $oRow = $oShape->getRow($keyRow);
1✔
1102
            } else {
1103
                $oRow = $oShape->createRow();
1✔
1104
            }
1105
            if ($oElementRow->hasAttribute('h')) {
1✔
1106
                $oRow->setHeight((int) CommonDrawing::emuToPixels((int) $oElementRow->getAttribute('h')));
1✔
1107
            }
1108
            $arrayElementsCell = $document->getElements('a:tc', $oElementRow);
1✔
1109
            foreach ($arrayElementsCell as $keyCell => $oElementCell) {
1✔
1110
                if (!($oElementCell instanceof DOMElement)) {
1✔
1111
                    continue;
×
1112
                }
1113
                $oCell = $oRow->getCell($keyCell);
1✔
1114
                $oCell->setParagraphs([]);
1✔
1115
                if ($oElementCell->hasAttribute('gridSpan')) {
1✔
1116
                    $oCell->setColSpan((int) $oElementCell->getAttribute('gridSpan'));
×
1117
                }
1118
                if ($oElementCell->hasAttribute('rowSpan')) {
1✔
1119
                    $oCell->setRowSpan((int) $oElementCell->getAttribute('rowSpan'));
×
1120
                }
1121

1122
                foreach ($document->getElements('a:txBody/a:p', $oElementCell) as $oElementPara) {
1✔
1123
                    if ($oElementPara instanceof DOMElement) {
1✔
1124
                        $this->loadParagraph($document, $oElementPara, $oCell);
1✔
1125
                    }
1126
                }
1127

1128
                $oElementTcPr = $document->getElement('a:tcPr', $oElementCell);
1✔
1129
                if ($oElementTcPr instanceof DOMElement) {
1✔
1130
                    $numParagraphs = count($oCell->getParagraphs());
1✔
1131
                    if ($numParagraphs > 0) {
1✔
1132
                        if ($oElementTcPr->hasAttribute('vert')) {
1✔
1133
                            $oCell->getParagraph(0)->getAlignment()->setTextDirection($oElementTcPr->getAttribute('vert'));
×
1134
                        }
1135
                        if ($oElementTcPr->hasAttribute('anchor')) {
1✔
1136
                            $oCell->getParagraph(0)->getAlignment()->setVertical($oElementTcPr->getAttribute('anchor'));
1✔
1137
                        }
1138
                        if ($oElementTcPr->hasAttribute('marB')) {
1✔
NEW
1139
                            $oCell->getParagraph(0)->getAlignment()->setMarginBottom(CommonDrawing::emuToPixels((int) $oElementTcPr->getAttribute('marB')));
×
1140
                        }
1141
                        if ($oElementTcPr->hasAttribute('marL')) {
1✔
1142
                            $oCell->getParagraph(0)->getAlignment()->setMarginLeft(CommonDrawing::emuToPixels((int) $oElementTcPr->getAttribute('marL')));
1✔
1143
                        }
1144
                        if ($oElementTcPr->hasAttribute('marR')) {
1✔
1145
                            $oCell->getParagraph(0)->getAlignment()->setMarginRight(CommonDrawing::emuToPixels((int) $oElementTcPr->getAttribute('marR')));
1✔
1146
                        }
1147
                        if ($oElementTcPr->hasAttribute('marT')) {
1✔
NEW
1148
                            $oCell->getParagraph(0)->getAlignment()->setMarginTop(CommonDrawing::emuToPixels((int) $oElementTcPr->getAttribute('marT')));
×
1149
                        }
1150
                    }
1151

1152
                    $oFill = $this->loadStyleFill($document, $oElementTcPr);
1✔
1153
                    if ($oFill instanceof Fill) {
1✔
1154
                        $oCell->setFill($oFill);
1✔
1155
                    }
1156

1157
                    $oBorders = new Borders();
1✔
1158
                    $oElementBorderL = $document->getElement('a:lnL', $oElementTcPr);
1✔
1159
                    if ($oElementBorderL instanceof DOMElement) {
1✔
1160
                        $this->loadStyleBorder($document, $oElementBorderL, $oBorders->getLeft());
1✔
1161
                    }
1162
                    $oElementBorderR = $document->getElement('a:lnR', $oElementTcPr);
1✔
1163
                    if ($oElementBorderR instanceof DOMElement) {
1✔
1164
                        $this->loadStyleBorder($document, $oElementBorderR, $oBorders->getRight());
1✔
1165
                    }
1166
                    $oElementBorderT = $document->getElement('a:lnT', $oElementTcPr);
1✔
1167
                    if ($oElementBorderT instanceof DOMElement) {
1✔
1168
                        $this->loadStyleBorder($document, $oElementBorderT, $oBorders->getTop());
1✔
1169
                    }
1170
                    $oElementBorderB = $document->getElement('a:lnB', $oElementTcPr);
1✔
1171
                    if ($oElementBorderB instanceof DOMElement) {
1✔
1172
                        $this->loadStyleBorder($document, $oElementBorderB, $oBorders->getBottom());
1✔
1173
                    }
1174
                    $oElementBorderDiagDown = $document->getElement('a:lnTlToBr', $oElementTcPr);
1✔
1175
                    if ($oElementBorderDiagDown instanceof DOMElement) {
1✔
1176
                        $this->loadStyleBorder($document, $oElementBorderDiagDown, $oBorders->getDiagonalDown());
×
1177
                    }
1178
                    $oElementBorderDiagUp = $document->getElement('a:lnBlToTr', $oElementTcPr);
1✔
1179
                    if ($oElementBorderDiagUp instanceof DOMElement) {
1✔
1180
                        $this->loadStyleBorder($document, $oElementBorderDiagUp, $oBorders->getDiagonalUp());
×
1181
                    }
1182
                    $oCell->setBorders($oBorders);
1✔
1183
                }
1184
            }
1185
        }
1186
    }
1187

1188
    protected function loadShapeChart(XMLReader $document, DOMElement $node, AbstractSlide $oSlide): void
1189
    {
1190
        $this->fileRels = $oSlide->getRelsIndex();
1✔
1191

1192
        $oShape = new Chart();
1✔
1193

1194
        $oElement = $document->getElement('p:cNvPr', $node);
1✔
1195
        if ($oElement instanceof DOMElement) {
1✔
NEW
1196
            if ($oElement->hasAttribute('name')) {
×
NEW
1197
                $oShape->setName($oElement->getAttribute('name'));
×
1198
            }
NEW
1199
            if ($oElement->hasAttribute('descr')) {
×
NEW
1200
                $oShape->setDescription($oElement->getAttribute('descr'));
×
1201
            }
1202
        }
1203

1204
        $oElement = $document->getElement('p:xfrm/a:off', $node);
1✔
1205
        if ($oElement instanceof DOMElement) {
1✔
1206
            if ($oElement->hasAttribute('x')) {
1✔
1207
                $oShape->setOffsetX((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('x')));
1✔
1208
            }
1209
            if ($oElement->hasAttribute('y')) {
1✔
1210
                $oShape->setOffsetY((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('y')));
1✔
1211
            }
1212
        }
1213

1214
        $oElement = $document->getElement('p:xfrm/a:ext', $node);
1✔
1215
        if ($oElement instanceof DOMElement) {
1✔
1216
            if ($oElement->hasAttribute('cx')) {
1✔
1217
                $oShape->setWidth((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cx')));
1✔
1218
            }
1219
            if ($oElement->hasAttribute('cy')) {
1✔
1220
                $oShape->setHeight((int) CommonDrawing::emuToPixels((int) $oElement->getAttribute('cy')));
1✔
1221
            }
1222
        }
1223

1224
        $chartElement = $document->getElement('a:graphic/a:graphicData/c:chart', $node);
1✔
1225
        if ($chartElement->hasAttribute('r:id') && isset($this->arrayRels[$this->fileRels][$chartElement->getAttribute('r:id')]['Target'])) {
1✔
1226
            $pathImage = 'ppt/slides/' . $this->arrayRels[$this->fileRels][$chartElement->getAttribute('r:id')]['Target'];
1✔
1227
            $pathImage = explode('/', $pathImage);
1✔
1228
            foreach ($pathImage as $key => $partPath) {
1✔
1229
                if ('..' == $partPath) {
1✔
1230
                    unset($pathImage[$key - 1], $pathImage[$key]);
1✔
1231
                }
1232
            }
1233
            $pathChart = implode('/', $pathImage);
1✔
1234
            $fileChart = $this->oZip->getFromName($pathChart);
1✔
1235
            if (false !== $fileChart) {
1✔
1236
                $xmlReader = new XMLReader();
1✔
1237
                // @phpstan-ignore-next-line
1238
                if ($xmlReader->getDomFromString($fileChart)) {
1✔
1239
                    if ($oElement = $xmlReader->getElement('/c:chartSpace/c:chart/c:autoTitleDeleted')) {
1✔
1240
                        $oShape->getTitle()->setVisible(false);
1✔
1241
                    }
1242

1243
                    if ($oElement = $xmlReader->getElement('/c:chartSpace/c:chart/c:plotArea/c:barChart')) {
1✔
1244
                        $shapeType = new Chart\Type\Bar();
1✔
1245

1246
                        $elementBarDir = $xmlReader->getElement('c:barDir', $oElement);
1✔
1247
                        if ($elementBarDir instanceof DOMElement) {
1✔
1248
                            $shapeType->setBarDirection($elementBarDir->getAttribute('val'));
1✔
1249
                        }
1250

1251
                        $elementGrouping = $xmlReader->getElement('c:grouping', $oElement);
1✔
1252
                        if ($elementGrouping instanceof DOMElement) {
1✔
1253
                            $shapeType->setBarGrouping($elementGrouping->getAttribute('val'));
1✔
1254
                        }
1255

1256
                        $elementSeries = $xmlReader->getElements('c:ser', $oElement);
1✔
1257
                        foreach ($elementSeries as $elementSerie) {
1✔
1258
                            $series = new Chart\Series();
1✔
1259
                            if ($elementTitle = $xmlReader->getElement('c:tx/c:strRef/c:strCache/c:pt/c:v', $elementSerie)) {
1✔
1260
                                $series->setTitle($elementTitle->nodeValue);
1✔
1261
                            }
1262

1263
                            $numPoints = 0;
1✔
1264
                            $elementCategory = $xmlReader->getElement('c:cat/c:strRef/c:strCache', $elementSerie);
1✔
1265
                            if ($elementCategoryNumPoints = $xmlReader->getElement('c:ptCount', $elementCategory)) {
1✔
1266
                                $numPoints = (int) $elementCategoryNumPoints->getAttribute('val');
1✔
1267
                            }
1268
                            $elementValue = $xmlReader->getElement('c:val/c:numRef/c:numCache', $elementSerie);
1✔
1269
                            for ($inc = 0; $inc < $numPoints; ++$inc) {
1✔
1270
                                $key = '';
1✔
1271
                                $val = '0';
1✔
1272
                                if ($subElementCategory = $xmlReader->getElement('c:pt[@idx="' . $inc . '"]/c:v', $elementCategory)) {
1✔
1273
                                    $key = $subElementCategory->nodeValue;
1✔
1274
                                }
1275
                                if ($subElementValue = $xmlReader->getElement('c:pt[@idx="' . $inc . '"]/c:v', $elementValue)) {
1✔
1276
                                    $val = $subElementValue->nodeValue;
1✔
1277
                                }
1278
                                $series->addValue($key, $val);
1✔
1279
                            }
1280

1281
                            if ($elementFill = $xmlReader->getElement('c:spPr', $elementSerie)) {
1✔
1282
                                $series->setFill(
1✔
1283
                                    $this->loadStyleFill($xmlReader, $elementFill)
1✔
1284
                                );
1✔
1285
                            }
1286

1287
                            if ($elementFill = $xmlReader->getElement('a:ln', $elementSerie)) {
1✔
NEW
1288
                                $series->setOutline(
×
NEW
1289
                                    $this->loadStyleOutline($xmlReader, $elementFill)
×
NEW
1290
                                );
×
1291
                            }
1292

1293
                            if ($elementShowLegendKey = $xmlReader->getElement('c:dLbls/c:showLegendKey', $elementSerie)) {
1✔
1294
                                $series->setShowLegendKey((bool) $elementShowLegendKey->getAttribute('val'));
1✔
1295
                            }
1296

1297
                            if ($elementShowVal = $xmlReader->getElement('c:dLbls/c:showVal', $elementSerie)) {
1✔
1298
                                $series->setShowValue((bool) $elementShowVal->getAttribute('val'));
1✔
1299
                            }
1300

1301
                            if ($elementShowCatName = $xmlReader->getElement('c:dLbls/c:showCatName', $elementSerie)) {
1✔
1302
                                $series->setShowCategoryName((bool) $elementShowCatName->getAttribute('val'));
1✔
1303
                            }
1304

1305
                            if ($elementShowSerName = $xmlReader->getElement('c:dLbls/c:showSerName', $elementSerie)) {
1✔
1306
                                $series->setShowSeriesName((bool) $elementShowSerName->getAttribute('val'));
1✔
1307
                            }
1308

1309
                            if ($elementShowPercent = $xmlReader->getElement('c:dLbls/c:showPercent', $elementSerie)) {
1✔
1310
                                $series->setShowPercentage((bool) $elementShowPercent->getAttribute('val'));
1✔
1311
                            }
1312

1313
                            if ($elementShowLeaderLines = $xmlReader->getElement('c:dLbls/c:showLeaderLines', $elementSerie)) {
1✔
1314
                                $series->setShowLeaderLines((bool) $elementShowLeaderLines->getAttribute('val'));
1✔
1315
                            }
1316

1317
                            $shapeType->addSeries($series);
1✔
1318
                        }
1319

1320
                        $elementGapWidth = $xmlReader->getElement('c:gapWidth', $oElement);
1✔
1321
                        if ($elementGapWidth instanceof DOMElement) {
1✔
1322
                            $shapeType->setGapWidthPercent((int) $elementGapWidth->getAttribute('val'));
1✔
1323
                        }
1324

1325
                        $elementOverlap = $xmlReader->getElement('c:overlap', $oElement);
1✔
1326
                        if ($elementOverlap instanceof DOMElement) {
1✔
1327
                            $shapeType->setOverlapWidthPercent((int) $elementOverlap->getAttribute('val'));
1✔
1328
                        }
1329

1330
                        $oShape->getPlotArea()->setType($shapeType);
1✔
1331
                    }
1332

1333
                    if ($oElement = $xmlReader->getElement('/c:chartSpace/c:chart/c:plotArea/c:catAx')) {
1✔
1334
                        if ($elementOrientation = $xmlReader->getElement('c:scaling/c:orientation', $oElement)) {
1✔
1335
                            $oShape->getPlotArea()->getAxisX()->setIsReversedOrder(
1✔
1336
                                (bool) ($elementOrientation->getAttribute('val') === 'maxMin')
1✔
1337
                            );
1✔
1338
                        }
1339
                        if ($elementDelete = $xmlReader->getElement('c:delete', $oElement)) {
1✔
1340
                            $oShape->getPlotArea()->getAxisX()->setIsVisible(
1✔
1341
                                (bool) ($elementDelete->getAttribute('val') === '0')
1✔
1342
                            );
1✔
1343
                        }
1344
                        if ($elementMajorTickMark = $xmlReader->getElement('c:majorTickMark', $oElement)) {
1✔
1345
                            $oShape->getPlotArea()->getAxisX()->setMajorTickMark($elementMajorTickMark->getAttribute('val'));
1✔
1346
                        }
1347
                        if ($elementMinorTickMark = $xmlReader->getElement('c:minorTickMark', $oElement)) {
1✔
1348
                            $oShape->getPlotArea()->getAxisX()->setMajorTickMark($elementMinorTickMark->getAttribute('val'));
1✔
1349
                        }
1350
                        if ($elementTickLabelPosition = $xmlReader->getElement('c:tickLblPos', $oElement)) {
1✔
1351
                            $oShape->getPlotArea()->getAxisX()->setTickLabelPosition($elementTickLabelPosition->getAttribute('val'));
1✔
1352
                        }
1353
                        if ($elementCrosses = $xmlReader->getElement('c:crosses', $oElement)) {
1✔
1354
                            $oShape->getPlotArea()->getAxisX()->setCrossesAt($elementCrosses->getAttribute('val'));
1✔
1355
                        }
1356

1357
                        if ($elementFill = $xmlReader->getElement('c:spPr', $oElement)) {
1✔
1358
                            $outline = $this->loadStyleOutline($xmlReader, $elementFill);
1✔
1359
                            if ($outline) {
1✔
1360
                                $oShape->getPlotArea()->getAxisX()->setOutline($outline);
1✔
1361
                            }
1362
                        }
1363
                    }
1364

1365
                    if ($oElement = $xmlReader->getElement('/c:chartSpace/c:chart/c:plotArea/c:valAx')) {
1✔
1366
                        if ($elementOrientation = $xmlReader->getElement('c:scaling/c:orientation', $oElement)) {
1✔
1367
                            $oShape->getPlotArea()->getAxisY()->setIsReversedOrder(
1✔
1368
                                (bool) ($elementOrientation->getAttribute('val') === 'maxMin')
1✔
1369
                            );
1✔
1370
                        }
1371
                        if ($elementDelete = $xmlReader->getElement('c:delete', $oElement)) {
1✔
1372
                            $oShape->getPlotArea()->getAxisY()->setIsVisible(
1✔
1373
                                (bool) ($elementDelete->getAttribute('val') === '0')
1✔
1374
                            );
1✔
1375
                        }
1376
                        if ($elementMajorTickMark = $xmlReader->getElement('c:majorTickMark', $oElement)) {
1✔
1377
                            $oShape->getPlotArea()->getAxisY()->setMajorTickMark($elementMajorTickMark->getAttribute('val'));
1✔
1378
                        }
1379
                        if ($elementMinorTickMark = $xmlReader->getElement('c:minorTickMark', $oElement)) {
1✔
1380
                            $oShape->getPlotArea()->getAxisY()->setMajorTickMark($elementMinorTickMark->getAttribute('val'));
1✔
1381
                        }
1382
                        if ($elementTickLabelPosition = $xmlReader->getElement('c:tickLblPos', $oElement)) {
1✔
1383
                            $oShape->getPlotArea()->getAxisY()->setTickLabelPosition($elementTickLabelPosition->getAttribute('val'));
1✔
1384
                        }
1385
                        if ($elementCrosses = $xmlReader->getElement('c:crosses', $oElement)) {
1✔
1386
                            $oShape->getPlotArea()->getAxisY()->setCrossesAt($elementCrosses->getAttribute('val'));
1✔
1387
                        }
1388
                        if ($elementFill = $xmlReader->getElement('c:spPr/a:ln', $oElement)) {
1✔
1389
                            if ($outline = $this->loadStyleOutline($xmlReader, $elementFill)) {
1✔
NEW
1390
                                $oShape->getPlotArea()->getAxisY()->setOutline($outline);
×
1391
                            }
1392
                        }
1393
                    }
1394

1395
                    if ($oElement = $xmlReader->getElement('/c:chartSpace/c:chart/c:legend')) {
1✔
1396
                        $oShape->getLegend()->setVisible(true);
1✔
1397

1398
                        if ($elementLegendPos = $xmlReader->getElement('c:legendPos', $oElement)) {
1✔
1399
                            $oShape->getLegend()->setPosition($elementLegendPos->getAttribute('val'));
1✔
1400
                        }
1401
                    } else {
NEW
1402
                        $oShape->getLegend()->setVisible(false);
×
1403
                    }
1404

1405
                    if ($oElement = $xmlReader->getElement('/c:chartSpace/c:chart/c:dispBlanksAs')) {
1✔
1406
                        $oShape->setDisplayBlankAs($oElement->getAttribute('val'));
1✔
1407
                    }
1408
                }
1409
            }
1410
            $oSlide->addShape($oShape);
1✔
1411
        }
1412
    }
1413

1414
    /**
1415
     * @param Cell|RichText $oShape
1416
     */
1417
    protected function loadParagraph(XMLReader $document, DOMElement $oElement, $oShape): void
1418
    {
1419
        // Core
1420
        $oParagraph = $oShape->createParagraph();
8✔
1421
        $oParagraph->setRichTextElements([]);
8✔
1422

1423
        $oSubElement = $document->getElement('a:pPr', $oElement);
8✔
1424
        if ($oSubElement instanceof DOMElement) {
8✔
1425
            if ($oSubElement->hasAttribute('algn')) {
8✔
1426
                $oParagraph->getAlignment()->setHorizontal($oSubElement->getAttribute('algn'));
8✔
1427
            }
1428
            if ($oSubElement->hasAttribute('fontAlgn')) {
8✔
1429
                $oParagraph->getAlignment()->setVertical($oSubElement->getAttribute('fontAlgn'));
4✔
1430
            }
1431
            if ($oSubElement->hasAttribute('marL')) {
8✔
1432
                $oParagraph->getAlignment()->setMarginLeft(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('marL')));
8✔
1433
            }
1434
            if ($oSubElement->hasAttribute('marR')) {
8✔
1435
                $oParagraph->getAlignment()->setMarginRight(CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('marR')));
4✔
1436
            }
1437
            if ($oSubElement->hasAttribute('indent')) {
8✔
1438
                $oParagraph->getAlignment()->setIndent((int) CommonDrawing::emuToPixels((int) $oSubElement->getAttribute('indent')));
7✔
1439
            }
1440
            if ($oSubElement->hasAttribute('lvl')) {
8✔
1441
                $oParagraph->getAlignment()->setLevel((int) $oSubElement->getAttribute('lvl'));
8✔
1442
            }
1443
            if ($oSubElement->hasAttribute('rtl')) {
8✔
1444
                $oParagraph->getAlignment()->setIsRTL((bool) $oSubElement->getAttribute('rtl'));
3✔
1445
            }
1446

1447
            $oElementLineSpacingPoints = $document->getElement('a:lnSpc/a:spcPts', $oSubElement);
8✔
1448
            if ($oElementLineSpacingPoints instanceof DOMElement) {
8✔
1449
                $oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_POINT);
×
1450
                $oParagraph->setLineSpacing((int) ((int) $oElementLineSpacingPoints->getAttribute('val') / 100));
×
1451
            }
1452
            $oElementLineSpacingPercent = $document->getElement('a:lnSpc/a:spcPct', $oSubElement);
8✔
1453
            if ($oElementLineSpacingPercent instanceof DOMElement) {
8✔
1454
                $oParagraph->setLineSpacingMode(Paragraph::LINE_SPACING_MODE_PERCENT);
1✔
1455
                $oParagraph->setLineSpacing((int) ((int) $oElementLineSpacingPercent->getAttribute('val') / 1000));
1✔
1456
            }
1457
            $oElementSpacingBefore = $document->getElement('a:spcBef/a:spcPts', $oSubElement);
8✔
1458
            if ($oElementSpacingBefore instanceof DOMElement) {
8✔
1459
                $oParagraph->setSpacingBefore((int) ((int) $oElementSpacingBefore->getAttribute('val') / 100));
2✔
1460
            }
1461
            $oElementSpacingAfter = $document->getElement('a:spcAft/a:spcPts', $oSubElement);
8✔
1462
            if ($oElementSpacingAfter instanceof DOMElement) {
8✔
1463
                $oParagraph->setSpacingAfter((int) ((int) $oElementSpacingAfter->getAttribute('val') / 100));
3✔
1464
            }
1465

1466
            $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NONE);
8✔
1467

1468
            $oElementBuFont = $document->getElement('a:buFont', $oSubElement);
8✔
1469
            if ($oElementBuFont instanceof DOMElement) {
8✔
1470
                if ($oElementBuFont->hasAttribute('typeface')) {
5✔
1471
                    $oParagraph->getBulletStyle()->setBulletFont($oElementBuFont->getAttribute('typeface'));
5✔
1472
                }
1473
            }
1474
            $oElementBuChar = $document->getElement('a:buChar', $oSubElement);
8✔
1475
            if ($oElementBuChar instanceof DOMElement) {
8✔
1476
                $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
5✔
1477
                if ($oElementBuChar->hasAttribute('char')) {
5✔
1478
                    $oParagraph->getBulletStyle()->setBulletChar($oElementBuChar->getAttribute('char'));
5✔
1479
                }
1480
            }
1481
            $oElementBuAutoNum = $document->getElement('a:buAutoNum', $oSubElement);
8✔
1482
            if ($oElementBuAutoNum instanceof DOMElement) {
8✔
1483
                $oParagraph->getBulletStyle()->setBulletType(Bullet::TYPE_NUMERIC);
×
1484
                if ($oElementBuAutoNum->hasAttribute('type')) {
×
1485
                    $oParagraph->getBulletStyle()->setBulletNumericStyle($oElementBuAutoNum->getAttribute('type'));
×
1486
                }
1487
                if ($oElementBuAutoNum->hasAttribute('startAt') && 1 != $oElementBuAutoNum->getAttribute('startAt')) {
×
1488
                    $oParagraph->getBulletStyle()->setBulletNumericStartAt($oElementBuAutoNum->getAttribute('startAt'));
×
1489
                }
1490
            }
1491
            $oElementBuClr = $document->getElement('a:buClr', $oSubElement);
8✔
1492
            if ($oElementBuClr instanceof DOMElement) {
8✔
1493
                $oColor = new Color();
1✔
1494
                /**
1495
                 * @todo Create protected for reading Color
1496
                 */
1497
                $oElementColor = $document->getElement('a:srgbClr', $oElementBuClr);
1✔
1498
                if ($oElementColor instanceof DOMElement) {
1✔
1499
                    $oColor->setRGB($oElementColor->hasAttribute('val') ? $oElementColor->getAttribute('val') : null);
1✔
1500
                }
1501
                $oParagraph->getBulletStyle()->setBulletColor($oColor);
1✔
1502
            }
1503
        }
1504
        $arraySubElements = $document->getElements('(a:r|a:br)', $oElement);
8✔
1505
        foreach ($arraySubElements as $oSubElement) {
8✔
1506
            if (!($oSubElement instanceof DOMElement)) {
8✔
1507
                continue;
×
1508
            }
1509
            if ('a:br' == $oSubElement->tagName) {
8✔
1510
                $oParagraph->createBreak();
4✔
1511
            }
1512
            if ('a:r' == $oSubElement->tagName) {
8✔
1513
                $oElementrPr = $document->getElement('a:rPr', $oSubElement);
8✔
1514
                if (is_object($oElementrPr)) {
8✔
1515
                    $oText = $oParagraph->createTextRun();
7✔
1516

1517
                    if ($oElementrPr->hasAttribute('b')) {
7✔
1518
                        $att = $oElementrPr->getAttribute('b');
6✔
1519
                        $oText->getFont()->setBold('true' == $att || '1' == $att ? true : false);
6✔
1520
                    }
1521
                    if ($oElementrPr->hasAttribute('i')) {
7✔
1522
                        $att = $oElementrPr->getAttribute('i');
4✔
1523
                        $oText->getFont()->setItalic('true' == $att || '1' == $att ? true : false);
4✔
1524
                    }
1525
                    if ($oElementrPr->hasAttribute('strike')) {
7✔
1526
                        $oText->getFont()->setStrikethrough($oElementrPr->getAttribute('strike'));
5✔
1527
                    }
1528
                    if ($oElementrPr->hasAttribute('sz')) {
7✔
1529
                        $oText->getFont()->setSize((int) ((int) $oElementrPr->getAttribute('sz') / 100));
6✔
1530
                    }
1531
                    if ($oElementrPr->hasAttribute('u')) {
7✔
1532
                        $oText->getFont()->setUnderline($oElementrPr->getAttribute('u'));
5✔
1533
                    }
1534
                    if ($oElementrPr->hasAttribute('cap')) {
7✔
1535
                        $oText->getFont()->setCapitalization($oElementrPr->getAttribute('cap'));
×
1536
                    }
1537
                    if ($oElementrPr->hasAttribute('lang')) {
7✔
1538
                        $oText->setLanguage($oElementrPr->getAttribute('lang'));
7✔
1539
                    }
1540
                    if ($oElementrPr->hasAttribute('baseline')) {
7✔
1541
                        $oText->getFont()->setBaseline((int) $oElementrPr->getAttribute('baseline'));
×
1542
                    }
1543
                    // Color
1544
                    $oElementSrgbClr = $document->getElement('a:solidFill/a:srgbClr', $oElementrPr);
7✔
1545
                    if (is_object($oElementSrgbClr) && $oElementSrgbClr->hasAttribute('val')) {
7✔
1546
                        $oColor = new Color();
5✔
1547
                        $oColor->setRGB($oElementSrgbClr->getAttribute('val'));
5✔
1548
                        $oText->getFont()->setColor($oColor);
5✔
1549
                    }
1550
                    // Hyperlink
1551
                    $oElementHlinkClick = $document->getElement('a:hlinkClick', $oElementrPr);
7✔
1552
                    if (is_object($oElementHlinkClick)) {
7✔
1553
                        $oText->setHyperlink(
4✔
1554
                            $this->loadHyperlink($document, $oElementHlinkClick, $oText->getHyperlink())
4✔
1555
                        );
4✔
1556
                    }
1557

1558
                    // Font
1559
                    $oElementFontFormat = null;
7✔
1560
                    $oElementFontFormatComplexScript = $document->getElement('a:cs', $oElementrPr);
7✔
1561
                    if (is_object($oElementFontFormatComplexScript)) {
7✔
1562
                        $oText->getFont()->setFormat(Font::FORMAT_COMPLEX_SCRIPT);
×
1563
                        $oElementFontFormat = $oElementFontFormatComplexScript;
×
1564
                    }
1565
                    $oElementFontFormatEastAsian = $document->getElement('a:ea', $oElementrPr);
7✔
1566
                    if (is_object($oElementFontFormatEastAsian)) {
7✔
1567
                        $oText->getFont()->setFormat(Font::FORMAT_EAST_ASIAN);
1✔
1568
                        $oElementFontFormat = $oElementFontFormatEastAsian;
1✔
1569
                    }
1570
                    $oElementFontFormatLatin = $document->getElement('a:latin', $oElementrPr);
7✔
1571
                    if (is_object($oElementFontFormatLatin)) {
7✔
1572
                        $oText->getFont()->setFormat(Font::FORMAT_LATIN);
5✔
1573
                        $oElementFontFormat = $oElementFontFormatLatin;
5✔
1574
                    }
1575
                    if (is_object($oElementFontFormat) && $oElementFontFormat->hasAttribute('typeface')) {
7✔
1576
                        $oText->getFont()->setName($oElementFontFormat->getAttribute('typeface'));
5✔
1577
                    }
1578
                    // Font definition
1579
                    $oElementFont = $document->getElement('a:latin', $oElementrPr);
7✔
1580
                    if ($oElementFont instanceof DOMElement) {
7✔
1581
                        if ($oElementFont->hasAttribute('typeface')) {
5✔
1582
                            $oText->getFont()->setName($oElementFont->getAttribute('typeface'));
5✔
1583
                        }
1584
                        if ($oElementFont->hasAttribute('panose')) {
5✔
1585
                            $oText->getFont()->setPanose($oElementFont->getAttribute('panose'));
×
1586
                        }
1587
                        if ($oElementFont->hasAttribute('pitchFamily')) {
5✔
1588
                            $oText->getFont()->setPitchFamily((int) $oElementFont->getAttribute('pitchFamily'));
×
1589
                        }
1590
                        if ($oElementFont->hasAttribute('charset')) {
5✔
1591
                            $oText->getFont()->setCharset((int) $oElementFont->getAttribute('charset'));
×
1592
                        }
1593
                    }
1594

1595
                    $oSubSubElement = $document->getElement('a:t', $oSubElement);
7✔
1596
                    $oText->setText($oSubSubElement->nodeValue);
7✔
1597
                }
1598
            }
1599
        }
1600
    }
1601

1602
    protected function loadHyperlink(XMLReader $xmlReader, DOMElement $element, Hyperlink $hyperlink): Hyperlink
1603
    {
1604
        if ($element->hasAttribute('tooltip')) {
4✔
1605
            $hyperlink->setTooltip($element->getAttribute('tooltip'));
4✔
1606
        }
1607
        if ($element->hasAttribute('r:id') && isset($this->arrayRels[$this->fileRels][$element->getAttribute('r:id')]['Target'])) {
4✔
1608
            $hyperlink->setUrl($this->arrayRels[$this->fileRels][$element->getAttribute('r:id')]['Target']);
4✔
1609
        }
1610
        if ($subElementExt = $xmlReader->getElement('a:extLst/a:ext', $element)) {
4✔
1611
            if ($subElementExt->hasAttribute('uri') && $subElementExt->getAttribute('uri') == '{A12FA001-AC4F-418D-AE19-62706E023703}') {
×
1612
                $hyperlink->setIsTextColorUsed(true);
×
1613
            }
1614
        }
1615

1616
        return $hyperlink;
4✔
1617
    }
1618

1619
    protected function loadStyleBorder(XMLReader $xmlReader, DOMElement $oElement, Border $oBorder): void
1620
    {
1621
        if ($oElement->hasAttribute('w')) {
1✔
1622
            $oBorder->setLineWidth(CommonDrawing::emuToPixels((int) $oElement->getAttribute('w')));
1✔
1623
        }
1624
        if ($oElement->hasAttribute('cmpd')) {
1✔
1625
            $oBorder->setLineStyle($oElement->getAttribute('cmpd'));
×
1626
        }
1627

1628
        $oElementNoFill = $xmlReader->getElement('a:noFill', $oElement);
1✔
1629
        if ($oElementNoFill instanceof DOMElement && Border::LINE_SINGLE == $oBorder->getLineStyle()) {
1✔
1630
            $oBorder->setLineStyle(Border::LINE_NONE);
×
1631
        }
1632

1633
        $oElementColor = $xmlReader->getElement('a:solidFill/a:srgbClr', $oElement);
1✔
1634
        if ($oElementColor instanceof DOMElement) {
1✔
1635
            $oBorder->setColor($this->loadStyleColor($xmlReader, $oElementColor));
1✔
1636
        }
1637

1638
        $oElementDashStyle = $xmlReader->getElement('a:prstDash', $oElement);
1✔
1639
        if ($oElementDashStyle instanceof DOMElement && $oElementDashStyle->hasAttribute('val')) {
1✔
1640
            $oBorder->setDashStyle($oElementDashStyle->getAttribute('val'));
1✔
1641
        }
1642
    }
1643

1644
    protected function loadStyleColor(XMLReader $xmlReader, DOMElement $oElement): Color
1645
    {
1646
        $oColor = new Color();
3✔
1647
        $oColor->setRGB($oElement->getAttribute('val'));
3✔
1648
        $oElementAlpha = $xmlReader->getElement('a:alpha', $oElement);
3✔
1649
        if ($oElementAlpha instanceof DOMElement && $oElementAlpha->hasAttribute('val')) {
3✔
1650
            $alpha = strtoupper(dechex((int) (((int) $oElementAlpha->getAttribute('val') / 1000) / 100) * 255));
×
1651
            $oColor->setRGB($oElement->getAttribute('val'), $alpha);
×
1652
        }
1653

1654
        return $oColor;
3✔
1655
    }
1656

1657
    protected function loadStyleFill(XMLReader $xmlReader, DOMElement $oElement): ?Fill
1658
    {
1659
        // Gradient fill
1660
        $oElementFill = $xmlReader->getElement('a:gradFill', $oElement);
8✔
1661
        if ($oElementFill instanceof DOMElement) {
8✔
1662
            $oFill = new Fill();
1✔
1663
            $oFill->setFillType(Fill::FILL_GRADIENT_LINEAR);
1✔
1664

1665
            $oElementColor = $xmlReader->getElement('a:gsLst/a:gs[@pos="0"]/a:srgbClr', $oElementFill);
1✔
1666
            if ($oElementColor instanceof DOMElement && $oElementColor->hasAttribute('val')) {
1✔
1667
                $oFill->setStartColor($this->loadStyleColor($xmlReader, $oElementColor));
1✔
1668
            }
1669

1670
            $oElementColor = $xmlReader->getElement('a:gsLst/a:gs[@pos="100000"]/a:srgbClr', $oElementFill);
1✔
1671
            if ($oElementColor instanceof DOMElement && $oElementColor->hasAttribute('val')) {
1✔
1672
                $oFill->setEndColor($this->loadStyleColor($xmlReader, $oElementColor));
1✔
1673
            }
1674

1675
            $oRotation = $xmlReader->getElement('a:lin', $oElementFill);
1✔
1676
            if ($oRotation instanceof DOMElement && $oRotation->hasAttribute('ang')) {
1✔
1677
                $oFill->setRotation(CommonDrawing::angleToDegrees((int) $oRotation->getAttribute('ang')));
1✔
1678
            }
1679

1680
            return $oFill;
1✔
1681
        }
1682

1683
        // Solid fill
1684
        $oElementFill = $xmlReader->getElement('a:solidFill', $oElement);
8✔
1685
        if ($oElementFill instanceof DOMElement) {
8✔
1686
            $oFill = new Fill();
3✔
1687
            $oFill->setFillType(Fill::FILL_SOLID);
3✔
1688

1689
            $oElementColor = $xmlReader->getElement('a:srgbClr', $oElementFill);
3✔
1690
            if ($oElementColor instanceof DOMElement) {
3✔
1691
                $oFill->setStartColor($this->loadStyleColor($xmlReader, $oElementColor));
3✔
1692
            }
1693

1694
            return $oFill;
3✔
1695
        }
1696

1697
        return null;
8✔
1698
    }
1699

1700
    protected function loadStyleOutline(XMLReader $xmlReader, DOMElement $oElement): ?Outline
1701
    {
1702
        if ($element = $xmlReader->getElement('a:ln', $oElement)) {
1✔
1703
            $outline = new Outline();
1✔
1704

1705
            $outline->setWidth((int) CommonDrawing::emuToPixels((int) $element->getAttribute('w')));
1✔
1706

1707
            $fill = $this->loadStyleFill($xmlReader, $element);
1✔
1708
            if ($fill) {
1✔
1709
                $outline->setFill($fill);
1✔
1710
            }
1711

1712
            return $outline;
1✔
1713
        }
1714

1715
        return null;
1✔
1716
    }
1717

1718
    protected function loadRels(string $fileRels): void
1719
    {
1720
        $sPart = $this->oZip->getFromName($fileRels);
8✔
1721
        if (false !== $sPart) {
8✔
1722
            $xmlReader = new XMLReader();
8✔
1723
            // @phpstan-ignore-next-line
1724
            if ($xmlReader->getDomFromString($sPart)) {
8✔
1725
                foreach ($xmlReader->getElements('*') as $oNode) {
8✔
1726
                    if (!($oNode instanceof DOMElement)) {
8✔
1727
                        continue;
×
1728
                    }
1729
                    $this->arrayRels[$fileRels][$oNode->getAttribute('Id')] = [
8✔
1730
                        'Target' => $oNode->getAttribute('Target'),
8✔
1731
                        'Type' => $oNode->getAttribute('Type'),
8✔
1732
                    ];
8✔
1733
                }
1734
            }
1735
        }
1736
    }
1737

1738
    /**
1739
     * @param AbstractSlide|Note $oSlide
1740
     * @param DOMNodeList<DOMNode> $oElements
1741
     *
1742
     * @internal param $baseFile
1743
     */
1744
    protected function loadSlideShapes(XMLReader $document, $oSlide, DOMNodeList $oElements, XMLReader $xmlReader): void
1745
    {
1746
        foreach ($oElements as $oNode) {
8✔
1747
            if (!($oNode instanceof DOMElement)) {
8✔
1748
                continue;
×
1749
            }
1750
            switch ($oNode->tagName) {
8✔
1751
                case 'p:graphicFrame':
8✔
1752
                    if ($oSlide instanceof AbstractSlide) {
1✔
1753
                        if ($document->elementExists('a:graphic/a:graphicData/a:tbl', $oNode)) {
1✔
1754
                            $this->loadShapeTable($xmlReader, $oNode, $oSlide);
1✔
1755
                        }
1756
                        if ($document->elementExists('a:graphic/a:graphicData/c:chart', $oNode)) {
1✔
1757
                            $this->loadShapeChart($xmlReader, $oNode, $oSlide);
1✔
1758
                        }
1759
                    }
1760

1761
                    break;
1✔
1762
                case 'p:pic':
8✔
1763
                    if ($this->loadImages && $oSlide instanceof AbstractSlide) {
6✔
1764
                        $this->loadShapeDrawing($xmlReader, $oNode, $oSlide);
5✔
1765
                    }
1766

1767
                    break;
6✔
1768
                case 'p:sp':
8✔
1769
                    $this->loadShapeRichText($xmlReader, $oNode, $oSlide);
8✔
1770

1771
                    break;
8✔
1772
                default:
1773
                    //throw new FeatureNotImplementedException();
1774
            }
1775
        }
1776
    }
1777
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc