Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.95% covered (success)
93.95%
978 / 1041
50.00% covered (danger)
50.00%
9 / 18
CRAP
0.00% covered (danger)
0.00%
0 / 1
AbstractSlide
93.95% covered (success)
93.95%
978 / 1041
50.00% covered (danger)
50.00%
9 / 18
180.71
0.00% covered (danger)
0.00%
0 / 1
 writeDrawingRelations
28.57% covered (danger)
28.57%
12 / 42
0.00% covered (danger)
0.00%
0 / 1
55.10
 writeShapeCollection
95.00% covered (success)
95.00%
19 / 20
0.00% covered (danger)
0.00%
0 / 1
11
 writeShapeText
97.09% covered (success)
97.09%
100 / 103
0.00% covered (danger)
0.00%
0 / 1
26
 writeShapeTable
97.58% covered (success)
97.58%
121 / 124
0.00% covered (danger)
0.00%
0 / 1
19
 writeParagraphs
100.00% covered (success)
100.00%
16 / 16
100.00% covered (success)
100.00%
1 / 1
7
 writeParagraphStyles
100.00% covered (success)
100.00%
47 / 47
100.00% covered (success)
100.00%
1 / 1
9
 writeRunStyles
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
3
 writeShapeLine
95.16% covered (success)
95.16%
59 / 62
0.00% covered (danger)
0.00%
0 / 1
8
 writeShadow
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
2
 writeHyperlink
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
4
 writeNote
100.00% covered (success)
100.00%
129 / 129
100.00% covered (success)
100.00%
1 / 1
3
 writeShapeAutoShape
100.00% covered (success)
100.00%
48 / 48
100.00% covered (success)
100.00%
1 / 1
1
 writeShapeChart
91.89% covered (success)
91.89%
34 / 37
0.00% covered (danger)
0.00%
0 / 1
2.00
 writeShapePic
96.67% covered (success)
96.67%
87 / 90
0.00% covered (danger)
0.00%
0 / 1
8
 writeShapeGroup
100.00% covered (success)
100.00%
31 / 31
100.00% covered (success)
100.00%
1 / 1
1
 writeSlideBackground
50.00% covered (danger)
50.00%
16 / 32
0.00% covered (danger)
0.00%
0 / 1
8.12
 writeSlideTransition
100.00% covered (success)
100.00%
184 / 184
100.00% covered (success)
100.00%
1 / 1
52
 getGUID
92.31% covered (success)
92.31%
12 / 13
0.00% covered (danger)
0.00%
0 / 1
2.00
1<?php
2/**
3 * This file is part of PHPPresentation - A pure PHP library for reading and writing
4 * presentations documents.
5 *
6 * PHPPresentation is free software distributed under the terms of the GNU Lesser
7 * General Public License version 3 as published by the Free Software Foundation.
8 *
9 * For the full copyright and license information, please read the LICENSE
10 * file that was distributed with this source code. For the full list of
11 * contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
12 *
13 * @see        https://github.com/PHPOffice/PHPPresentation
14 *
15 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16 */
17
18declare(strict_types=1);
19
20namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;
21
22use ArrayObject;
23use PhpOffice\Common\Drawing as CommonDrawing;
24use PhpOffice\Common\Text;
25use PhpOffice\Common\XMLWriter;
26use PhpOffice\PhpPresentation\AbstractShape;
27use PhpOffice\PhpPresentation\Exception\UndefinedChartTypeException;
28use PhpOffice\PhpPresentation\Shape\AbstractGraphic;
29use PhpOffice\PhpPresentation\Shape\AutoShape;
30use PhpOffice\PhpPresentation\Shape\Chart as ShapeChart;
31use PhpOffice\PhpPresentation\Shape\Comment;
32use PhpOffice\PhpPresentation\Shape\Drawing\AbstractDrawingAdapter;
33use PhpOffice\PhpPresentation\Shape\Drawing\File as ShapeDrawingFile;
34use PhpOffice\PhpPresentation\Shape\Drawing\Gd as ShapeDrawingGd;
35use PhpOffice\PhpPresentation\Shape\Group;
36use PhpOffice\PhpPresentation\Shape\Line;
37use PhpOffice\PhpPresentation\Shape\Media;
38use PhpOffice\PhpPresentation\Shape\Placeholder;
39use PhpOffice\PhpPresentation\Shape\RichText;
40use PhpOffice\PhpPresentation\Shape\RichText\BreakElement;
41use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
42use PhpOffice\PhpPresentation\Shape\RichText\Run;
43use PhpOffice\PhpPresentation\Shape\RichText\TextElement;
44use PhpOffice\PhpPresentation\Shape\Table as ShapeTable;
45use PhpOffice\PhpPresentation\Slide;
46use PhpOffice\PhpPresentation\Slide\AbstractSlide as AbstractSlideAlias;
47use PhpOffice\PhpPresentation\Slide\Note;
48use PhpOffice\PhpPresentation\Style\Alignment;
49use PhpOffice\PhpPresentation\Style\Border;
50use PhpOffice\PhpPresentation\Style\Bullet;
51use PhpOffice\PhpPresentation\Style\Color;
52use PhpOffice\PhpPresentation\Style\Font;
53use PhpOffice\PhpPresentation\Style\Shadow;
54
55abstract class AbstractSlide extends AbstractDecoratorWriter
56{
57    /**
58     * @return mixed
59     */
60    protected function writeDrawingRelations(AbstractSlideAlias $pSlideMaster, XMLWriter $objWriter, int $relId)
61    {
62        if (count($pSlideMaster->getShapeCollection()) > 0) {
63            // Loop trough images and write relationships
64            foreach ($pSlideMaster->getShapeCollection() as $shape) {
65                if ($shape instanceof ShapeDrawingFile || $shape instanceof ShapeDrawingGd) {
66                    // Write relationship for image drawing
67                    $this->writeRelationship(
68                        $objWriter,
69                        $relId,
70                        'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
71                        '../media/' . str_replace(' ', '_', $shape->getIndexedFilename())
72                    );
73                    $shape->relationId = 'rId' . $relId;
74                    ++$relId;
75                } elseif ($shape instanceof ShapeChart) {
76                    // Write relationship for chart drawing
77                    $this->writeRelationship(
78                        $objWriter,
79                        $relId,
80                        'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
81                        '../charts/' . $shape->getIndexedFilename()
82                    );
83                    $shape->relationId = 'rId' . $relId;
84                    ++$relId;
85                } elseif ($shape instanceof Group) {
86                    foreach ($shape->getShapeCollection() as $subShape) {
87                        if ($subShape instanceof ShapeDrawingFile ||
88                            $subShape instanceof ShapeDrawingGd
89                        ) {
90                            // Write relationship for image drawing
91                            $this->writeRelationship(
92                                $objWriter,
93                                $relId,
94                                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image',
95                                '../media/' . str_replace(' ', '_', $subShape->getIndexedFilename())
96                            );
97                            $subShape->relationId = 'rId' . $relId;
98                            ++$relId;
99                        } elseif ($subShape instanceof ShapeChart) {
100                            // Write relationship for chart drawing
101                            $this->writeRelationship(
102                                $objWriter,
103                                $relId,
104                                'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart',
105                                '../charts/' . $subShape->getIndexedFilename()
106                            );
107                            $subShape->relationId = 'rId' . $relId;
108                            ++$relId;
109                        }
110                    }
111                }
112            }
113        }
114
115        return $relId;
116    }
117
118    /**
119     * Note : $shapeId needs to start to 1
120     *  The animation is applied to the shape which is next to the target shape.
121     *
122     * @param array<int, AbstractShape>|ArrayObject<int, AbstractShape> $shapes
123     * @param int $shapeId
124     */
125    protected function writeShapeCollection(XMLWriter $objWriter, $shapes = [], &$shapeId = 1): void
126    {
127        if (0 == count($shapes)) {
128            return;
129        }
130        foreach ($shapes as $shape) {
131            // Increment $shapeId
132            ++$shapeId;
133            // Check type
134            if ($shape instanceof RichText) {
135                $this->writeShapeText($objWriter, $shape, $shapeId);
136            } elseif ($shape instanceof ShapeTable) {
137                $this->writeShapeTable($objWriter, $shape, $shapeId);
138            } elseif ($shape instanceof Line) {
139                $this->writeShapeLine($objWriter, $shape, $shapeId);
140            } elseif ($shape instanceof ShapeChart) {
141                $this->writeShapeChart($objWriter, $shape, $shapeId);
142            } elseif ($shape instanceof AbstractGraphic) {
143                $this->writeShapePic($objWriter, $shape, $shapeId);
144            } elseif ($shape instanceof AutoShape) {
145                $this->writeShapeAutoShape($objWriter, $shape, $shapeId);
146            } elseif ($shape instanceof Group) {
147                $this->writeShapeGroup($objWriter, $shape, $shapeId);
148            } elseif ($shape instanceof Comment) {
149            } else {
150                throw new UndefinedChartTypeException();
151            }
152        }
153    }
154
155    /**
156     * Write txt.
157     *
158     * @param XMLWriter $objWriter XML Writer
159     */
160    protected function writeShapeText(XMLWriter $objWriter, RichText $shape, int $shapeId): void
161    {
162        // p:sp
163        $objWriter->startElement('p:sp');
164        // p:sp\p:nvSpPr
165        $objWriter->startElement('p:nvSpPr');
166        // p:sp\p:nvSpPr\p:cNvPr
167        $objWriter->startElement('p:cNvPr');
168        $objWriter->writeAttribute('id', $shapeId);
169        if ($shape->isPlaceholder()) {
170            $objWriter->writeAttribute('name', 'Placeholder for ' . $shape->getPlaceholder()->getType());
171        } else {
172            $objWriter->writeAttribute('name', $shape->getName());
173        }
174        // Hyperlink
175        if ($shape->hasHyperlink()) {
176            $this->writeHyperlink($objWriter, $shape);
177        }
178        // > p:sp\p:nvSpPr
179        $objWriter->endElement();
180        // p:sp\p:nvSpPr\p:cNvSpPr
181        $objWriter->startElement('p:cNvSpPr');
182        $objWriter->writeAttribute('txBox', '1');
183        $objWriter->endElement();
184        // p:sp\p:nvSpPr\p:nvPr
185        if ($shape->isPlaceholder()) {
186            $objWriter->startElement('p:nvPr');
187            $objWriter->startElement('p:ph');
188            $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
189            if (null !== $shape->getPlaceholder()->getIdx()) {
190                $objWriter->writeAttribute('idx', $shape->getPlaceholder()->getIdx());
191            }
192            $objWriter->endElement();
193            $objWriter->endElement();
194        } else {
195            $objWriter->writeElement('p:nvPr', null);
196        }
197        // > p:sp\p:nvSpPr
198        $objWriter->endElement();
199        // p:sp\p:spPr
200        $objWriter->startElement('p:spPr');
201
202        // p:sp\p:spPr\a:xfrm
203        $objWriter->startElement('a:xfrm');
204        $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle($shape->getRotation()));
205        // p:sp\p:spPr\a:xfrm\a:off
206        $objWriter->startElement('a:off');
207        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
208        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
209        $objWriter->endElement();
210        // p:sp\p:spPr\a:xfrm\a:ext
211        $objWriter->startElement('a:ext');
212        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
213        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
214        $objWriter->endElement();
215        // > p:sp\p:spPr\a:xfrm
216        $objWriter->endElement();
217        // p:sp\p:spPr\a:prstGeom
218        $objWriter->startElement('a:prstGeom');
219        $objWriter->writeAttribute('prst', 'rect');
220
221        // p:sp\p:spPr\a:prstGeom\a:avLst
222        $objWriter->writeElement('a:avLst');
223
224        $objWriter->endElement();
225
226        $this->writeFill($objWriter, $shape->getFill());
227        $this->writeBorder($objWriter, $shape->getBorder(), '');
228        $this->writeShadow($objWriter, $shape->getShadow());
229
230        // > p:sp\p:spPr
231        $objWriter->endElement();
232        // p:txBody
233        $objWriter->startElement('p:txBody');
234        // a:bodyPr
235        //@link :http://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.bodyproperties%28v=office.14%29.aspx
236        $objWriter->startElement('a:bodyPr');
237        if (!$shape->isPlaceholder()) {
238            // Vertical alignment
239            $verticalAlign = $shape->getActiveParagraph()->getAlignment()->getVertical();
240            if (Alignment::VERTICAL_BASE != $verticalAlign && Alignment::VERTICAL_AUTO != $verticalAlign) {
241                $objWriter->writeAttribute('anchor', $verticalAlign);
242            }
243            $objWriter->writeAttribute('anchorCtr', $shape->getVerticalAlignCenter());
244            if (RichText::WRAP_SQUARE != $shape->getWrap()) {
245                $objWriter->writeAttribute('wrap', $shape->getWrap());
246            }
247            $objWriter->writeAttribute('rtlCol', '0');
248            if (RichText::OVERFLOW_OVERFLOW != $shape->getHorizontalOverflow()) {
249                $objWriter->writeAttribute('horzOverflow', $shape->getHorizontalOverflow());
250            }
251            if (RichText::OVERFLOW_OVERFLOW != $shape->getVerticalOverflow()) {
252                $objWriter->writeAttribute('vertOverflow', $shape->getVerticalOverflow());
253            }
254            if ($shape->isUpright()) {
255                $objWriter->writeAttribute('upright', '1');
256            }
257            $objWriter->writeAttribute('vert', $shape->isVertical() ? 'vert' : 'horz');
258            $objWriter->writeAttribute('bIns', CommonDrawing::pixelsToEmu($shape->getInsetBottom()));
259            $objWriter->writeAttribute('lIns', CommonDrawing::pixelsToEmu($shape->getInsetLeft()));
260            $objWriter->writeAttribute('rIns', CommonDrawing::pixelsToEmu($shape->getInsetRight()));
261            $objWriter->writeAttribute('tIns', CommonDrawing::pixelsToEmu($shape->getInsetTop()));
262            if ($shape->getColumns() != 1) {
263                $objWriter->writeAttribute('numCol', $shape->getColumns());
264                $objWriter->writeAttribute('spcCol', CommonDrawing::pixelsToEmu($shape->getColumnSpacing()));
265            }
266            // a:spAutoFit
267            $objWriter->startElement('a:' . $shape->getAutoFit());
268            if (RichText::AUTOFIT_NORMAL == $shape->getAutoFit()) {
269                if (null !== $shape->getFontScale()) {
270                    $objWriter->writeAttribute('fontScale', $shape->getFontScale() * 1000);
271                }
272                if (null !== $shape->getLineSpaceReduction()) {
273                    $objWriter->writeAttribute('lnSpcReduction', $shape->getLineSpaceReduction() * 1000);
274                }
275            }
276            $objWriter->endElement();
277        }
278        $objWriter->endElement();
279        // a:lstStyle
280        $objWriter->writeElement('a:lstStyle', null);
281        if ($shape->isPlaceholder() &&
282            (Placeholder::PH_TYPE_SLIDENUM == $shape->getPlaceholder()->getType() ||
283                Placeholder::PH_TYPE_DATETIME == $shape->getPlaceholder()->getType())
284        ) {
285            $objWriter->startElement('a:p');
286
287            // Paragraph Style
288            $paragraphs = $shape->getParagraphs();
289            if (!empty($paragraphs)) {
290                $paragraph = &$paragraphs[0];
291                $this->writeParagraphStyles($objWriter, $paragraph, true);
292            }
293
294            $objWriter->startElement('a:fld');
295            $objWriter->writeAttribute('id', $this->getGUID());
296            $objWriter->writeAttribute('type', (
297                Placeholder::PH_TYPE_SLIDENUM == $shape->getPlaceholder()->getType() ? 'slidenum' : 'datetime'
298            ));
299
300            if (isset($paragraph)) {
301                $elements = $paragraph->getRichTextElements();
302                if (!empty($elements)) {
303                    $element = &$elements[0];
304                    if ($element instanceof Run) {
305                        $this->writeRunStyles($objWriter, $element);
306                    }
307                }
308            }
309
310            $objWriter->writeElement('a:t', (
311                Placeholder::PH_TYPE_SLIDENUM == $shape->getPlaceholder()->getType() ? '<nr.>' : '03-04-05'
312            ));
313            $objWriter->endElement();
314            $objWriter->endElement();
315        } else {
316            // Write paragraphs
317            $this->writeParagraphs($objWriter, $shape->getParagraphs());
318        }
319        $objWriter->endElement();
320        $objWriter->endElement();
321    }
322
323    /**
324     * Write table.
325     *
326     * @param XMLWriter $objWriter XML Writer
327     */
328    protected function writeShapeTable(XMLWriter $objWriter, ShapeTable $shape, int $shapeId): void
329    {
330        // p:graphicFrame
331        $objWriter->startElement('p:graphicFrame');
332        // p:graphicFrame/p:nvGraphicFramePr
333        $objWriter->startElement('p:nvGraphicFramePr');
334        // p:graphicFrame/p:nvGraphicFramePr/p:cNvPr
335        $objWriter->startElement('p:cNvPr');
336        $objWriter->writeAttribute('id', $shapeId);
337        $objWriter->writeAttribute('name', $shape->getName());
338        $objWriter->writeAttribute('descr', $shape->getDescription());
339        $objWriter->endElement();
340        // p:graphicFrame/p:nvGraphicFramePr/p:cNvGraphicFramePr
341        $objWriter->startElement('p:cNvGraphicFramePr');
342        // p:graphicFrame/p:nvGraphicFramePr/p:cNvGraphicFramePr/a:graphicFrameLocks
343        $objWriter->startElement('a:graphicFrameLocks');
344        $objWriter->writeAttribute('noGrp', '1');
345        $objWriter->endElement();
346        // p:graphicFrame/p:nvGraphicFramePr/p:cNvGraphicFramePr/
347        $objWriter->endElement();
348        // p:graphicFrame/p:nvGraphicFramePr/p:nvPr
349        $objWriter->startElement('p:nvPr');
350        if ($shape->isPlaceholder()) {
351            $objWriter->startElement('p:ph');
352            $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
353            $objWriter->endElement();
354        }
355        $objWriter->endElement();
356        // p:graphicFrame/p:nvGraphicFramePr/
357        $objWriter->endElement();
358        // p:graphicFrame/p:xfrm
359        $objWriter->startElement('p:xfrm');
360        // p:graphicFrame/p:xfrm/a:off
361        $objWriter->startElement('a:off');
362        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
363        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
364        $objWriter->endElement();
365        // p:graphicFrame/p:xfrm/a:ext
366        $objWriter->startElement('a:ext');
367        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
368        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
369        $objWriter->endElement();
370        // p:graphicFrame/p:xfrm/
371        $objWriter->endElement();
372        // p:graphicFrame/a:graphic
373        $objWriter->startElement('a:graphic');
374        // p:graphicFrame/a:graphic/a:graphicData
375        $objWriter->startElement('a:graphicData');
376        $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/table');
377        // p:graphicFrame/a:graphic/a:graphicData/a:tbl
378        $objWriter->startElement('a:tbl');
379        // p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tblPr
380        $objWriter->startElement('a:tblPr');
381        $objWriter->writeAttribute('firstRow', '1');
382        $objWriter->writeAttribute('bandRow', '1');
383        $objWriter->endElement();
384        // p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tblGrid
385        $objWriter->startElement('a:tblGrid');
386        // Write cell widths
387        $countCells = count($shape->getRow(0)->getCells());
388        for ($cell = 0; $cell < $countCells; ++$cell) {
389            //  p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tblGrid/a:gridCol
390            $objWriter->startElement('a:gridCol');
391            // Calculate column width
392            $width = $shape->getRow(0)->getCell($cell)->getWidth();
393            if (0 == $width) {
394                $colCount = count($shape->getRow(0)->getCells());
395                $totalWidth = $shape->getWidth();
396                $width = $totalWidth / $colCount;
397            }
398            $objWriter->writeAttribute('w', CommonDrawing::pixelsToEmu($width));
399            $objWriter->endElement();
400        }
401        // p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tblGrid/
402        $objWriter->endElement();
403        // Colspan / rowspan containers
404        $colSpan = $rowSpan = [];
405        // Default border style
406        $defaultBorder = new Border();
407        // Write rows
408        $countRows = count($shape->getRows());
409        for ($row = 0; $row < $countRows; ++$row) {
410            // p:graphicFrame/a:graphic/a:graphicData/a:tbl/a:tr
411            $objWriter->startElement('a:tr');
412            $objWriter->writeAttribute('h', CommonDrawing::pixelsToEmu($shape->getRow($row)->getHeight()));
413            // Write cells
414            $countCells = count($shape->getRow($row)->getCells());
415            for ($cell = 0; $cell < $countCells; ++$cell) {
416                // Current cell
417                $currentCell = $shape->getRow($row)->getCell($cell);
418                // Next cell right
419                $hasNextCellRight = $shape->getRow($row)->hasCell($cell + 1);
420                // Next cell below
421                $hasNextRowBelow = $shape->hasRow($row + 1);
422                // a:tc
423                $objWriter->startElement('a:tc');
424                // Colspan
425                if ($currentCell->getColSpan() > 1) {
426                    $objWriter->writeAttribute('gridSpan', $currentCell->getColSpan());
427                    $colSpan[$row] = $currentCell->getColSpan() - 1;
428                } elseif (isset($colSpan[$row]) && $colSpan[$row] > 0) {
429                    --$colSpan[$row];
430                    $objWriter->writeAttribute('hMerge', '1');
431                }
432                // Rowspan
433                if ($currentCell->getRowSpan() > 1) {
434                    $objWriter->writeAttribute('rowSpan', $currentCell->getRowSpan());
435                    $rowSpan[$cell] = $currentCell->getRowSpan() - 1;
436                } elseif (isset($rowSpan[$cell]) && $rowSpan[$cell] > 0) {
437                    --$rowSpan[$cell];
438                    $objWriter->writeAttribute('vMerge', '1');
439                }
440                // a:txBody
441                $objWriter->startElement('a:txBody');
442                // a:txBody/a:bodyPr
443                $objWriter->startElement('a:bodyPr');
444                $objWriter->writeAttribute('wrap', 'square');
445                $objWriter->writeAttribute('rtlCol', '0');
446                // a:txBody/a:bodyPr/a:spAutoFit
447                $objWriter->writeElement('a:spAutoFit', null);
448                // a:txBody/a:bodyPr/
449                $objWriter->endElement();
450                // a:lstStyle
451                $objWriter->writeElement('a:lstStyle', null);
452                // Write paragraphs
453                $this->writeParagraphs($objWriter, $currentCell->getParagraphs());
454                $objWriter->endElement();
455                // a:tcPr
456                $objWriter->startElement('a:tcPr');
457                $firstParagraph = $currentCell->getParagraph(0);
458                $firstParagraphAlignment = $firstParagraph->getAlignment();
459
460                // Text Direction
461                $textDirection = $firstParagraphAlignment->getTextDirection();
462                if (Alignment::TEXT_DIRECTION_HORIZONTAL != $textDirection) {
463                    $objWriter->writeAttribute('vert', $textDirection);
464                }
465                // Alignment (horizontal)
466                $verticalAlign = $firstParagraphAlignment->getVertical();
467                if (Alignment::VERTICAL_BASE != $verticalAlign && Alignment::VERTICAL_AUTO != $verticalAlign) {
468                    $objWriter->writeAttribute('anchor', $verticalAlign);
469                }
470
471                // Margins
472                $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($firstParagraphAlignment->getMarginLeft()));
473                $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($firstParagraphAlignment->getMarginRight()));
474                $objWriter->writeAttribute('marT', CommonDrawing::pixelsToEmu($firstParagraphAlignment->getMarginTop()));
475                $objWriter->writeAttribute('marB', CommonDrawing::pixelsToEmu($firstParagraphAlignment->getMarginBottom()));
476
477                // Determine borders
478                $borderLeft = $currentCell->getBorders()->getLeft();
479                $borderRight = $currentCell->getBorders()->getRight();
480                $borderTop = $currentCell->getBorders()->getTop();
481                $borderBottom = $currentCell->getBorders()->getBottom();
482                $borderDiagonalDown = $currentCell->getBorders()->getDiagonalDown();
483                $borderDiagonalUp = $currentCell->getBorders()->getDiagonalUp();
484                // Fix PowerPoint implementation
485                if ($hasNextCellRight) {
486                    $nextCellRight = $shape->getRow($row)->getCell($cell + 1);
487                    if ($nextCellRight->getBorders()->getRight()->getHashCode() != $defaultBorder->getHashCode()) {
488                        $borderRight = $nextCellRight->getBorders()->getLeft();
489                    }
490                }
491                if ($hasNextRowBelow) {
492                    $nextRowBelow = $shape->getRow($row + 1);
493                    $nextCellBelow = $nextRowBelow->getCell($cell);
494                    if ($nextCellBelow->getBorders()->getBottom()->getHashCode() != $defaultBorder->getHashCode()) {
495                        $borderBottom = $nextCellBelow->getBorders()->getTop();
496                    }
497                }
498                // Write borders
499                $this->writeBorder($objWriter, $borderLeft, 'L');
500                $this->writeBorder($objWriter, $borderRight, 'R');
501                $this->writeBorder($objWriter, $borderTop, 'T');
502                $this->writeBorder($objWriter, $borderBottom, 'B');
503                $this->writeBorder($objWriter, $borderDiagonalDown, 'TlToBr');
504                $this->writeBorder($objWriter, $borderDiagonalUp, 'BlToTr');
505                // Fill
506                $this->writeFill($objWriter, $currentCell->getFill());
507                $objWriter->endElement();
508                $objWriter->endElement();
509            }
510            $objWriter->endElement();
511        }
512        $objWriter->endElement();
513        $objWriter->endElement();
514        $objWriter->endElement();
515        $objWriter->endElement();
516    }
517
518    /**
519     * Write paragraphs.
520     *
521     * @param XMLWriter $objWriter XML Writer
522     * @param array<Paragraph> $paragraphs
523     */
524    protected function writeParagraphs(XMLWriter $objWriter, array $paragraphs): void
525    {
526        // Loop trough paragraphs
527        foreach ($paragraphs as $paragraph) {
528            // a:p
529            $objWriter->startElement('a:p');
530
531            // a:pPr
532            $this->writeParagraphStyles($objWriter, $paragraph, false);
533
534            // Loop trough rich text elements
535            $elements = $paragraph->getRichTextElements();
536            foreach ($elements as $element) {
537                if ($element instanceof BreakElement) {
538                    // a:br
539                    $objWriter->writeElement('a:br', null);
540                } elseif ($element instanceof Run || $element instanceof TextElement) {
541                    // a:r
542                    $objWriter->startElement('a:r');
543
544                    // a:rPr
545                    if ($element instanceof Run) {
546                        $this->writeRunStyles($objWriter, $element);
547                    }
548
549                    // t
550                    $objWriter->startElement('a:t');
551                    $objWriter->writeCData(Text::controlCharacterPHP2OOXML($element->getText()));
552                    $objWriter->endElement();
553
554                    $objWriter->endElement();
555                }
556            }
557
558            $objWriter->endElement();
559        }
560    }
561
562    /**
563     * Write Paragraph Styles (a:pPr).
564     */
565    protected function writeParagraphStyles(XMLWriter $objWriter, Paragraph $paragraph, bool $isPlaceholder = false): void
566    {
567        $objWriter->startElement('a:pPr');
568        $objWriter->writeAttribute('algn', $paragraph->getAlignment()->getHorizontal());
569        $objWriter->writeAttribute('rtl', $paragraph->getAlignment()->isRTL() ? '1' : '0');
570        $objWriter->writeAttribute('fontAlgn', $paragraph->getAlignment()->getVertical());
571        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginLeft()));
572        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getMarginRight()));
573        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($paragraph->getAlignment()->getIndent()));
574        $objWriter->writeAttribute('lvl', $paragraph->getAlignment()->getLevel());
575
576        $objWriter->startElement('a:lnSpc');
577        if ($paragraph->getLineSpacingMode() == Paragraph::LINE_SPACING_MODE_POINT) {
578            $objWriter->startElement('a:spcPts');
579            $objWriter->writeAttribute('val', $paragraph->getLineSpacing() * 100);
580            $objWriter->endElement();
581        } else {
582            $objWriter->startElement('a:spcPct');
583            $objWriter->writeAttribute('val', $paragraph->getLineSpacing() * 1000);
584            $objWriter->endElement();
585        }
586        $objWriter->endElement();
587
588        $objWriter->startElement('a:spcBef');
589        $objWriter->startElement('a:spcPts');
590        $objWriter->writeAttribute('val', $paragraph->getSpacingBefore() * 100);
591        $objWriter->endElement();
592        $objWriter->endElement();
593
594        $objWriter->startElement('a:spcAft');
595        $objWriter->startElement('a:spcPts');
596        $objWriter->writeAttribute('val', $paragraph->getSpacingAfter() * 100);
597        $objWriter->endElement();
598        $objWriter->endElement();
599
600        if (!$isPlaceholder) {
601            // Bullet type specified?
602            if ($paragraph->getBulletStyle()->getBulletType() != Bullet::TYPE_NONE) {
603                // Color
604                // a:buClr must be before a:buFont (else PowerPoint crashes at launch)
605                if ($paragraph->getBulletStyle()->getBulletColor() instanceof Color) {
606                    $objWriter->startElement('a:buClr');
607                    $this->writeColor($objWriter, $paragraph->getBulletStyle()->getBulletColor());
608                    $objWriter->endElement();
609                }
610
611                // a:buFont
612                $objWriter->startElement('a:buFont');
613                $objWriter->writeAttribute('typeface', $paragraph->getBulletStyle()->getBulletFont());
614                $objWriter->endElement();
615
616                if ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_BULLET) {
617                    // a:buChar
618                    $objWriter->startElement('a:buChar');
619                    $objWriter->writeAttribute('char', $paragraph->getBulletStyle()->getBulletChar());
620                    $objWriter->endElement();
621                } elseif ($paragraph->getBulletStyle()->getBulletType() == Bullet::TYPE_NUMERIC) {
622                    // a:buAutoNum
623                    $objWriter->startElement('a:buAutoNum');
624                    $objWriter->writeAttribute('type', $paragraph->getBulletStyle()->getBulletNumericStyle());
625                    if ($paragraph->getBulletStyle()->getBulletNumericStartAt() != 1) {
626                        $objWriter->writeAttribute('startAt', $paragraph->getBulletStyle()->getBulletNumericStartAt());
627                    }
628                    $objWriter->endElement();
629                }
630            }
631        }
632
633        $objWriter->endElement();
634    }
635
636    /**
637     * Write RichTextElement Styles (a:pPr).
638     */
639    protected function writeRunStyles(XMLWriter $objWriter, Run $element): void
640    {
641        // a:rPr
642        $objWriter->startElement('a:rPr');
643
644        // Lang
645        $objWriter->writeAttribute('lang', ($element->getLanguage() ? $element->getLanguage() : 'en-US'));
646
647        $objWriter->writeAttributeIf($element->getFont()->isBold(), 'b', '1');
648        $objWriter->writeAttributeIf($element->getFont()->isItalic(), 'i', '1');
649
650        // Strikethrough
651        $objWriter->writeAttribute('strike', $element->getFont()->getStrikethrough());
652
653        // Size
654        $objWriter->writeAttribute('sz', ($element->getFont()->getSize() * 100));
655
656        // Character spacing
657        $objWriter->writeAttribute('spc', $element->getFont()->getCharacterSpacing());
658
659        // Underline
660        $objWriter->writeAttribute('u', $element->getFont()->getUnderline());
661
662        // Capitalization
663        $objWriter->writeAttribute('cap', $element->getFont()->getCapitalization());
664
665        // Baseline
666        $objWriter->writeAttributeIf($element->getFont()->getBaseline() !== 0, 'baseline', $element->getFont()->getBaseline());
667
668        // Color - a:solidFill
669        $objWriter->startElement('a:solidFill');
670        $this->writeColor($objWriter, $element->getFont()->getColor());
671        $objWriter->endElement();
672
673        // Font
674        // - a:latin
675        // - a:ea
676        // - a:cs
677        $objWriter->startElement('a:' . $element->getFont()->getFormat());
678        $objWriter->writeAttribute('typeface', $element->getFont()->getName());
679        if ($element->getFont()->getPanose() !== '') {
680            $panose = array_map(function (string $value) {
681                return '0' . $value;
682            }, str_split($element->getFont()->getPanose()));
683
684            $objWriter->writeAttribute('panose', implode('', $panose));
685        }
686        $objWriter->writeAttributeIf(
687            $element->getFont()->getPitchFamily() !== 0,
688            'pitchFamily',
689            $element->getFont()->getPitchFamily()
690        );
691        $objWriter->writeAttributeIf(
692            $element->getFont()->getCharset() !== Font::CHARSET_DEFAULT,
693            'charset',
694            dechex($element->getFont()->getCharset())
695        );
696        $objWriter->endElement();
697
698        // a:hlinkClick
699        $this->writeHyperlink($objWriter, $element);
700
701        $objWriter->endElement();
702    }
703
704    /**
705     * Write Line Shape.
706     *
707     * @param XMLWriter $objWriter XML Writer
708     */
709    protected function writeShapeLine(XMLWriter $objWriter, Line $shape, int $shapeId): void
710    {
711        // p:sp
712        $objWriter->startElement('p:cxnSp');
713        // p:nvSpPr
714        $objWriter->startElement('p:nvCxnSpPr');
715        // p:cNvPr
716        $objWriter->startElement('p:cNvPr');
717        $objWriter->writeAttribute('id', $shapeId);
718        $objWriter->writeAttribute('name', '');
719        $objWriter->endElement();
720        // p:cNvCxnSpPr
721        $objWriter->writeElement('p:cNvCxnSpPr', null);
722        // p:nvPr
723        $objWriter->startElement('p:nvPr');
724        if ($shape->isPlaceholder()) {
725            $objWriter->startElement('p:ph');
726            $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
727            $objWriter->endElement();
728        }
729        $objWriter->endElement();
730        $objWriter->endElement();
731        // p:spPr
732        $objWriter->startElement('p:spPr');
733        // a:xfrm
734        $objWriter->startElement('a:xfrm');
735        if ($shape->getWidth() >= 0 && $shape->getHeight() >= 0) {
736            // a:off
737            $objWriter->startElement('a:off');
738            $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
739            $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
740            $objWriter->endElement();
741            // a:ext
742            $objWriter->startElement('a:ext');
743            $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
744            $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
745            $objWriter->endElement();
746        } elseif ($shape->getWidth() < 0 && $shape->getHeight() < 0) {
747            // a:off
748            $objWriter->startElement('a:off');
749            $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX() + $shape->getWidth()));
750            $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY() + $shape->getHeight()));
751            $objWriter->endElement();
752            // a:ext
753            $objWriter->startElement('a:ext');
754            $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(-$shape->getWidth()));
755            $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu(-$shape->getHeight()));
756            $objWriter->endElement();
757        } elseif ($shape->getHeight() < 0) {
758            $objWriter->writeAttribute('flipV', 1);
759            // a:off
760            $objWriter->startElement('a:off');
761            $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
762            $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY() + $shape->getHeight()));
763            $objWriter->endElement();
764            // a:ext
765            $objWriter->startElement('a:ext');
766            $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
767            $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu(-$shape->getHeight()));
768            $objWriter->endElement();
769        } elseif ($shape->getWidth() < 0) {
770            $objWriter->writeAttribute('flipV', 1);
771            // a:off
772            $objWriter->startElement('a:off');
773            $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX() + $shape->getWidth()));
774            $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
775            $objWriter->endElement();
776            // a:ext
777            $objWriter->startElement('a:ext');
778            $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(-$shape->getWidth()));
779            $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
780            $objWriter->endElement();
781        }
782        $objWriter->endElement();
783        // a:prstGeom
784        $objWriter->startElement('a:prstGeom');
785        $objWriter->writeAttribute('prst', 'line');
786
787        // a:prstGeom/a:avLst
788        $objWriter->writeElement('a:avLst');
789
790        $objWriter->endElement();
791        $this->writeBorder($objWriter, $shape->getBorder(), '');
792        $objWriter->endElement();
793        $objWriter->endElement();
794    }
795
796    /**
797     * Write Shadow.
798     */
799    protected function writeShadow(XMLWriter $objWriter, Shadow $oShadow): void
800    {
801        if (!$oShadow->isVisible()) {
802            return;
803        }
804
805        // a:effectLst
806        $objWriter->startElement('a:effectLst');
807
808        // a:outerShdw
809        $objWriter->startElement('a:' . $oShadow->getType());
810        $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($oShadow->getBlurRadius()));
811        $objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($oShadow->getDistance()));
812        $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle((int) $oShadow->getDirection()));
813        $objWriter->writeAttribute('algn', $oShadow->getAlignment());
814        $objWriter->writeAttribute('rotWithShape', '0');
815
816        $this->writeColor($objWriter, $oShadow->getColor(), $oShadow->getAlpha());
817
818        $objWriter->endElement();
819
820        $objWriter->endElement();
821    }
822
823    /**
824     * Write hyperlink.
825     *
826     * @param XMLWriter $objWriter XML Writer
827     * @param AbstractShape|TextElement $shape
828     */
829    protected function writeHyperlink(XMLWriter $objWriter, $shape): void
830    {
831        if (!$shape->hasHyperlink()) {
832            return;
833        }
834        // a:hlinkClick
835        $objWriter->startElement('a:hlinkClick');
836        $objWriter->writeAttribute('r:id', $shape->getHyperlink()->relationId);
837        $objWriter->writeAttribute('tooltip', $shape->getHyperlink()->getTooltip());
838        if ($shape->getHyperlink()->isInternal()) {
839            $objWriter->writeAttribute('action', $shape->getHyperlink()->getUrl());
840        }
841
842        if ($shape->getHyperlink()->isTextColorUsed()) {
843            $objWriter->startElement('a:extLst');
844            $objWriter->startElement('a:ext');
845            $objWriter->writeAttribute('uri', '{A12FA001-AC4F-418D-AE19-62706E023703}');
846            $objWriter->startElement('ahyp:hlinkClr');
847            $objWriter->writeAttribute('xmlns:ahyp', 'http://schemas.microsoft.com/office/drawing/2018/hyperlinkcolor');
848            $objWriter->writeAttribute('val', 'tx');
849            $objWriter->endElement();
850            $objWriter->endElement();
851            $objWriter->endElement();
852        }
853
854        $objWriter->endElement();
855    }
856
857    /**
858     * Write Note Slide.
859     */
860    protected function writeNote(Note $pNote): string
861    {
862        // Create XML writer
863        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
864
865        // XML header
866        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
867
868        // p:notes
869        $objWriter->startElement('p:notes');
870        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
871        $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main');
872        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
873
874        // p:notes/p:cSld
875        $objWriter->startElement('p:cSld');
876
877        // p:notes/p:cSld/p:spTree
878        $objWriter->startElement('p:spTree');
879
880        // p:notes/p:cSld/p:spTree/p:nvGrpSpPr
881        $objWriter->startElement('p:nvGrpSpPr');
882
883        // p:notes/p:cSld/p:spTree/p:nvGrpSpPr/p:cNvPr
884        $objWriter->startElement('p:cNvPr');
885        $objWriter->writeAttribute('id', '1');
886        $objWriter->writeAttribute('name', '');
887        $objWriter->endElement();
888
889        // p:notes/p:cSld/p:spTree/p:nvGrpSpPr/p:cNvGrpSpPr
890        $objWriter->writeElement('p:cNvGrpSpPr', null);
891
892        // p:notes/p:cSld/p:spTree/p:nvGrpSpPr/p:nvPr
893        $objWriter->writeElement('p:nvPr', null);
894
895        // p:notes/p:cSld/p:spTree/p:nvGrpSpPr
896        $objWriter->endElement();
897
898        // p:notes/p:cSld/p:spTree/p:grpSpPr
899        $objWriter->startElement('p:grpSpPr');
900
901        // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm
902        $objWriter->startElement('a:xfrm');
903
904        // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:off
905        $objWriter->startElement('a:off');
906        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX()));
907        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pNote->getOffsetY()));
908        $objWriter->endElement(); // a:off
909
910        // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:ext
911        $objWriter->startElement('a:ext');
912        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pNote->getExtentX()));
913        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pNote->getExtentY()));
914        $objWriter->endElement(); // a:ext
915
916        // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:chOff
917        $objWriter->startElement('a:chOff');
918        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX()));
919        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pNote->getOffsetY()));
920        $objWriter->endElement(); // a:chOff
921
922        // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm/a:chExt
923        $objWriter->startElement('a:chExt');
924        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pNote->getExtentX()));
925        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pNote->getExtentY()));
926        $objWriter->endElement(); // a:chExt
927
928        // p:notes/p:cSld/p:spTree/p:grpSpPr/a:xfrm
929        $objWriter->endElement();
930
931        // p:notes/p:cSld/p:spTree/p:grpSpPr
932        $objWriter->endElement();
933
934        // p:notes/p:cSld/p:spTree/p:sp[1]
935        $objWriter->startElement('p:sp');
936
937        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr
938        $objWriter->startElement('p:nvSpPr');
939
940        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvPr
941        $objWriter->startElement('p:cNvPr');
942        $objWriter->writeAttribute('id', '2');
943        $objWriter->writeAttribute('name', 'Slide Image Placeholder 1');
944        $objWriter->endElement();
945
946        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvSpPr
947        $objWriter->startElement('p:cNvSpPr');
948
949        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvSpPr/a:spLocks
950        $objWriter->startElement('a:spLocks');
951        $objWriter->writeAttribute('noGrp', '1');
952        $objWriter->writeAttribute('noRot', '1');
953        $objWriter->writeAttribute('noChangeAspect', '1');
954        $objWriter->endElement();
955
956        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:cNvSpPr
957        $objWriter->endElement();
958
959        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:nvPr
960        $objWriter->startElement('p:nvPr');
961
962        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:nvPr/p:ph
963        $objWriter->startElement('p:ph');
964        $objWriter->writeAttribute('type', 'sldImg');
965        $objWriter->endElement();
966
967        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr/p:nvPr
968        $objWriter->endElement();
969
970        // p:notes/p:cSld/p:spTree/p:sp[1]/p:nvSpPr
971        $objWriter->endElement();
972
973        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr
974        $objWriter->startElement('p:spPr');
975
976        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm
977        $objWriter->startElement('a:xfrm');
978
979        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm/a:off
980        $objWriter->startElement('a:off');
981        $objWriter->writeAttribute('x', 0);
982        $objWriter->writeAttribute('y', 0);
983        $objWriter->endElement();
984
985        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm/a:ext
986        $objWriter->startElement('a:ext');
987        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu(round($pNote->getExtentX() / 2)));
988        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu(round($pNote->getExtentY() / 2)));
989        $objWriter->endElement();
990
991        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:xfrm
992        $objWriter->endElement();
993
994        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:prstGeom
995        $objWriter->startElement('a:prstGeom');
996        $objWriter->writeAttribute('prst', 'rect');
997
998        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:prstGeom/a:avLst
999        $objWriter->writeElement('a:avLst', null);
1000
1001        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:prstGeom
1002        $objWriter->endElement();
1003
1004        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:noFill
1005        $objWriter->writeElement('a:noFill', null);
1006
1007        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln
1008        $objWriter->startElement('a:ln');
1009        $objWriter->writeAttribute('w', '12700');
1010
1011        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln/a:solidFill
1012        $objWriter->startElement('a:solidFill');
1013
1014        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln/a:solidFill/a:prstClr
1015        $objWriter->startElement('a:prstClr');
1016        $objWriter->writeAttribute('val', 'black');
1017        $objWriter->endElement();
1018
1019        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln/a:solidFill
1020        $objWriter->endElement();
1021
1022        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr/a:ln
1023        $objWriter->endElement();
1024
1025        // p:notes/p:cSld/p:spTree/p:sp[1]/p:spPr
1026        $objWriter->endElement();
1027
1028        // p:notes/p:cSld/p:spTree/p:sp[1]
1029        $objWriter->endElement();
1030
1031        // p:notes/p:cSld/p:spTree/p:sp[2]
1032        $objWriter->startElement('p:sp');
1033
1034        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr
1035        $objWriter->startElement('p:nvSpPr');
1036
1037        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvPr
1038        $objWriter->startElement('p:cNvPr');
1039        $objWriter->writeAttribute('id', '3');
1040        $objWriter->writeAttribute('name', 'Notes Placeholder');
1041        $objWriter->endElement();
1042
1043        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvSpPr
1044        $objWriter->startElement('p:cNvSpPr');
1045
1046        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvSpPr/a:spLocks
1047        $objWriter->startElement('a:spLocks');
1048        $objWriter->writeAttribute('noGrp', '1');
1049        $objWriter->endElement();
1050
1051        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:cNvSpPr
1052        $objWriter->endElement();
1053
1054        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:nvPr
1055        $objWriter->startElement('p:nvPr');
1056
1057        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:nvPr/p:ph
1058        $objWriter->startElement('p:ph');
1059        $objWriter->writeAttribute('type', 'body');
1060        $objWriter->writeAttribute('idx', '1');
1061        $objWriter->endElement();
1062
1063        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr/p:nvPr
1064        $objWriter->endElement();
1065
1066        // p:notes/p:cSld/p:spTree/p:sp[2]/p:nvSpPr
1067        $objWriter->endElement();
1068
1069        // START notes print below rectangle section
1070        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr
1071        $objWriter->startElement('p:spPr');
1072
1073        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm
1074        $objWriter->startElement('a:xfrm');
1075
1076        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm/a:off
1077        $objWriter->startElement('a:off');
1078        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pNote->getOffsetX()));
1079        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu(round($pNote->getExtentY() / 2) + $pNote->getOffsetY()));
1080        $objWriter->endElement();
1081
1082        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm/a:ext
1083        $objWriter->startElement('a:ext');
1084        $objWriter->writeAttribute('cx', '5486400');
1085        $objWriter->writeAttribute('cy', '3600450');
1086        $objWriter->endElement();
1087
1088        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:xfrm
1089        $objWriter->endElement();
1090
1091        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:prstGeom
1092        $objWriter->startElement('a:prstGeom');
1093        $objWriter->writeAttribute('prst', 'rect');
1094
1095        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:prstGeom/a:avLst
1096        $objWriter->writeElement('a:avLst', null);
1097
1098        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr/a:prstGeom
1099        $objWriter->endElement();
1100
1101        // p:notes/p:cSld/p:spTree/p:sp[2]/p:spPr
1102        $objWriter->endElement();
1103
1104        // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody
1105        $objWriter->startElement('p:txBody');
1106
1107        // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody/a:bodyPr
1108        $objWriter->writeElement('a:bodyPr', null);
1109        // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody/a:lstStyle
1110        $objWriter->writeElement('a:lstStyle', null);
1111
1112        // Loop shapes
1113        $shapes = $pNote->getShapeCollection();
1114        foreach ($shapes as $shape) {
1115            // Check type
1116            if ($shape instanceof RichText) {
1117                $paragraphs = $shape->getParagraphs();
1118                $this->writeParagraphs($objWriter, $paragraphs);
1119            }
1120        }
1121
1122        // p:notes/p:cSld/p:spTree/p:sp[2]/p:txBody
1123        $objWriter->endElement();
1124
1125        // p:notes/p:cSld/p:spTree/p:sp[2]
1126        $objWriter->endElement();
1127
1128        // p:notes/p:cSld/p:spTree
1129        $objWriter->endElement();
1130
1131        // p:notes/p:cSld
1132        $objWriter->endElement();
1133
1134        // p:notes
1135        $objWriter->endElement();
1136
1137        // Return
1138        return $objWriter->getData();
1139    }
1140
1141    /**
1142     * Write AutoShape.
1143     *
1144     * @param XMLWriter $objWriter XML Writer
1145     */
1146    protected function writeShapeAutoShape(XMLWriter $objWriter, AutoShape $shape, int $shapeId): void
1147    {
1148        // p:sp
1149        $objWriter->startElement('p:sp');
1150
1151        // p:sp\p:nvSpPr
1152        $objWriter->startElement('p:nvSpPr');
1153        // p:sp\p:nvSpPr\p:cNvPr
1154        $objWriter->startElement('p:cNvPr');
1155        $objWriter->writeAttribute('id', $shapeId);
1156        $objWriter->writeAttribute('name', '');
1157        $objWriter->writeAttribute('descr', '');
1158        // p:sp\p:nvSpPr\p:cNvPr\
1159        $objWriter->endElement();
1160        // p:sp\p:nvSpPr\p:cNvSpPr
1161        $objWriter->writeElement('p:cNvSpPr');
1162        // p:sp\p:nvSpPr\p:nvPr
1163        $objWriter->writeElement('p:nvPr');
1164        // p:sp\p:nvSpPr\
1165        $objWriter->endElement();
1166
1167        // p:sp\p:spPr
1168        $objWriter->startElement('p:spPr');
1169
1170        // p:sp\p:spPr\a:xfrm
1171        $objWriter->startElement('a:xfrm');
1172        $objWriter->writeAttributeIf($shape->getRotation() != 0, 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
1173        // p:sp\p:spPr\a:xfrm\a:off
1174        $objWriter->startElement('a:off');
1175        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
1176        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
1177        $objWriter->endElement();
1178        // p:sp\p:spPr\a:xfrm\a:ext
1179        $objWriter->startElement('a:ext');
1180        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
1181        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
1182        $objWriter->endElement();
1183        // p:sp\p:spPr\a:xfrm\
1184        $objWriter->endElement();
1185
1186        // p:sp\p:spPr\a:prstGeom
1187        $objWriter->startElement('a:prstGeom');
1188        $objWriter->writeAttribute('prst', $shape->getType());
1189        // p:sp\p:spPr\a:prstGeom\a:avLst
1190        $objWriter->writeElement('a:avLst');
1191        // p:sp\p:spPr\a:prstGeom\
1192        $objWriter->endElement();
1193        // Fill
1194        $this->writeFill($objWriter, $shape->getFill());
1195        // Outline
1196        $this->writeOutline($objWriter, $shape->getOutline());
1197
1198        // p:sp\p:spPr\
1199        $objWriter->endElement();
1200        // p:sp\p:txBody
1201        $objWriter->startElement('p:txBody');
1202        // p:sp\p:txBody\a:bodyPr
1203        $objWriter->startElement('a:bodyPr');
1204        $objWriter->writeAttribute('vertOverflow', 'clip');
1205        $objWriter->writeAttribute('rtlCol', '0');
1206        $objWriter->writeAttribute('anchor', 'ctr');
1207        // p:sp\p:txBody\a:bodyPr\
1208        $objWriter->endElement();
1209
1210        // p:sp\p:txBody\a:lstStyle
1211        $objWriter->writeElement('a:lstStyle');
1212
1213        // p:sp\p:txBody\a:p
1214        $objWriter->startElement('a:p');
1215
1216        // p:sp\p:txBody\a:p\a:pPr
1217        $objWriter->writeElementBlock('a:pPr', [
1218            'algn' => 'ctr',
1219        ]);
1220        // p:sp\p:txBody\a:p\a:r
1221        $objWriter->startElement('a:r');
1222        // p:sp\p:txBody\a:p\a:r\a:t
1223        $objWriter->startElement('a:t');
1224        $objWriter->writeCData(Text::controlCharacterPHP2OOXML($shape->getText()));
1225        $objWriter->endElement();
1226        // p:sp\p:txBody\a:p\a:r\
1227        $objWriter->endElement();
1228        // p:sp\p:txBody\a:p\
1229        $objWriter->endElement();
1230        // p:sp\p:txBody\
1231        $objWriter->endElement();
1232        // p:sp\
1233        $objWriter->endElement();
1234    }
1235
1236    /**
1237     * Write chart.
1238     *
1239     * @param XMLWriter $objWriter XML Writer
1240     */
1241    protected function writeShapeChart(XMLWriter $objWriter, ShapeChart $shape, int $shapeId): void
1242    {
1243        // p:graphicFrame
1244        $objWriter->startElement('p:graphicFrame');
1245        // p:nvGraphicFramePr
1246        $objWriter->startElement('p:nvGraphicFramePr');
1247        // p:cNvPr
1248        $objWriter->startElement('p:cNvPr');
1249        $objWriter->writeAttribute('id', $shapeId);
1250        $objWriter->writeAttribute('name', $shape->getName());
1251        $objWriter->writeAttribute('descr', $shape->getDescription());
1252        $objWriter->endElement();
1253        // p:cNvGraphicFramePr
1254        $objWriter->writeElement('p:cNvGraphicFramePr', null);
1255        // p:nvPr
1256        $objWriter->startElement('p:nvPr');
1257        if ($shape->isPlaceholder()) {
1258            $objWriter->startElement('p:ph');
1259            $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
1260            $objWriter->endElement();
1261        }
1262        $objWriter->endElement();
1263        $objWriter->endElement();
1264        // p:xfrm
1265        $objWriter->startElement('p:xfrm');
1266        $objWriter->writeAttributeIf(0 != $shape->getRotation(), 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
1267        // a:off
1268        $objWriter->startElement('a:off');
1269        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
1270        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
1271        $objWriter->endElement();
1272        // a:ext
1273        $objWriter->startElement('a:ext');
1274        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
1275        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
1276        $objWriter->endElement();
1277        $objWriter->endElement();
1278        // a:graphic
1279        $objWriter->startElement('a:graphic');
1280        // a:graphicData
1281        $objWriter->startElement('a:graphicData');
1282        $objWriter->writeAttribute('uri', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
1283        // c:chart
1284        $objWriter->startElement('c:chart');
1285        $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
1286        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
1287        $objWriter->writeAttribute('r:id', $shape->relationId);
1288        $objWriter->endElement();
1289        $objWriter->endElement();
1290        $objWriter->endElement();
1291        $objWriter->endElement();
1292    }
1293
1294    /**
1295     * Write pic.
1296     *
1297     * @param XMLWriter $objWriter XML Writer
1298     */
1299    protected function writeShapePic(XMLWriter $objWriter, AbstractGraphic $shape, int $shapeId): void
1300    {
1301        // p:pic
1302        $objWriter->startElement('p:pic');
1303
1304        // p:nvPicPr
1305        $objWriter->startElement('p:nvPicPr');
1306
1307        // p:cNvPr
1308        $objWriter->startElement('p:cNvPr');
1309        $objWriter->writeAttribute('id', $shapeId);
1310        $objWriter->writeAttribute('name', $shape->getName());
1311        $objWriter->writeAttribute('descr', $shape->getDescription());
1312
1313        // a:hlinkClick
1314        if ($shape->hasHyperlink()) {
1315            $this->writeHyperlink($objWriter, $shape);
1316        }
1317
1318        if ($shape instanceof AbstractDrawingAdapter && $shape->getExtension() == 'svg') {
1319            $objWriter->startElement('a:extLst');
1320            $objWriter->startElement('a:ext');
1321            $objWriter->writeAttribute('uri', '{FF2B5EF4-FFF2-40B4-BE49-F238E27FC236}');
1322            $objWriter->startElement('a16:creationId');
1323            $objWriter->writeAttribute('xmlns:a16', 'http://schemas.microsoft.com/office/drawing/2014/main');
1324            $objWriter->writeAttribute('id', '{F8CFD691-5332-EB49-9B42-7D7B3DB9185D}');
1325            $objWriter->endElement();
1326            $objWriter->endElement();
1327            $objWriter->endElement();
1328        }
1329
1330        $objWriter->endElement();
1331
1332        // p:cNvPicPr
1333        $objWriter->startElement('p:cNvPicPr');
1334
1335        // a:picLocks
1336        $objWriter->startElement('a:picLocks');
1337        $objWriter->writeAttribute('noChangeAspect', '1');
1338        $objWriter->endElement();
1339
1340        // #p:cNvPicPr
1341        $objWriter->endElement();
1342
1343        // p:nvPr
1344        $objWriter->startElement('p:nvPr');
1345        // PlaceHolder
1346        if ($shape->isPlaceholder()) {
1347            $objWriter->startElement('p:ph');
1348            $objWriter->writeAttribute('type', $shape->getPlaceholder()->getType());
1349            $objWriter->endElement();
1350        }
1351        // @link : https://github.com/stefslon/exportToPPTX/blob/master/exportToPPTX.m#L2128
1352        if ($shape instanceof Media) {
1353            // p:nvPr > a:videoFile
1354            $objWriter->startElement('a:videoFile');
1355            $objWriter->writeAttribute('r:link', $shape->relationId);
1356            $objWriter->endElement();
1357            // p:nvPr > p:extLst
1358            $objWriter->startElement('p:extLst');
1359            // p:nvPr > p:extLst > p:ext
1360            $objWriter->startElement('p:ext');
1361            $objWriter->writeAttribute('uri', '{DAA4B4D4-6D71-4841-9C94-3DE7FCFB9230}');
1362            // p:nvPr > p:extLst > p:ext > p14:media
1363            $objWriter->startElement('p14:media');
1364            $objWriter->writeAttribute('r:embed', 'rId' . ((int) substr($shape->relationId, strlen('rId')) + 1));
1365            $objWriter->writeAttribute('xmlns:p14', 'http://schemas.microsoft.com/office/powerpoint/2010/main');
1366            // p:nvPr > p:extLst > p:ext > ##p14:media
1367            $objWriter->endElement();
1368            // p:nvPr > p:extLst > ##p:ext
1369            $objWriter->endElement();
1370            // p:nvPr > ##p:extLst
1371            $objWriter->endElement();
1372        }
1373        // ##p:nvPr
1374        $objWriter->endElement();
1375        $objWriter->endElement();
1376
1377        // p:blipFill
1378        $objWriter->startElement('p:blipFill');
1379
1380        // a:blip
1381        $objWriter->startElement('a:blip');
1382        $objWriter->writeAttribute('r:embed', $shape->relationId);
1383
1384        if ($shape instanceof AbstractDrawingAdapter && $shape->getExtension() == 'svg') {
1385            // a:extLst
1386            $objWriter->startElement('a:extLst');
1387
1388            // a:extLst > a:ext
1389            $objWriter->startElement('a:ext');
1390            $objWriter->writeAttribute('uri', '{28A0092B-C50C-407E-A947-70E740481C1C}');
1391            // a:extLst > a:ext > a14:useLocalDpi
1392            $objWriter->startElement('a14:useLocalDpi');
1393            $objWriter->writeAttribute('xmlns:a14', 'http://schemas.microsoft.com/office/drawing/2010/main');
1394            $objWriter->writeAttribute('val', '0');
1395            // a:extLst > a:ext > ##a14:useLocalDpi
1396            $objWriter->endElement();
1397            // a:extLst > ##a:ext
1398            $objWriter->endElement();
1399
1400            // a:extLst > a:ext
1401            $objWriter->startElement('a:ext');
1402            $objWriter->writeAttribute('uri', '{96DAC541-7B7A-43D3-8B79-37D633B846F1}');
1403            // a:extLst > a:ext > asvg:svgBlip
1404            $objWriter->startElement('asvg:svgBlip');
1405            $objWriter->writeAttribute('xmlns:asvg', 'http://schemas.microsoft.com/office/drawing/2016/SVG/main');
1406            $objWriter->writeAttribute('r:embed', $shape->relationId);
1407            // a:extLst > a:ext > ##asvg:svgBlip
1408            $objWriter->endElement();
1409            // a:extLst > ##a:ext
1410            $objWriter->endElement();
1411
1412            // ##a:extLst
1413            $objWriter->endElement();
1414        }
1415
1416        $objWriter->endElement();
1417
1418        // a:stretch
1419        $objWriter->startElement('a:stretch');
1420        $objWriter->writeElement('a:fillRect');
1421        $objWriter->endElement();
1422
1423        $objWriter->endElement();
1424
1425        // p:spPr
1426        $objWriter->startElement('p:spPr');
1427        // a:xfrm
1428        $objWriter->startElement('a:xfrm');
1429        $objWriter->writeAttributeIf(0 != $shape->getRotation(), 'rot', CommonDrawing::degreesToAngle((int) $shape->getRotation()));
1430
1431        // a:off
1432        $objWriter->startElement('a:off');
1433        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($shape->getOffsetX()));
1434        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($shape->getOffsetY()));
1435        $objWriter->endElement();
1436
1437        // a:ext
1438        $objWriter->startElement('a:ext');
1439        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($shape->getWidth()));
1440        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($shape->getHeight()));
1441        $objWriter->endElement();
1442
1443        $objWriter->endElement();
1444
1445        // a:prstGeom
1446        $objWriter->startElement('a:prstGeom');
1447        $objWriter->writeAttribute('prst', 'rect');
1448        // // a:prstGeom/a:avLst
1449        $objWriter->writeElement('a:avLst', null);
1450        // ##a:prstGeom
1451        $objWriter->endElement();
1452
1453        $this->writeFill($objWriter, $shape->getFill());
1454        $this->writeBorder($objWriter, $shape->getBorder(), '');
1455        $this->writeShadow($objWriter, $shape->getShadow());
1456
1457        $objWriter->endElement();
1458
1459        $objWriter->endElement();
1460    }
1461
1462    /**
1463     * Write group.
1464     *
1465     * @param XMLWriter $objWriter XML Writer
1466     */
1467    protected function writeShapeGroup(XMLWriter $objWriter, Group $group, int &$shapeId): void
1468    {
1469        // p:grpSp
1470        $objWriter->startElement('p:grpSp');
1471        // p:nvGrpSpPr
1472        $objWriter->startElement('p:nvGrpSpPr');
1473        // p:cNvPr
1474        $objWriter->startElement('p:cNvPr');
1475        $objWriter->writeAttribute('name', 'Group ' . $shapeId++);
1476        $objWriter->writeAttribute('id', $shapeId);
1477        $objWriter->endElement(); // p:cNvPr
1478        // NOTE: Re: $shapeId This seems to be how PowerPoint 2010 does business.
1479        // p:cNvGrpSpPr
1480        $objWriter->writeElement('p:cNvGrpSpPr', null);
1481        // p:nvPr
1482        $objWriter->writeElement('p:nvPr', null);
1483        $objWriter->endElement(); // p:nvGrpSpPr
1484        // p:grpSpPr
1485        $objWriter->startElement('p:grpSpPr');
1486        // a:xfrm
1487        $objWriter->startElement('a:xfrm');
1488        // a:off
1489        $objWriter->startElement('a:off');
1490        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX()));
1491        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY()));
1492        $objWriter->endElement(); // a:off
1493        // a:ext
1494        $objWriter->startElement('a:ext');
1495        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX()));
1496        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY()));
1497        $objWriter->endElement(); // a:ext
1498        // a:chOff
1499        $objWriter->startElement('a:chOff');
1500        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($group->getOffsetX()));
1501        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($group->getOffsetY()));
1502        $objWriter->endElement(); // a:chOff
1503        // a:chExt
1504        $objWriter->startElement('a:chExt');
1505        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($group->getExtentX()));
1506        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($group->getExtentY()));
1507        $objWriter->endElement(); // a:chExt
1508        $objWriter->endElement(); // a:xfrm
1509        $objWriter->endElement(); // p:grpSpPr
1510
1511        $this->writeShapeCollection($objWriter, $group->getShapeCollection(), $shapeId);
1512
1513        $objWriter->endElement(); // p:grpSp
1514    }
1515
1516    protected function writeSlideBackground(AbstractSlideAlias $pSlide, XMLWriter $objWriter): void
1517    {
1518        if (!($pSlide->getBackground() instanceof Slide\AbstractBackground)) {
1519            return;
1520        }
1521        $oBackground = $pSlide->getBackground();
1522        // p:bg
1523        $objWriter->startElement('p:bg');
1524        if ($oBackground instanceof Slide\Background\Color) {
1525            // p:bgPr
1526            $objWriter->startElement('p:bgPr');
1527            // a:solidFill
1528            $objWriter->startElement('a:solidFill');
1529            // a:srgbClr
1530            $objWriter->startElement('a:srgbClr');
1531            $objWriter->writeAttribute('val', $oBackground->getColor()->getRGB());
1532            $objWriter->endElement();
1533            // > a:solidFill
1534            $objWriter->endElement();
1535
1536            // p:bgPr/a:effectLst
1537            $objWriter->writeElement('a:effectLst');
1538
1539            // > p:bgPr
1540            $objWriter->endElement();
1541        }
1542        if ($oBackground instanceof Slide\Background\Image) {
1543            // p:bgPr
1544            $objWriter->startElement('p:bgPr');
1545            // a:blipFill
1546            $objWriter->startElement('a:blipFill');
1547            // a:blip
1548            $objWriter->startElement('a:blip');
1549            $objWriter->writeAttribute('r:embed', $oBackground->relationId);
1550            // > a:blipFill
1551            $objWriter->endElement();
1552            // a:stretch
1553            $objWriter->startElement('a:stretch');
1554            // a:fillRect
1555            $objWriter->writeElement('a:fillRect');
1556            // > a:stretch
1557            $objWriter->endElement();
1558            // > a:blipFill
1559            $objWriter->endElement();
1560            // > p:bgPr
1561            $objWriter->endElement();
1562        }
1563        // @link : http://www.officeopenxml.com/prSlide-background.php
1564        if ($oBackground instanceof Slide\Background\SchemeColor) {
1565            // p:bgRef
1566            $objWriter->startElement('p:bgRef');
1567            $objWriter->writeAttribute('idx', '1001');
1568            // a:schemeClr
1569            $objWriter->startElement('a:schemeClr');
1570            $objWriter->writeAttribute('val', $oBackground->getSchemeColor()->getValue());
1571            $objWriter->endElement();
1572            // > p:bgRef
1573            $objWriter->endElement();
1574        }
1575        // > p:bg
1576        $objWriter->endElement();
1577    }
1578
1579    /**
1580     * Write Transition Slide.
1581     *
1582     * @see http://officeopenxml.com/prSlide-transitions.php
1583     */
1584    protected function writeSlideTransition(XMLWriter $objWriter, ?Slide\Transition $transition): void
1585    {
1586        if (!$transition) {
1587            return;
1588        }
1589        $objWriter->startElement('p:transition');
1590        if (null !== $transition->getSpeed()) {
1591            $objWriter->writeAttribute('spd', $transition->getSpeed());
1592        }
1593        $objWriter->writeAttribute('advClick', $transition->hasManualTrigger() ? '1' : '0');
1594        if ($transition->hasTimeTrigger()) {
1595            $objWriter->writeAttribute('advTm', $transition->getAdvanceTimeTrigger());
1596        }
1597
1598        switch ($transition->getTransitionType()) {
1599            case Slide\Transition::TRANSITION_BLINDS_HORIZONTAL:
1600                $objWriter->startElement('p:blinds');
1601                $objWriter->writeAttribute('dir', 'horz');
1602                $objWriter->endElement();
1603
1604                break;
1605            case Slide\Transition::TRANSITION_BLINDS_VERTICAL:
1606                $objWriter->startElement('p:blinds');
1607                $objWriter->writeAttribute('dir', 'vert');
1608                $objWriter->endElement();
1609
1610                break;
1611            case Slide\Transition::TRANSITION_CHECKER_HORIZONTAL:
1612                $objWriter->startElement('p:checker');
1613                $objWriter->writeAttribute('dir', 'horz');
1614                $objWriter->endElement();
1615
1616                break;
1617            case Slide\Transition::TRANSITION_CHECKER_VERTICAL:
1618                $objWriter->startElement('p:checker');
1619                $objWriter->writeAttribute('dir', 'vert');
1620                $objWriter->endElement();
1621
1622                break;
1623            case Slide\Transition::TRANSITION_CIRCLE:
1624                $objWriter->writeElement('p:circle');
1625
1626                break;
1627            case Slide\Transition::TRANSITION_COMB_HORIZONTAL:
1628                $objWriter->startElement('p:comb');
1629                $objWriter->writeAttribute('dir', 'horz');
1630                $objWriter->endElement();
1631
1632                break;
1633            case Slide\Transition::TRANSITION_COMB_VERTICAL:
1634                $objWriter->startElement('p:comb');
1635                $objWriter->writeAttribute('dir', 'vert');
1636                $objWriter->endElement();
1637
1638                break;
1639            case Slide\Transition::TRANSITION_COVER_DOWN:
1640                $objWriter->startElement('p:cover');
1641                $objWriter->writeAttribute('dir', 'd');
1642                $objWriter->endElement();
1643
1644                break;
1645            case Slide\Transition::TRANSITION_COVER_LEFT:
1646                $objWriter->startElement('p:cover');
1647                $objWriter->writeAttribute('dir', 'l');
1648                $objWriter->endElement();
1649
1650                break;
1651            case Slide\Transition::TRANSITION_COVER_LEFT_DOWN:
1652                $objWriter->startElement('p:cover');
1653                $objWriter->writeAttribute('dir', 'ld');
1654                $objWriter->endElement();
1655
1656                break;
1657            case Slide\Transition::TRANSITION_COVER_LEFT_UP:
1658                $objWriter->startElement('p:cover');
1659                $objWriter->writeAttribute('dir', 'lu');
1660                $objWriter->endElement();
1661
1662                break;
1663            case Slide\Transition::TRANSITION_COVER_RIGHT:
1664                $objWriter->startElement('p:cover');
1665                $objWriter->writeAttribute('dir', 'r');
1666                $objWriter->endElement();
1667
1668                break;
1669            case Slide\Transition::TRANSITION_COVER_RIGHT_DOWN:
1670                $objWriter->startElement('p:cover');
1671                $objWriter->writeAttribute('dir', 'rd');
1672                $objWriter->endElement();
1673
1674                break;
1675            case Slide\Transition::TRANSITION_COVER_RIGHT_UP:
1676                $objWriter->startElement('p:cover');
1677                $objWriter->writeAttribute('dir', 'ru');
1678                $objWriter->endElement();
1679
1680                break;
1681            case Slide\Transition::TRANSITION_COVER_UP:
1682                $objWriter->startElement('p:cover');
1683                $objWriter->writeAttribute('dir', 'u');
1684                $objWriter->endElement();
1685
1686                break;
1687            case Slide\Transition::TRANSITION_CUT:
1688                $objWriter->writeElement('p:cut');
1689
1690                break;
1691            case Slide\Transition::TRANSITION_DIAMOND:
1692                $objWriter->writeElement('p:diamond');
1693
1694                break;
1695            case Slide\Transition::TRANSITION_DISSOLVE:
1696                $objWriter->writeElement('p:dissolve');
1697
1698                break;
1699            case Slide\Transition::TRANSITION_FADE:
1700                $objWriter->writeElement('p:fade');
1701
1702                break;
1703            case Slide\Transition::TRANSITION_NEWSFLASH:
1704                $objWriter->writeElement('p:newsflash');
1705
1706                break;
1707            case Slide\Transition::TRANSITION_PLUS:
1708                $objWriter->writeElement('p:plus');
1709
1710                break;
1711            case Slide\Transition::TRANSITION_PULL_DOWN:
1712                $objWriter->startElement('p:pull');
1713                $objWriter->writeAttribute('dir', 'd');
1714                $objWriter->endElement();
1715
1716                break;
1717            case Slide\Transition::TRANSITION_PULL_LEFT:
1718                $objWriter->startElement('p:pull');
1719                $objWriter->writeAttribute('dir', 'l');
1720                $objWriter->endElement();
1721
1722                break;
1723            case Slide\Transition::TRANSITION_PULL_RIGHT:
1724                $objWriter->startElement('p:pull');
1725                $objWriter->writeAttribute('dir', 'r');
1726                $objWriter->endElement();
1727
1728                break;
1729            case Slide\Transition::TRANSITION_PULL_UP:
1730                $objWriter->startElement('p:pull');
1731                $objWriter->writeAttribute('dir', 'u');
1732                $objWriter->endElement();
1733
1734                break;
1735            case Slide\Transition::TRANSITION_PUSH_DOWN:
1736                $objWriter->startElement('p:push');
1737                $objWriter->writeAttribute('dir', 'd');
1738                $objWriter->endElement();
1739
1740                break;
1741            case Slide\Transition::TRANSITION_PUSH_LEFT:
1742                $objWriter->startElement('p:push');
1743                $objWriter->writeAttribute('dir', 'l');
1744                $objWriter->endElement();
1745
1746                break;
1747            case Slide\Transition::TRANSITION_PUSH_RIGHT:
1748                $objWriter->startElement('p:push');
1749                $objWriter->writeAttribute('dir', 'r');
1750                $objWriter->endElement();
1751
1752                break;
1753            case Slide\Transition::TRANSITION_PUSH_UP:
1754                $objWriter->startElement('p:push');
1755                $objWriter->writeAttribute('dir', 'u');
1756                $objWriter->endElement();
1757
1758                break;
1759            case Slide\Transition::TRANSITION_RANDOM:
1760                $objWriter->writeElement('p:random');
1761
1762                break;
1763            case Slide\Transition::TRANSITION_RANDOMBAR_HORIZONTAL:
1764                $objWriter->startElement('p:randomBar');
1765                $objWriter->writeAttribute('dir', 'horz');
1766                $objWriter->endElement();
1767
1768                break;
1769            case Slide\Transition::TRANSITION_RANDOMBAR_VERTICAL:
1770                $objWriter->startElement('p:randomBar');
1771                $objWriter->writeAttribute('dir', 'vert');
1772                $objWriter->endElement();
1773
1774                break;
1775            case Slide\Transition::TRANSITION_SPLIT_IN_HORIZONTAL:
1776                $objWriter->startElement('p:split');
1777                $objWriter->writeAttribute('dir', 'in');
1778                $objWriter->writeAttribute('orient', 'horz');
1779                $objWriter->endElement();
1780
1781                break;
1782            case Slide\Transition::TRANSITION_SPLIT_OUT_HORIZONTAL:
1783                $objWriter->startElement('p:split');
1784                $objWriter->writeAttribute('dir', 'out');
1785                $objWriter->writeAttribute('orient', 'horz');
1786                $objWriter->endElement();
1787
1788                break;
1789            case Slide\Transition::TRANSITION_SPLIT_IN_VERTICAL:
1790                $objWriter->startElement('p:split');
1791                $objWriter->writeAttribute('dir', 'in');
1792                $objWriter->writeAttribute('orient', 'vert');
1793                $objWriter->endElement();
1794
1795                break;
1796            case Slide\Transition::TRANSITION_SPLIT_OUT_VERTICAL:
1797                $objWriter->startElement('p:split');
1798                $objWriter->writeAttribute('dir', 'out');
1799                $objWriter->writeAttribute('orient', 'vert');
1800                $objWriter->endElement();
1801
1802                break;
1803            case Slide\Transition::TRANSITION_STRIPS_LEFT_DOWN:
1804                $objWriter->startElement('p:strips');
1805                $objWriter->writeAttribute('dir', 'ld');
1806                $objWriter->endElement();
1807
1808                break;
1809            case Slide\Transition::TRANSITION_STRIPS_LEFT_UP:
1810                $objWriter->startElement('p:strips');
1811                $objWriter->writeAttribute('dir', 'lu');
1812                $objWriter->endElement();
1813
1814                break;
1815            case Slide\Transition::TRANSITION_STRIPS_RIGHT_DOWN:
1816                $objWriter->startElement('p:strips');
1817                $objWriter->writeAttribute('dir', 'rd');
1818                $objWriter->endElement();
1819
1820                break;
1821            case Slide\Transition::TRANSITION_STRIPS_RIGHT_UP:
1822                $objWriter->startElement('p:strips');
1823                $objWriter->writeAttribute('dir', 'ru');
1824                $objWriter->endElement();
1825
1826                break;
1827            case Slide\Transition::TRANSITION_WEDGE:
1828                $objWriter->writeElement('p:wedge');
1829
1830                break;
1831            case Slide\Transition::TRANSITION_WIPE_DOWN:
1832                $objWriter->startElement('p:wipe');
1833                $objWriter->writeAttribute('dir', 'd');
1834                $objWriter->endElement();
1835
1836                break;
1837            case Slide\Transition::TRANSITION_WIPE_LEFT:
1838                $objWriter->startElement('p:wipe');
1839                $objWriter->writeAttribute('dir', 'l');
1840                $objWriter->endElement();
1841
1842                break;
1843            case Slide\Transition::TRANSITION_WIPE_RIGHT:
1844                $objWriter->startElement('p:wipe');
1845                $objWriter->writeAttribute('dir', 'r');
1846                $objWriter->endElement();
1847
1848                break;
1849            case Slide\Transition::TRANSITION_WIPE_UP:
1850                $objWriter->startElement('p:wipe');
1851                $objWriter->writeAttribute('dir', 'u');
1852                $objWriter->endElement();
1853
1854                break;
1855            case Slide\Transition::TRANSITION_ZOOM_IN:
1856                $objWriter->startElement('p:zoom');
1857                $objWriter->writeAttribute('dir', 'in');
1858                $objWriter->endElement();
1859
1860                break;
1861            case Slide\Transition::TRANSITION_ZOOM_OUT:
1862                $objWriter->startElement('p:zoom');
1863                $objWriter->writeAttribute('dir', 'out');
1864                $objWriter->endElement();
1865
1866                break;
1867        }
1868
1869        $objWriter->endElement();
1870    }
1871
1872    private function getGUID(): string
1873    {
1874        if (function_exists('com_create_guid')) {
1875            return com_create_guid();
1876        }
1877        mt_srand((int) (microtime(true) * 10000));
1878        $charid = strtoupper(md5(uniqid((string) mt_rand(), true)));
1879        $hyphen = chr(45); // "-"
1880        $uuid = chr(123)// "{"
1881            . substr($charid, 0, 8) . $hyphen
1882            . substr($charid, 8, 4) . $hyphen
1883            . substr($charid, 12, 4) . $hyphen
1884            . substr($charid, 16, 4) . $hyphen
1885            . substr($charid, 20, 12)
1886            . chr(125); // "}"
1887
1888        return $uuid;
1889    }
1890}