Completed
Pull Request — develop (#208)
by Franck
16:00 queued 02:07
created

ODPresentation::readParagraphItem()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 20
cts 20
cp 1
rs 8.9297
c 0
b 0
f 0
cc 6
nc 10
nop 2
crap 6
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
 * @link        https://github.com/PHPOffice/PHPPresentation
14
 * @copyright   2009-2015 PHPPresentation contributors
15
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
16
 */
17
18
namespace PhpOffice\PhpPresentation\Reader;
19
20
use ZipArchive;
21
use PhpOffice\Common\XMLReader;
22
use PhpOffice\Common\Drawing as CommonDrawing;
23
use PhpOffice\PhpPresentation\PhpPresentation;
24
use PhpOffice\PhpPresentation\Shape\Drawing\Gd;
25
use PhpOffice\PhpPresentation\Shape\RichText;
26
use PhpOffice\PhpPresentation\Shape\RichText\Paragraph;
27
use PhpOffice\PhpPresentation\Slide\Background\Image;
28
use PhpOffice\PhpPresentation\Style\Bullet;
29
use PhpOffice\PhpPresentation\Style\Color;
30
use PhpOffice\PhpPresentation\Style\Fill;
31
use PhpOffice\PhpPresentation\Style\Font;
32
use PhpOffice\PhpPresentation\Style\Shadow;
33
use PhpOffice\PhpPresentation\Style\Alignment;
34
35
/**
36
 * Serialized format reader
37
 */
38
class ODPresentation extends AbstractReader implements ReaderInterface
39
{
40
    /**
41
     * Output Object
42
     * @var PhpPresentation
43
     */
44
    protected $oPhpPresentation;
45
46
    /**
47
     * Output Object
48
     * @var \ZipArchive
49
     */
50
    protected $oZip;
51
52
    /**
53
     * @var string
54
     */
55
    protected $filename;
56
57
    /**
58
     * @var array[]
59
     */
60
    protected $arrayStyles = array();
61
62
    /**
63
     * @var array[]
64
     */
65
    protected $arrayCommonStyles = array();
66
67
    /**
68
     * @var \PhpOffice\Common\XMLReader
69
     */
70
    protected $oXMLReader;
71
72
    /**
73
     * @var \PhpOffice\Common\XMLReader
74
     */
75
    protected $oXMLMetaInfManifest;
76
77
    /**
78
     * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
79
     *
80
     * @param  string $pFilename
81
     * @throws \Exception
82
     * @return boolean
83
     */
84 2
    public function canRead($pFilename)
85
    {
86 2
        return $this->fileSupportsUnserializePhpPresentation($pFilename);
87
    }
88
89
    /**
90
     * Does a file support UnserializePhpPresentation ?
91
     *
92
     * @param  string    $pFilename
93
     * @throws \Exception
94
     * @return boolean
95
     */
96 8
    public function fileSupportsUnserializePhpPresentation($pFilename = '')
97
    {
98
        // Check if file exists
99 8
        if (!file_exists($pFilename)) {
100 2
            throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
101
        }
102
        
103 6
        $oZip = new ZipArchive();
104
        // Is it a zip ?
105 6
        if ($oZip->open($pFilename) === true) {
106
            // Is it an OpenXML Document ?
107
            // Is it a Presentation ?
108 4
            if (is_array($oZip->statName('META-INF/manifest.xml')) && is_array($oZip->statName('mimetype')) && $oZip->getFromName('mimetype') == 'application/vnd.oasis.opendocument.presentation') {
109 4
                return true;
110
            }
111 1
        }
112 3
        return false;
113
    }
114
115
    /**
116
     * Loads PhpPresentation Serialized file
117
     *
118
     * @param  string        $pFilename
119
     * @return \PhpOffice\PhpPresentation\PhpPresentation
120
     * @throws \Exception
121
     */
122 5
    public function load($pFilename)
123
    {
124
        // Unserialize... First make sure the file supports it!
125 5
        if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
126 1
            throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\ODPresentation: " . $pFilename . ".");
127
        }
128
129 3
        return $this->loadFile($pFilename);
130
    }
131
132
    /**
133
     * Load PhpPresentation Serialized file
134
     *
135
     * @param  string $pFilename
136
     * @return \PhpOffice\PhpPresentation\PhpPresentation
137
     * @throws \Exception
138
     */
139 3
    protected function loadFile($pFilename)
140
    {
141 3
        $this->filename = $pFilename;
142
143 3
        $this->oPhpPresentation = new PhpPresentation();
144 3
        $this->oPhpPresentation->removeSlideByIndex();
145
        
146 3
        $this->oZip = new ZipArchive();
147 3
        $this->oZip->open($this->filename);
148
149 3
        if ($this->loadFileFromODP('meta.xml') !== false) {
150 3
            $this->loadDocumentProperties();
151 3
        }
152 3
        if ($this->loadFileFromODP('styles.xml') !== false) {
153 3
            $this->loadStylesFile();
154 3
        }
155 3
        if ($this->loadFileFromODP('content.xml') !== false) {
156 3
            $this->loadSlides();
157 3
        }
158
159 3
        return $this->oPhpPresentation;
160
    }
161
    
162
    /**
163
     * Read Document Properties
164
     */
165 3
    protected function loadDocumentProperties()
166
    {
167
        $arrayProperties = array(
168 3
            '/office:document-meta/office:meta/meta:initial-creator' => 'setCreator',
169 3
            '/office:document-meta/office:meta/dc:creator' => 'setLastModifiedBy',
170 3
            '/office:document-meta/office:meta/dc:title' => 'setTitle',
171 3
            '/office:document-meta/office:meta/dc:description' => 'setDescription',
172 3
            '/office:document-meta/office:meta/dc:subject' => 'setSubject',
173 3
            '/office:document-meta/office:meta/meta:keyword' => 'setKeywords',
174 3
            '/office:document-meta/office:meta/meta:creation-date' => 'setCreated',
175 3
            '/office:document-meta/office:meta/dc:date' => 'setModified',
176 3
        );
177 3
        $oProperties = $this->oPhpPresentation->getDocumentProperties();
178 3
        foreach ($arrayProperties as $path => $property) {
179 3
            $oElement = $this->oXMLReader->getElement($path);
180 3
            if ($oElement instanceof \DOMElement) {
181 3
                if (in_array($property, array('setCreated', 'setModified'))) {
182 3
                    $oDateTime = new \DateTime();
183 3
                    $oDateTime->createFromFormat(\DateTime::W3C, $oElement->nodeValue);
184 3
                    $oProperties->{$property}($oDateTime->getTimestamp());
185 3
                } else {
186 1
                    $oProperties->{$property}($oElement->nodeValue);
187
                }
188 3
            }
189 3
        }
190 3
    }
191
    
192
    /**
193
     * Extract all slides
194
     */
195 3
    protected function loadSlides()
196
    {
197 3
        foreach ($this->oXMLReader->getElements('/office:document-content/office:automatic-styles/*') as $oElement) {
198 3
            if ($oElement->hasAttribute('style:name')) {
199 3
                $this->loadStyle($oElement);
200 3
            }
201 3
        }
202 3
        foreach ($this->oXMLReader->getElements('/office:document-content/office:body/office:presentation/draw:page') as $oElement) {
203 3
            if ($oElement->nodeName == 'draw:page') {
204 3
                $this->loadSlide($oElement);
205 3
            }
206 3
        }
207 3
    }
208
209
    /**
210
     * Extract style
211
     * @param \DOMElement $nodeStyle
212
     * @return bool
213
     * @throws \Exception
214
     */
215 3
    protected function loadStyle(\DOMElement $nodeStyle)
216
    {
217 3
        $keyStyle = $nodeStyle->getAttribute('style:name');
218
219 3
        $nodeDrawingPageProps = $this->oXMLReader->getElement('style:drawing-page-properties', $nodeStyle);
220 3
        if ($nodeDrawingPageProps instanceof \DOMElement) {
221
            // Read Background Color
222 3
            if ($nodeDrawingPageProps->hasAttribute('draw:fill-color') && $nodeDrawingPageProps->getAttribute('draw:fill') == 'solid') {
223
                $oBackground = new \PhpOffice\PhpPresentation\Slide\Background\Color();
224
                $oColor = new Color();
225
                $oColor->setRGB(substr($nodeDrawingPageProps->getAttribute('draw:fill-color'), -6));
226
                $oBackground->setColor($oColor);
227
            }
228
            // Read Background Image
229 3
            if ($nodeDrawingPageProps->getAttribute('draw:fill') == 'bitmap' && $nodeDrawingPageProps->hasAttribute('draw:fill-image-name')) {
230
                $nameStyle = $nodeDrawingPageProps->getAttribute('draw:fill-image-name');
231
                if (!empty($this->arrayCommonStyles[$nameStyle]) && $this->arrayCommonStyles[$nameStyle]['type'] == 'image' && !empty($this->arrayCommonStyles[$nameStyle]['path'])) {
232
                    $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderODPBkg');
233
                    $contentImg = $this->oZip->getFromName($this->arrayCommonStyles[$nameStyle]['path']);
234
                    file_put_contents($tmpBkgImg, $contentImg);
235
236
                    $oBackground = new Image();
237
                    $oBackground->setPath($tmpBkgImg);
238
                }
239
            }
240 3
        }
241
242 3
        $nodeGraphicProps = $this->oXMLReader->getElement('style:graphic-properties', $nodeStyle);
243 3
        if ($nodeGraphicProps instanceof \DOMElement) {
244
            // Read Shadow
245 3
            if ($nodeGraphicProps->hasAttribute('draw:shadow') && $nodeGraphicProps->getAttribute('draw:shadow') == 'visible') {
246 1
                $oShadow = new Shadow();
247 1
                $oShadow->setVisible(true);
248 1
                if ($nodeGraphicProps->hasAttribute('draw:shadow-color')) {
249 1
                    $oShadow->getColor()->setRGB(substr($nodeGraphicProps->getAttribute('draw:shadow-color'), -6));
250 1
                }
251 1
                if ($nodeGraphicProps->hasAttribute('draw:shadow-opacity')) {
252 1
                    $oShadow->setAlpha(100 - (int)substr($nodeGraphicProps->getAttribute('draw:shadow-opacity'), 0, -1));
253 1
                }
254 1
                if ($nodeGraphicProps->hasAttribute('draw:shadow-offset-x') && $nodeGraphicProps->hasAttribute('draw:shadow-offset-y')) {
255 1
                    $offsetX = substr($nodeGraphicProps->getAttribute('draw:shadow-offset-x'), 0, -2);
256 1
                    $offsetY = substr($nodeGraphicProps->getAttribute('draw:shadow-offset-y'), 0, -2);
257 1
                    $distance = 0;
258 1
                    if ($offsetX != 0) {
259 1
                        $distance = ($offsetX < 0 ? $offsetX * -1 : $offsetX);
260 1
                    } elseif ($offsetY != 0) {
261
                        $distance = ($offsetY < 0 ? $offsetY * -1 : $offsetY);
262
                    }
263 1
                    $oShadow->setDirection(rad2deg(atan2($offsetY, $offsetX)));
264 1
                    $oShadow->setDistance(CommonDrawing::centimetersToPixels($distance));
265 1
                }
266 1
            }
267
            // Read Fill
268 3
            if ($nodeGraphicProps->hasAttribute('draw:fill')) {
269 1
                $value = $nodeGraphicProps->getAttribute('draw:fill');
270
271
                switch ($value) {
272 1
                    case 'none':
273 1
                        $oFill = new Fill();
274 1
                        $oFill->setFillType(Fill::FILL_NONE);
275 1
                        break;
276
                    case 'solid':
277
                        $oFill = new Fill();
278
                        $oFill->setFillType(Fill::FILL_SOLID);
279
                        if ($nodeGraphicProps->hasAttribute('draw:fill-color')) {
280
                            $oColor = new Color();
281
                            $oColor->setRGB(substr($nodeGraphicProps->getAttribute('draw:fill-color'), 1));
282
                            $oFill->setStartColor($oColor);
283
                        }
284
                        break;
285
                }
286 1
            }
287 3
        }
288
        
289 3
        $nodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $nodeStyle);
290 3
        if ($nodeTextProperties instanceof \DOMElement) {
291 1
            $oFont = new Font();
292 1
            if ($nodeTextProperties->hasAttribute('fo:color')) {
293 1
                $oFont->getColor()->setRGB(substr($nodeTextProperties->getAttribute('fo:color'), -6));
294 1
            }
295 1
            if ($nodeTextProperties->hasAttribute('fo:font-family')) {
296 1
                $oFont->setName($nodeTextProperties->getAttribute('fo:font-family'));
297 1
            }
298 1
            if ($nodeTextProperties->hasAttribute('fo:font-weight') && $nodeTextProperties->getAttribute('fo:font-weight') == 'bold') {
299 1
                $oFont->setBold(true);
300 1
            }
301 1
            if ($nodeTextProperties->hasAttribute('fo:font-size')) {
302 1
                $oFont->setSize(substr($nodeTextProperties->getAttribute('fo:font-size'), 0, -2));
303 1
            }
304 1
        }
305
306 3
        $nodeParagraphProps = $this->oXMLReader->getElement('style:paragraph-properties', $nodeStyle);
307 3
        if ($nodeParagraphProps instanceof \DOMElement) {
308 2
            $oAlignment = new Alignment();
309 2
            if ($nodeParagraphProps->hasAttribute('fo:text-align')) {
310 2
                $oAlignment->setHorizontal($nodeParagraphProps->getAttribute('fo:text-align'));
311 2
            }
312 2
        }
313
        
314 3
        if ($nodeStyle->nodeName == 'text:list-style') {
315 2
            $arrayListStyle = array();
316 2
            foreach ($this->oXMLReader->getElements('text:list-level-style-bullet', $nodeStyle) as $oNodeListLevel) {
317 2
                $oAlignment = new Alignment();
318 2
                $oBullet = new Bullet();
319 2
                $oBullet->setBulletType(Bullet::TYPE_NONE);
320 2
                if ($oNodeListLevel->hasAttribute('text:level')) {
321 2
                    $oAlignment->setLevel((int) $oNodeListLevel->getAttribute('text:level') - 1);
322 2
                }
323 2
                if ($oNodeListLevel->hasAttribute('text:bullet-char')) {
324 2
                    $oBullet->setBulletChar($oNodeListLevel->getAttribute('text:bullet-char'));
325 2
                    $oBullet->setBulletType(Bullet::TYPE_BULLET);
326 2
                }
327
                
328 2
                $oNodeListProperties = $this->oXMLReader->getElement('style:list-level-properties', $oNodeListLevel);
329 2
                if ($oNodeListProperties instanceof \DOMElement) {
330 2
                    if ($oNodeListProperties->hasAttribute('text:min-label-width')) {
331 2
                        $oAlignment->setIndent((int)round(CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:min-label-width'), 0, -2))));
332 2
                    }
333 2
                    if ($oNodeListProperties->hasAttribute('text:space-before')) {
334 2
                        $iSpaceBefore = CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:space-before'), 0, -2));
335 2
                        $iMarginLeft = $iSpaceBefore + $oAlignment->getIndent();
336 2
                        $oAlignment->setMarginLeft($iMarginLeft);
337 2
                    }
338 2
                }
339 2
                $oNodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $oNodeListLevel);
340 2
                if ($oNodeTextProperties instanceof \DOMElement) {
341 2
                    if ($oNodeTextProperties->hasAttribute('fo:font-family')) {
342 2
                        $oBullet->setBulletFont($oNodeTextProperties->getAttribute('fo:font-family'));
343 2
                    }
344 2
                }
345
                
346 2
                $arrayListStyle[$oAlignment->getLevel()] = array(
347 2
                    'alignment' => $oAlignment,
348 2
                    'bullet' => $oBullet,
349
                );
350 2
            }
351 2
        }
352
        
353 3
        $this->arrayStyles[$keyStyle] = array(
354 3
            'alignment' => isset($oAlignment) ? $oAlignment : null,
355 3
            'background' => isset($oBackground) ? $oBackground : null,
356 3
            'fill' => isset($oFill) ? $oFill : null,
357 3
            'font' => isset($oFont) ? $oFont : null,
358 3
            'shadow' => isset($oShadow) ? $oShadow : null,
359 3
            'listStyle' => isset($arrayListStyle) ? $arrayListStyle : null,
360
        );
361
        
362 3
        return true;
363
    }
364
365
    /**
366
     * Read Slide
367
     *
368
     * @param \DOMElement $nodeSlide
369
     * @return bool
370
     * @throws \Exception
371
     */
372 3
    protected function loadSlide(\DOMElement $nodeSlide)
373
    {
374
        // Core
375 3
        $this->oPhpPresentation->createSlide();
376 3
        $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);
377 3
        if ($nodeSlide->hasAttribute('draw:name')) {
378 3
            $this->oPhpPresentation->getActiveSlide()->setName($nodeSlide->getAttribute('draw:name'));
379 3
        }
380 3
        if ($nodeSlide->hasAttribute('draw:style-name')) {
381 3
            $keyStyle = $nodeSlide->getAttribute('draw:style-name');
382 3
            if (isset($this->arrayStyles[$keyStyle])) {
383 3
                $this->oPhpPresentation->getActiveSlide()->setBackground($this->arrayStyles[$keyStyle]['background']);
384 3
            }
385 3
        }
386 3
        foreach ($this->oXMLReader->getElements('draw:frame', $nodeSlide) as $oNodeFrame) {
387 3
            if ($this->oXMLReader->getElement('draw:image', $oNodeFrame)) {
388 2
                $this->loadShapeDrawing($oNodeFrame);
389 2
                continue;
390
            }
391 3
            if ($this->oXMLReader->getElement('draw:text-box', $oNodeFrame)) {
392 3
                $this->loadShapeRichText($oNodeFrame);
393 3
                continue;
394
            }
395 3
        }
396 3
        return true;
397
    }
398
399
    /**
400
     * Read Shape Drawing
401
     *
402
     * @param \DOMElement $oNodeFrame
403
     * @throws \Exception
404
     */
405 2
    protected function loadShapeDrawing(\DOMElement $oNodeFrame)
406
    {
407
        // Core
408 2
        $oShape = new Gd();
409 2
        $oShape->getShadow()->setVisible(false);
410
411 2
        $oNodeImage = $this->oXMLReader->getElement('draw:image', $oNodeFrame);
412 2
        if ($oNodeImage instanceof \DOMElement) {
413 2
            if ($oNodeImage->hasAttribute('xlink:href')) {
414 2
                $sFilename = $oNodeImage->getAttribute('xlink:href');
415
                // svm = StarView Metafile
416 2
                if (pathinfo($sFilename, PATHINFO_EXTENSION) == 'svm') {
417 1
                    return;
418
                }
419 2
                $imageFile = $this->oZip->getFromName($sFilename);
420 2
                if (!empty($imageFile)) {
421 2
                    $oShape->setImageResource(imagecreatefromstring($imageFile));
422 2
                }
423 2
            }
424 2
        }
425
        
426 2
        $oShape->setName($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
427 2
        $oShape->setDescription($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
428 2
        $oShape->setResizeProportional(false);
429 2
        $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
430 2
        $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
431 2
        $oShape->setResizeProportional(true);
432 2
        $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
433 2
        $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
434
        
435 2
        if ($oNodeFrame->hasAttribute('draw:style-name')) {
436 2
            $keyStyle = $oNodeFrame->getAttribute('draw:style-name');
437 2
            if (isset($this->arrayStyles[$keyStyle])) {
438 2
                $oShape->setShadow($this->arrayStyles[$keyStyle]['shadow']);
439 2
                $oShape->setFill($this->arrayStyles[$keyStyle]['fill']);
440 2
            }
441 2
        }
442
        
443 2
        $this->oPhpPresentation->getActiveSlide()->addShape($oShape);
444 2
    }
445
446
    /**
447
     * Read Shape RichText
448
     *
449
     * @param \DOMElement $oNodeFrame
450
     * @throws \Exception
451
     */
452 3
    protected function loadShapeRichText(\DOMElement $oNodeFrame)
453
    {
454
        // Core
455 3
        $oShape = $this->oPhpPresentation->getActiveSlide()->createRichTextShape();
456 3
        $oShape->setParagraphs(array());
457
        
458 3
        $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
459 3
        $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
460 3
        $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
461 3
        $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
462
        
463 3
        foreach ($this->oXMLReader->getElements('draw:text-box/*', $oNodeFrame) as $oNodeParagraph) {
464 2
            $this->levelParagraph = 0;
465 2
            if ($oNodeParagraph->nodeName == 'text:p') {
466 2
                $this->readParagraph($oShape, $oNodeParagraph);
467 2
            }
468 2
            if ($oNodeParagraph->nodeName == 'text:list') {
469 1
                $this->readList($oShape, $oNodeParagraph);
470 1
            }
471 3
        }
472
        
473 3
        if (count($oShape->getParagraphs()) > 0) {
474 2
            $oShape->setActiveParagraph(0);
475 2
        }
476 3
    }
477
    
478
    protected $levelParagraph = 0;
479
480
    /**
481
     * Read Paragraph
482
     * @param RichText $oShape
483
     * @param \DOMElement $oNodeParent
484
     * @throws \Exception
485
     */
486 2
    protected function readParagraph(RichText $oShape, \DOMElement $oNodeParent)
487
    {
488 2
        $oParagraph = $oShape->createParagraph();
489 2
        $oDomList = $this->oXMLReader->getElements('text:span', $oNodeParent);
490 2
        $oDomTextNodes = $this->oXMLReader->getElements('text()', $oNodeParent);
491 2
        foreach ($oDomTextNodes as $oDomTextNode) {
492 2
            if (trim($oDomTextNode->nodeValue) != '') {
493 1
                $oTextRun = $oParagraph->createTextRun();
494 1
                $oTextRun->setText(trim($oDomTextNode->nodeValue));
495 1
            }
496 2
        }
497 2
        foreach ($oDomList as $oNodeRichTextElement) {
498 1
            $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
499 2
        }
500 2
    }
501
502
    /**
503
     * Read Paragraph Item
504
     * @param Paragraph $oParagraph
505
     * @param \DOMElement $oNodeParent
506
     * @throws \Exception
507
     */
508 1
    protected function readParagraphItem(Paragraph $oParagraph, \DOMElement $oNodeParent)
509
    {
510 1
        if ($this->oXMLReader->elementExists('text:line-break', $oNodeParent)) {
511 1
            $oParagraph->createBreak();
512 1
        } else {
513 1
            $oTextRun = $oParagraph->createTextRun();
514 1
            if ($oNodeParent->hasAttribute('text:style-name')) {
515 1
                $keyStyle = $oNodeParent->getAttribute('text:style-name');
516 1
                if (isset($this->arrayStyles[$keyStyle])) {
517 1
                    $oTextRun->setFont($this->arrayStyles[$keyStyle]['font']);
518 1
                }
519 1
            }
520 1
            $oTextRunLink = $this->oXMLReader->getElement('text:a', $oNodeParent);
521 1
            if ($oTextRunLink instanceof \DOMElement) {
522 1
                $oTextRun->setText($oTextRunLink->nodeValue);
523 1
                if ($oTextRunLink->hasAttribute('xlink:href')) {
524 1
                    $oTextRun->getHyperlink()->setUrl($oTextRunLink->getAttribute('xlink:href'));
525 1
                }
526 1
            } else {
527 1
                $oTextRun->setText($oNodeParent->nodeValue);
528
            }
529
        }
530 1
    }
531
532
    /**
533
     * Read List
534
     *
535
     * @param RichText $oShape
536
     * @param \DOMElement $oNodeParent
537
     * @throws \Exception
538
     */
539 1
    protected function readList(RichText $oShape, \DOMElement $oNodeParent)
540
    {
541 1
        foreach ($this->oXMLReader->getElements('text:list-item/*', $oNodeParent) as $oNodeListItem) {
542 1
            if ($oNodeListItem->nodeName == 'text:p') {
543 1
                $this->readListItem($oShape, $oNodeListItem, $oNodeParent);
544 1
            }
545 1
            if ($oNodeListItem->nodeName == 'text:list') {
546 1
                $this->levelParagraph++;
547 1
                $this->readList($oShape, $oNodeListItem);
548 1
                $this->levelParagraph--;
549 1
            }
550 1
        }
551 1
    }
552
553
    /**
554
     * Read List Item
555
     * @param RichText $oShape
556
     * @param \DOMElement $oNodeParent
557
     * @param \DOMElement $oNodeParagraph
558
     * @throws \Exception
559
     */
560 1
    protected function readListItem(RichText $oShape, \DOMElement $oNodeParent, \DOMElement $oNodeParagraph)
561
    {
562 1
        $oParagraph = $oShape->createParagraph();
563 1
        if ($oNodeParagraph->hasAttribute('text:style-name')) {
564 1
            $keyStyle = $oNodeParagraph->getAttribute('text:style-name');
565 1
            if (isset($this->arrayStyles[$keyStyle]) && !empty($this->arrayStyles[$keyStyle]['listStyle'])) {
566 1
                $oParagraph->setAlignment($this->arrayStyles[$keyStyle]['listStyle'][$this->levelParagraph]['alignment']);
567 1
                $oParagraph->setBulletStyle($this->arrayStyles[$keyStyle]['listStyle'][$this->levelParagraph]['bullet']);
568 1
            }
569 1
        }
570 1
        foreach ($this->oXMLReader->getElements('text:span', $oNodeParent) as $oNodeRichTextElement) {
571 1
            $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
572 1
        }
573 1
    }
574
575
    /**
576
     * Load file 'styles.xml'
577
     */
578 3
    protected function loadStylesFile()
579
    {
580 3
        foreach ($this->oXMLReader->getElements('/office:document-styles/office:styles/*') as $oElement) {
581 3
            if ($oElement->nodeName == 'draw:fill-image') {
582
                $this->arrayCommonStyles[$oElement->getAttribute('draw:name')] = array(
583
                    'type' => 'image',
584
                    'path' => $oElement->hasAttribute('xlink:href') ? $oElement->getAttribute('xlink:href') : null
585
                );
586
            }
587 3
        }
588 3
    }
589
590
    /**
591
     * @param string $filename
592
     * @return bool
593
     * @throws \Exception
594
     */
595 3
    protected function loadFileFromODP($filename)
596
    {
597 3
        $bEncrypted = false;
598
599 3
        if (!$this->oXMLMetaInfManifest) {
600 3
            $this->oXMLMetaInfManifest = new XMLReader();
601 3
            if ($this->oXMLMetaInfManifest->getDomFromZip($this->filename, 'META-INF/manifest.xml') === false) {
602
                return false;
603
            }
604 3
        }
605
        // Search file in META-INF/manifest.xml
606 3
        $oElement = $this->oXMLMetaInfManifest->getElement('/manifest:manifest/manifest:file-entry[@manifest:full-path=\''.$filename.'\']');
607 3
        if (!$oElement) {
608
            return false;
609
        }
610
        // Has it some manifest:encryption-data ?
611 3
        $oElementEncryption = $this->oXMLMetaInfManifest->getElement('manifest:encryption-data', $oElement);
0 ignored issues
show
Documentation introduced by
$oElement is of type object<DOMNode>, but the function expects a null|object<DOMElement>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
612 3
        if ($oElementEncryption) {
613
            $bEncrypted = true;
614
        }
615
616 3
        $fileContent = $this->oZip->getFromName($filename);
617 3
        if (!$fileContent){
618
            return false;
619
        }
620
621
        // No Encrypted file
622 3
        if (!$bEncrypted) {
623 3
            $this->oXMLReader = new XMLReader();
624 3
            $this->oXMLReader->getDomFromString($fileContent);
625 3
            return true;
626
        }
627
628
        //return false;
629
        /*
630
          <manifest:encryption-data
631
                manifest:checksum-type="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha256-1k"
632
                manifest:checksum="BfB+taOY0kcVO/9WNi4DfqioRp3LMwVoNbqfAQ37yac=">
633
              <manifest:algorithm
634
                    manifest:algorithm-name="http://www.w3.org/2001/04/xmlenc#aes256-cbc"
635
                    manifest:initialisation-vector="I7rMXmvuynJFxJtm+EQ5qA=="/>
636
              <manifest:key-derivation
637
                    manifest:key-derivation-name="PBKDF2"
638
                    manifest:key-size="32"
639
                    manifest:iteration-count="1024"
640
                    manifest:salt="Mows9XX/YiNKNJ0qll3jgA=="/>
641
              <manifest:start-key-generation
642
                    manifest:start-key-generation-name="http://www.w3.org/2000/09/xmldsig#sha256"
643
                    manifest:key-size="32"/>
644
          </manifest:encryption-data>
645
         </manifest:file-entry>
646
         */
647
        return false;
648
        // Encrypted file
649
        $checksum = $oElementEncryption->getAttribute('manifest:checksum');
0 ignored issues
show
Unused Code introduced by
$checksum = $oElementEnc...e('manifest:checksum'); does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
Unused Code introduced by
$checksum is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
650
651
        $oEltKeyDerivation = $this->oXMLMetaInfManifest->getElement('manifest:key-derivation', $oElementEncryption);
652
        $salt = $oEltKeyDerivation->getAttribute('manifest:salt');
653
        //$salt = base64_decode($salt);
654
        echo 'manifest:salt : ';
655
        var_dump($salt);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($salt); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
656
        $iterationCount = $oEltKeyDerivation->getAttribute('manifest:iteration-count');
657
        echo 'manifest:iteration-count : ';
658
        var_dump($iterationCount);
659
        $keySize = $oEltKeyDerivation->getAttribute('manifest:key-size');
660
        echo 'manifest:key-size : ';
661
        var_dump($keySize);
662
663
        $oEltAlgorithm = $this->oXMLMetaInfManifest->getElement('manifest:algorithm', $oElementEncryption);
664
        $iv = $oEltAlgorithm->getAttribute('manifest:initialisation-vector');
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $iv. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
665
        $iv = base64_decode($iv);
666
        echo 'manifest:initialisation-vector : ';
667
        var_dump($iv);
668
669
        $pwdHash = hash('sha256', $this->getPassword());
670
        echo 'sha256('.$this->getPassword().'): ';
671
        var_dump($pwdHash);
672
        //$pwdHash = substr($pwdHash, 0 , 32);
673
        //var_dump($pwdHash);
674
675
        $key = hash_pbkdf2('sha1', $pwdHash, $salt, $iterationCount, $keySize, true);
676
        echo 'hash_pbkdf2 (sha1, hash, salt, iterationCount, $iterationCount) : ';
677
        var_dump($key);
678
        //$key = $this->hash_pbkdf2('sha1', $pwdHash, $salt, $iterationCount, $keySize, true);
679
        //echo 'hash_pbkdf2 (sha1, hash, salt, iterationCount, $iterationCount) : ';
680
        //var_dump($key);
681
682
        $data = openssl_decrypt($fileContent, 'AES-256-CBC', $key, 0, $iv);
683
        if(!$data) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
684
            while ($msg = openssl_error_string())
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
Coding Style introduced by
Expected 0 spaces before semicolon; newline found
Loading history...
685
                var_dump($msg);
686
            die();
0 ignored issues
show
Coding Style Compatibility introduced by
The method loadFileFromODP() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
687
        } else {
688
            var_dump($data);
689
            $data = gzinflate($data);
690
            var_dump($data);
691
        }
692
693
        /*$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $fileContent, MCRYPT_MODE_CBC, $iv);
694
        var_dump($data);
695
        $data = gzinflate($data);
696
        if($data) {
697
            var_dump($data);
698
        }*/
699
700
        return false;
701
    }
702
703
    protected function hash_pbkdf2($a = 'sha256', $password, $salt, $rounds = 5000, $key_length = 32, $raw_output = false)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $a. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Coding Style Naming introduced by
The method hash_pbkdf2 is not named in camelCase.

This check marks method names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $key_length is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $raw_output is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The variable $key_length is not named in camelCase.

This check marks variable names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The variable $raw_output is not named in camelCase.

This check marks variable names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
Method name "ODPresentation::hash_pbkdf2" is not in camel caps format
Loading history...
Coding Style introduced by
Parameters which have default values should be placed at the end.

If you place a parameter with a default value before a parameter with a default value, the default value of the first parameter will never be used as it will always need to be passed anyway:

// $a must always be passed; it's default value is never used.
function someFunction($a = 5, $b) { }
Loading history...
704
    {
705
        // Derived key
706
        $dk = '';
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $dk. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
707
        // Create key
708
        for ($block=1; $block<=$key_length; $block++)
709
        {
710
            // Initial hash for this block
711
            $ib = $h = hash_hmac($a, $salt . pack('N', $block), $password, true);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $ib. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $h. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
712
            // Perform block iterations
713
            for ($i=1; $i<$rounds; $i++)
714
            {
715
                // XOR each iteration
716
                $ib ^= ($h = hash_hmac($a, $h, $password, true));
717
            }
718
            // Append iterated block
719
            $dk .= $ib;
720
        }
721
        // Return derived key of correct length
722
        $key = substr($dk, 0, $key_length);
723
        return $raw_output ? $key : base64_encode($key);
724
    }
725
726
}
727