Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
83.92% covered (warning)
83.92%
287 / 342
25.00% covered (danger)
25.00%
1 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
PptSlides
83.92% covered (warning)
83.92%
287 / 342
25.00% covered (danger)
25.00%
1 / 4
100.02
0.00% covered (danger)
0.00%
0 / 1
 render
90.00% covered (success)
90.00%
9 / 10
0.00% covered (danger)
0.00%
0 / 1
5.03
 writeSlideRelationships
71.65% covered (warning)
71.65%
91 / 127
0.00% covered (danger)
0.00%
0 / 1
134.62
 writeSlide
72.73% covered (warning)
72.73%
48 / 66
0.00% covered (danger)
0.00%
0 / 1
4.32
 writeSlideAnimations
100.00% covered (success)
100.00%
139 / 139
100.00% covered (success)
100.00%
1 / 1
9
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 PhpOffice\Common\Adapter\Zip\ZipInterface;
23use PhpOffice\Common\Drawing as CommonDrawing;
24use PhpOffice\Common\XMLWriter;
25use PhpOffice\PhpPresentation\Shape\Chart as ShapeChart;
26use PhpOffice\PhpPresentation\Shape\Comment;
27use PhpOffice\PhpPresentation\Shape\Drawing as ShapeDrawing;
28use PhpOffice\PhpPresentation\Shape\Group;
29use PhpOffice\PhpPresentation\Shape\Media;
30use PhpOffice\PhpPresentation\Shape\RichText;
31use PhpOffice\PhpPresentation\Shape\RichText\Run;
32use PhpOffice\PhpPresentation\Shape\RichText\TextElement;
33use PhpOffice\PhpPresentation\Shape\Table as ShapeTable;
34use PhpOffice\PhpPresentation\ShapeContainerInterface;
35use PhpOffice\PhpPresentation\Slide;
36use PhpOffice\PhpPresentation\Slide\Background\Image;
37use PhpOffice\PhpPresentation\Slide\Note;
38
39class PptSlides extends AbstractSlide
40{
41    /**
42     * Add slides (drawings, ...) and slide relationships (drawings, ...).
43     */
44    public function render(): ZipInterface
45    {
46        foreach ($this->oPresentation->getAllSlides() as $idx => $oSlide) {
47            $this->oZip->addFromString('ppt/slides/_rels/slide' . ($idx + 1) . '.xml.rels', $this->writeSlideRelationships($oSlide));
48            $this->oZip->addFromString('ppt/slides/slide' . ($idx + 1) . '.xml', $this->writeSlide($oSlide));
49
50            // Add note slide
51            if ($oSlide->getNote() instanceof Note) {
52                if (count($oSlide->getNote()->getShapeCollection()) > 0) {
53                    $this->oZip->addFromString('ppt/notesSlides/notesSlide' . ($idx + 1) . '.xml', $this->writeNote($oSlide->getNote()));
54                }
55            }
56
57            // Add background image slide
58            $oBkgImage = $oSlide->getBackground();
59            if ($oBkgImage instanceof Image) {
60                $this->oZip->addFromString('ppt/media/' . $oBkgImage->getIndexedFilename((string) $idx), file_get_contents($oBkgImage->getPath()));
61            }
62        }
63
64        return $this->oZip;
65    }
66
67    /**
68     * Write slide relationships to XML format.
69     *
70     * @return string XML Output
71     */
72    protected function writeSlideRelationships(Slide $pSlide): string
73    {
74        //@todo Group all getShapeCollection()->getIterator
75
76        // Create XML writer
77        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
78
79        // XML header
80        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
81
82        // Relationships
83        $objWriter->startElement('Relationships');
84        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
85
86        // Starting relation id
87        $relId = 1;
88        $idxSlide = $pSlide->getParent()->getIndex($pSlide);
89
90        // Write slideLayout relationship
91        $layoutId = 1;
92        if ($pSlide->getSlideLayout()) {
93            $layoutId = $pSlide->getSlideLayout()->layoutNr;
94        }
95        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout', '../slideLayouts/slideLayout' . $layoutId . '.xml');
96        ++$relId;
97
98        // Write drawing relationships?
99        if (count($pSlide->getShapeCollection()) > 0) {
100            $collections = [$pSlide->getShapeCollection()];
101
102            // Loop trough images and write relationships
103            while (count($collections)) {
104                $collection = array_shift($collections);
105
106                foreach ($collection as $currentShape) {
107                    if ($currentShape instanceof Media) {
108                        // Write relationship for image drawing
109                        $currentShape->relationId = 'rId' . $relId;
110                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/video', '../media/' . $currentShape->getIndexedFilename());
111                        ++$relId;
112                        $this->writeRelationship($objWriter, $relId, 'http://schemas.microsoft.com/office/2007/relationships/media', '../media/' . $currentShape->getIndexedFilename());
113                        ++$relId;
114                    } elseif ($currentShape instanceof ShapeDrawing\AbstractDrawingAdapter) {
115                        // Write relationship for image drawing
116                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $currentShape->getIndexedFilename());
117                        $currentShape->relationId = 'rId' . $relId;
118                        ++$relId;
119                    } elseif ($currentShape instanceof ShapeChart) {
120                        // Write relationship for chart drawing
121                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/chart', '../charts/' . $currentShape->getIndexedFilename());
122                        $currentShape->relationId = 'rId' . $relId;
123                        ++$relId;
124                    } elseif ($currentShape instanceof ShapeContainerInterface) {
125                        $collections[] = $currentShape->getShapeCollection();
126                    }
127                }
128            }
129        }
130
131        // Write background relationships?
132        $oBackground = $pSlide->getBackground();
133        if ($oBackground instanceof Image) {
134            $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image', '../media/' . $oBackground->getIndexedFilename((string) $idxSlide));
135            $oBackground->relationId = 'rId' . $relId;
136            ++$relId;
137        }
138
139        // Write hyperlink relationships?
140        if (count($pSlide->getShapeCollection()) > 0) {
141            // Loop trough hyperlinks and write relationships
142            foreach ($pSlide->getShapeCollection() as $shape) {
143                // Hyperlink on shape
144                if ($shape->hasHyperlink()) {
145                    // Write relationship for hyperlink
146                    $hyperlink = $shape->getHyperlink();
147                    $hyperlink->relationId = 'rId' . $relId;
148
149                    if (!$hyperlink->isInternal()) {
150                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External');
151                    } else {
152                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml');
153                    }
154
155                    ++$relId;
156                }
157
158                // Hyperlink on rich text run
159                if ($shape instanceof RichText) {
160                    foreach ($shape->getParagraphs() as $paragraph) {
161                        foreach ($paragraph->getRichTextElements() as $element) {
162                            if ($element instanceof Run || $element instanceof TextElement) {
163                                if ($element->hasHyperlink()) {
164                                    // Write relationship for hyperlink
165                                    $hyperlink = $element->getHyperlink();
166                                    $hyperlink->relationId = 'rId' . $relId;
167
168                                    if (!$hyperlink->isInternal()) {
169                                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External');
170                                    } else {
171                                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml');
172                                    }
173
174                                    ++$relId;
175                                }
176                            }
177                        }
178                    }
179                }
180
181                // Hyperlink in table
182                if ($shape instanceof ShapeTable) {
183                    // Rows
184                    $countRows = count($shape->getRows());
185                    for ($row = 0; $row < $countRows; ++$row) {
186                        // Cells in rows
187                        $countCells = count($shape->getRow($row)->getCells());
188                        for ($cell = 0; $cell < $countCells; ++$cell) {
189                            $currentCell = $shape->getRow($row)->getCell($cell);
190                            // Paragraphs in cell
191                            foreach ($currentCell->getParagraphs() as $paragraph) {
192                                // RichText in paragraph
193                                foreach ($paragraph->getRichTextElements() as $element) {
194                                    // Run or Text in RichText
195                                    if ($element instanceof Run || $element instanceof TextElement) {
196                                        if ($element->hasHyperlink()) {
197                                            // Write relationship for hyperlink
198                                            $hyperlink = $element->getHyperlink();
199                                            $hyperlink->relationId = 'rId' . $relId;
200
201                                            if (!$hyperlink->isInternal()) {
202                                                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External');
203                                            } else {
204                                                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml');
205                                            }
206
207                                            ++$relId;
208                                        }
209                                    }
210                                }
211                            }
212                        }
213                    }
214                }
215
216                if ($shape instanceof Group) {
217                    foreach ($shape->getShapeCollection() as $subShape) {
218                        // Hyperlink on shape
219                        if ($subShape->hasHyperlink()) {
220                            // Write relationship for hyperlink
221                            $hyperlink = $subShape->getHyperlink();
222                            $hyperlink->relationId = 'rId' . $relId;
223
224                            if (!$hyperlink->isInternal()) {
225                                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External');
226                            } else {
227                                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml');
228                            }
229
230                            ++$relId;
231                        }
232
233                        // Hyperlink on rich text run
234                        if ($subShape instanceof RichText) {
235                            foreach ($subShape->getParagraphs() as $paragraph) {
236                                foreach ($paragraph->getRichTextElements() as $element) {
237                                    if ($element instanceof Run || $element instanceof TextElement) {
238                                        if ($element->hasHyperlink()) {
239                                            // Write relationship for hyperlink
240                                            $hyperlink = $element->getHyperlink();
241                                            $hyperlink->relationId = 'rId' . $relId;
242
243                                            if (!$hyperlink->isInternal()) {
244                                                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External');
245                                            } else {
246                                                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml');
247                                            }
248
249                                            ++$relId;
250                                        }
251                                    }
252                                }
253                            }
254                        }
255
256                        // Hyperlink in table
257                        if ($subShape instanceof ShapeTable) {
258                            // Rows
259                            $countRows = count($subShape->getRows());
260                            for ($row = 0; $row < $countRows; ++$row) {
261                                // Cells in rows
262                                $countCells = count($subShape->getRow($row)->getCells());
263                                for ($cell = 0; $cell < $countCells; ++$cell) {
264                                    $currentCell = $subShape->getRow($row)->getCell($cell);
265                                    // Paragraphs in cell
266                                    foreach ($currentCell->getParagraphs() as $paragraph) {
267                                        // RichText in paragraph
268                                        foreach ($paragraph->getRichTextElements() as $element) {
269                                            // Run or Text in RichText
270                                            if ($element instanceof Run || $element instanceof TextElement) {
271                                                if ($element->hasHyperlink()) {
272                                                    // Write relationship for hyperlink
273                                                    $hyperlink = $element->getHyperlink();
274                                                    $hyperlink->relationId = 'rId' . $relId;
275
276                                                    if (!$hyperlink->isInternal()) {
277                                                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink', $hyperlink->getUrl(), 'External');
278                                                    } else {
279                                                        $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/slide', 'slide' . $hyperlink->getSlideNumber() . '.xml');
280                                                    }
281
282                                                    ++$relId;
283                                                }
284                                            }
285                                        }
286                                    }
287                                }
288                            }
289                        }
290                    }
291                }
292            }
293        }
294
295        // Write comment relationships
296        if (count($pSlide->getShapeCollection()) > 0) {
297            $hasSlideComment = false;
298
299            // Loop trough images and write relationships
300            foreach ($pSlide->getShapeCollection() as $shape) {
301                if ($shape instanceof Comment) {
302                    $hasSlideComment = true;
303
304                    break;
305                } elseif ($shape instanceof Group) {
306                    foreach ($shape->getShapeCollection() as $subShape) {
307                        if ($subShape instanceof Comment) {
308                            $hasSlideComment = true;
309
310                            break 2;
311                        }
312                    }
313                }
314            }
315
316            if ($hasSlideComment) {
317                $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments', '../comments/comment' . ($idxSlide + 1) . '.xml');
318                ++$relId;
319            }
320        }
321
322        if (count($pSlide->getNote()->getShapeCollection()) > 0) {
323            $this->writeRelationship($objWriter, $relId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/notesSlide', '../notesSlides/notesSlide' . ($idxSlide + 1) . '.xml');
324        }
325
326        $objWriter->endElement();
327
328        // Return
329        return $objWriter->getData();
330    }
331
332    /**
333     * Write slide to XML format.
334     *
335     * @return string XML Output
336     */
337    protected function writeSlide(Slide $pSlide): string
338    {
339        // Create XML writer
340        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
341
342        // XML header
343        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
344
345        // p:sld
346        $objWriter->startElement('p:sld');
347        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
348        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
349        $objWriter->writeAttribute('xmlns:p', 'http://schemas.openxmlformats.org/presentationml/2006/main');
350        $objWriter->writeAttributeIf(!$pSlide->isVisible(), 'show', 0);
351
352        // p:sld/p:cSld
353        $objWriter->startElement('p:cSld');
354
355        // Background
356        if ($pSlide->getBackground() instanceof Slide\AbstractBackground) {
357            $oBackground = $pSlide->getBackground();
358            // p:bg
359            $objWriter->startElement('p:bg');
360
361            // p:bgPr
362            $objWriter->startElement('p:bgPr');
363
364            if ($oBackground instanceof Slide\Background\Color) {
365                // a:solidFill
366                $objWriter->startElement('a:solidFill');
367
368                $this->writeColor($objWriter, $oBackground->getColor());
369
370                // > a:solidFill
371                $objWriter->endElement();
372            }
373
374            if ($oBackground instanceof Image) {
375                // a:blipFill
376                $objWriter->startElement('a:blipFill');
377
378                // a:blip
379                $objWriter->startElement('a:blip');
380                $objWriter->writeAttribute('r:embed', $oBackground->relationId);
381
382                // > a:blipFill
383                $objWriter->endElement();
384
385                // a:stretch
386                $objWriter->startElement('a:stretch');
387
388                // a:fillRect
389                $objWriter->writeElement('a:fillRect');
390
391                // > a:stretch
392                $objWriter->endElement();
393
394                // > a:blipFill
395                $objWriter->endElement();
396            }
397
398            // > p:bgPr
399            $objWriter->endElement();
400
401            // > p:bg
402            $objWriter->endElement();
403        }
404
405        // p:spTree
406        $objWriter->startElement('p:spTree');
407
408        // p:nvGrpSpPr
409        $objWriter->startElement('p:nvGrpSpPr');
410
411        // p:cNvPr
412        $objWriter->startElement('p:cNvPr');
413        $objWriter->writeAttribute('id', '1');
414        $objWriter->writeAttribute('name', '');
415        $objWriter->endElement();
416
417        // p:cNvGrpSpPr
418        $objWriter->writeElement('p:cNvGrpSpPr', null);
419
420        // p:nvPr
421        $objWriter->writeElement('p:nvPr', null);
422
423        $objWriter->endElement();
424
425        // p:grpSpPr
426        $objWriter->startElement('p:grpSpPr');
427
428        // a:xfrm
429        $objWriter->startElement('a:xfrm');
430
431        // a:off
432        $objWriter->startElement('a:off');
433        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlide->getOffsetX()));
434        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlide->getOffsetY()));
435        $objWriter->endElement(); // a:off
436
437        // a:ext
438        $objWriter->startElement('a:ext');
439        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlide->getExtentX()));
440        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlide->getExtentY()));
441        $objWriter->endElement(); // a:ext
442
443        // a:chOff
444        $objWriter->startElement('a:chOff');
445        $objWriter->writeAttribute('x', CommonDrawing::pixelsToEmu($pSlide->getOffsetX()));
446        $objWriter->writeAttribute('y', CommonDrawing::pixelsToEmu($pSlide->getOffsetY()));
447        $objWriter->endElement(); // a:chOff
448
449        // a:chExt
450        $objWriter->startElement('a:chExt');
451        $objWriter->writeAttribute('cx', CommonDrawing::pixelsToEmu($pSlide->getExtentX()));
452        $objWriter->writeAttribute('cy', CommonDrawing::pixelsToEmu($pSlide->getExtentY()));
453        $objWriter->endElement(); // a:chExt
454
455        $objWriter->endElement();
456
457        $objWriter->endElement();
458
459        // Loop shapes
460        $this->writeShapeCollection($objWriter, $pSlide->getShapeCollection());
461
462        // TODO
463        $objWriter->endElement();
464
465        $objWriter->endElement();
466
467        // p:clrMapOvr
468        $objWriter->startElement('p:clrMapOvr');
469        // p:clrMapOvr\a:masterClrMapping
470        $objWriter->writeElement('a:masterClrMapping', null);
471        // ##p:clrMapOvr
472        $objWriter->endElement();
473
474        $this->writeSlideTransition($objWriter, $pSlide->getTransition());
475
476        $this->writeSlideAnimations($objWriter, $pSlide);
477
478        $objWriter->endElement();
479
480        // Return
481        return $objWriter->getData();
482    }
483
484    protected function writeSlideAnimations(XMLWriter $objWriter, Slide $oSlide): void
485    {
486        $arrayAnimations = $oSlide->getAnimations();
487        if (empty($arrayAnimations)) {
488            return;
489        }
490
491        // Variables
492        $shapeId = 1;
493        $idCount = 1;
494        $hashToIdMap = [];
495        $arrayAnimationIds = [];
496
497        foreach ($oSlide->getShapeCollection() as $shape) {
498            $hashToIdMap[$shape->getHashCode()] = ++$shapeId;
499        }
500        foreach ($arrayAnimations as $oAnimation) {
501            foreach ($oAnimation->getShapeCollection() as $oShape) {
502                $arrayAnimationIds[] = $hashToIdMap[$oShape->getHashCode()];
503            }
504        }
505
506        // p:timing
507        $objWriter->startElement('p:timing');
508        // p:timing/p:tnLst
509        $objWriter->startElement('p:tnLst');
510        // p:timing/p:tnLst/p:par
511        $objWriter->startElement('p:par');
512        // p:timing/p:tnLst/p:par/p:cTn
513        $objWriter->startElement('p:cTn');
514        $objWriter->writeAttribute('id', $idCount++);
515        $objWriter->writeAttribute('dur', 'indefinite');
516        $objWriter->writeAttribute('restart', 'never');
517        $objWriter->writeAttribute('nodeType', 'tmRoot');
518        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst
519        $objWriter->startElement('p:childTnLst');
520        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq
521        $objWriter->startElement('p:seq');
522        $objWriter->writeAttribute('concurrent', '1');
523        $objWriter->writeAttribute('nextAc', 'seek');
524        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn
525        $objWriter->startElement('p:cTn');
526        $objWriter->writeAttribute('id', $idCount++);
527        $objWriter->writeAttribute('dur', 'indefinite');
528        $objWriter->writeAttribute('nodeType', 'mainSeq');
529        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst
530        $objWriter->startElement('p:childTnLst');
531
532        // Each animation has multiple shapes
533        foreach ($arrayAnimations as $oAnimation) {
534            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par
535            $objWriter->startElement('p:par');
536            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn
537            $objWriter->startElement('p:cTn');
538            $objWriter->writeAttribute('id', $idCount++);
539            $objWriter->writeAttribute('fill', 'hold');
540            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst
541            $objWriter->startElement('p:stCondLst');
542            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst/p:cond
543            $objWriter->startElement('p:cond');
544            $objWriter->writeAttribute('delay', 'indefinite');
545            $objWriter->endElement();
546            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn\##p:stCondLst
547            $objWriter->endElement();
548            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst
549            $objWriter->startElement('p:childTnLst');
550            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par
551            $objWriter->startElement('p:par');
552            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn
553            $objWriter->startElement('p:cTn');
554            $objWriter->writeAttribute('id', $idCount++);
555            $objWriter->writeAttribute('fill', 'hold');
556            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst
557            $objWriter->startElement('p:stCondLst');
558            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:stCondLst/p:cond
559            $objWriter->startElement('p:cond');
560            $objWriter->writeAttribute('delay', '0');
561            $objWriter->endElement();
562            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn\##p:stCondLst
563            $objWriter->endElement();
564            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst
565            $objWriter->startElement('p:childTnLst');
566
567            $firstAnimation = true;
568            foreach ($oAnimation->getShapeCollection() as $oShape) {
569                $nodeType = $firstAnimation ? 'clickEffect' : 'withEffect';
570                $shapeId = $hashToIdMap[$oShape->getHashCode()];
571
572                // p:par
573                $objWriter->startElement('p:par');
574                // p:par/p:cTn
575                $objWriter->startElement('p:cTn');
576                $objWriter->writeAttribute('id', $idCount++);
577                $objWriter->writeAttribute('presetID', '1');
578                $objWriter->writeAttribute('presetClass', 'entr');
579                $objWriter->writeAttribute('fill', 'hold');
580                $objWriter->writeAttribute('presetSubtype', '0');
581                $objWriter->writeAttribute('grpId', '0');
582                $objWriter->writeAttribute('nodeType', $nodeType);
583                // p:par/p:cTn/p:stCondLst
584                $objWriter->startElement('p:stCondLst');
585                // p:par/p:cTn/p:stCondLst/p:cond
586                $objWriter->startElement('p:cond');
587                $objWriter->writeAttribute('delay', '0');
588                $objWriter->endElement();
589                // p:par/p:cTn\##p:stCondLst
590                $objWriter->endElement();
591                // p:par/p:cTn/p:childTnLst
592                $objWriter->startElement('p:childTnLst');
593                // p:par/p:cTn/p:childTnLst/p:set
594                $objWriter->startElement('p:set');
595                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr
596                $objWriter->startElement('p:cBhvr');
597                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn
598                $objWriter->startElement('p:cTn');
599                $objWriter->writeAttribute('id', $idCount++);
600                $objWriter->writeAttribute('dur', '1');
601                $objWriter->writeAttribute('fill', 'hold');
602                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn/p:stCondLst
603                $objWriter->startElement('p:stCondLst');
604                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn/p:stCondLst/p:cond
605                $objWriter->startElement('p:cond');
606                $objWriter->writeAttribute('delay', '0');
607                $objWriter->endElement();
608                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:cTn\##p:stCondLst
609                $objWriter->endElement();
610                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr\##p:cTn
611                $objWriter->endElement();
612                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:tgtEl
613                $objWriter->startElement('p:tgtEl');
614                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:tgtEl/p:spTgt
615                $objWriter->startElement('p:spTgt');
616                $objWriter->writeAttribute('spid', $shapeId);
617                $objWriter->endElement();
618                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr\##p:tgtEl
619                $objWriter->endElement();
620                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:attrNameLst
621                $objWriter->startElement('p:attrNameLst');
622                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr/p:attrNameLst/p:attrName
623                $objWriter->writeElement('p:attrName', 'style.visibility');
624                // p:par/p:cTn/p:childTnLst/p:set/p:cBhvr\##p:attrNameLst
625                $objWriter->endElement();
626                // p:par/p:cTn/p:childTnLst/p:set\##p:cBhvr
627                $objWriter->endElement();
628                // p:par/p:cTn/p:childTnLst/p:set/p:to
629                $objWriter->startElement('p:to');
630                // p:par/p:cTn/p:childTnLst/p:set/p:to/p:strVal
631                $objWriter->startElement('p:strVal');
632                $objWriter->writeAttribute('val', 'visible');
633                $objWriter->endElement();
634                // p:par/p:cTn/p:childTnLst/p:set\##p:to
635                $objWriter->endElement();
636                // p:par/p:cTn/p:childTnLst\##p:set
637                $objWriter->endElement();
638                // p:par/p:cTn\##p:childTnLst
639                $objWriter->endElement();
640                // p:par\##p:cTn
641                $objWriter->endElement();
642                // ##p:par
643                $objWriter->endElement();
644
645                $firstAnimation = false;
646            }
647
648            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par/p:cTn\##p:childTnLst
649            $objWriter->endElement();
650            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst/p:par\##p:cTn
651            $objWriter->endElement();
652            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn/p:childTnLst\##p:par
653            $objWriter->endElement();
654            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par/p:cTn\##p:childTnLst
655            $objWriter->endElement();
656            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst/p:par\##p:cTn
657            $objWriter->endElement();
658            // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn/p:childTnLst\##p:par
659            $objWriter->endElement();
660        }
661
662        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:cTn\##p:childTnLst
663        $objWriter->endElement();
664        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq\##p:cTn
665        $objWriter->endElement();
666        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst
667        $objWriter->startElement('p:prevCondLst');
668        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond
669        $objWriter->startElement('p:cond');
670        $objWriter->writeAttribute('evt', 'onPrev');
671        $objWriter->writeAttribute('delay', '0');
672        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond/p:tgtEl
673        $objWriter->startElement('p:tgtEl');
674        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond/p:tgtEl/p:sldTgt
675        $objWriter->writeElement('p:sldTgt', null);
676        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst/p:cond\##p:tgtEl
677        $objWriter->endElement();
678        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:prevCondLst\##p:cond
679        $objWriter->endElement();
680        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq\##p:prevCondLst
681        $objWriter->endElement();
682        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst
683        $objWriter->startElement('p:nextCondLst');
684        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond
685        $objWriter->startElement('p:cond');
686        $objWriter->writeAttribute('evt', 'onNext');
687        $objWriter->writeAttribute('delay', '0');
688        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond/p:tgtEl
689        $objWriter->startElement('p:tgtEl');
690        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond/p:tgtEl/p:sldTgt
691        $objWriter->writeElement('p:sldTgt', null);
692        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst/p:cond\##p:tgtEl
693        $objWriter->endElement();
694        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq/p:nextCondLst\##p:cond
695        $objWriter->endElement();
696        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst/p:seq\##p:nextCondLst
697        $objWriter->endElement();
698        // p:timing/p:tnLst/p:par/p:cTn/p:childTnLst\##p:seq
699        $objWriter->endElement();
700        // p:timing/p:tnLst/p:par/p:cTn\##p:childTnLst
701        $objWriter->endElement();
702        // p:timing/p:tnLst/p:par\##p:cTn
703        $objWriter->endElement();
704        // p:timing/p:tnLst\##p:par
705        $objWriter->endElement();
706        // p:timing\##p:tnLst
707        $objWriter->endElement();
708
709        // p:timing/p:bldLst
710        $objWriter->startElement('p:bldLst');
711
712        // Add in ids of all shapes in this slides animations
713        foreach ($arrayAnimationIds as $id) {
714            // p:timing/p:bldLst/p:bldP
715            $objWriter->startElement('p:bldP');
716            $objWriter->writeAttribute('spid', $id);
717            $objWriter->writeAttribute('grpId', 0);
718            $objWriter->endELement();
719        }
720
721        // p:timing\##p:bldLst
722        $objWriter->endElement();
723
724        // ##p:timing
725        $objWriter->endElement();
726    }
727}