Completed
Pull Request — develop (#208)
by Franck
09:10
created

ODPresentation::readListItem()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 14
ccs 10
cts 10
cp 1
rs 8.8571
cc 5
eloc 9
nc 6
nop 3
crap 5
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
        }
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
     */
138 3
    protected function loadFile($pFilename)
139
    {
140 3
        $this->filename = $pFilename;
141
142 3
        $this->oPhpPresentation = new PhpPresentation();
143 3
        $this->oPhpPresentation->removeSlideByIndex();
144
        
145 3
        $this->oZip = new ZipArchive();
146 3
        $this->oZip->open($this->filename);
147
148 3
        if ($this->loadFileFromODP('meta.xml') !== false) {
149 3
            $this->loadDocumentProperties();
150
        }
151 3
        if ($this->loadFileFromODP('styles.xml') !== false) {
152 3
            $this->loadStylesFile();
153
        }
154 3
        if ($this->loadFileFromODP('content.xml') !== false) {
155 3
            $this->loadSlides();
156
        }
157
158 3
        return $this->oPhpPresentation;
159
    }
160
    
161
    /**
162
     * Read Document Properties
163
     */
164 3
    protected function loadDocumentProperties()
165
    {
166
        $arrayProperties = array(
167 3
            '/office:document-meta/office:meta/meta:initial-creator' => 'setCreator',
168
            '/office:document-meta/office:meta/dc:creator' => 'setLastModifiedBy',
169
            '/office:document-meta/office:meta/dc:title' => 'setTitle',
170
            '/office:document-meta/office:meta/dc:description' => 'setDescription',
171
            '/office:document-meta/office:meta/dc:subject' => 'setSubject',
172
            '/office:document-meta/office:meta/meta:keyword' => 'setKeywords',
173
            '/office:document-meta/office:meta/meta:creation-date' => 'setCreated',
174
            '/office:document-meta/office:meta/dc:date' => 'setModified',
175
        );
176 3
        $oProperties = $this->oPhpPresentation->getDocumentProperties();
177 3
        foreach ($arrayProperties as $path => $property) {
178 3
            $oElement = $this->oXMLReader->getElement($path);
179 3
            if ($oElement instanceof \DOMElement) {
180 3
                if (in_array($property, array('setCreated', 'setModified'))) {
181 3
                    $oDateTime = new \DateTime();
182 3
                    $oDateTime->createFromFormat(\DateTime::W3C, $oElement->nodeValue);
183 3
                    $oProperties->{$property}($oDateTime->getTimestamp());
184
                } else {
185 3
                    $oProperties->{$property}($oElement->nodeValue);
186
                }
187
            }
188
        }
189 3
    }
190
    
191
    /**
192
     * Extract all slides
193
     */
194 3
    protected function loadSlides()
195
    {
196 3
        foreach ($this->oXMLReader->getElements('/office:document-content/office:automatic-styles/*') as $oElement) {
197 3
            if ($oElement->hasAttribute('style:name')) {
198 3
                $this->loadStyle($oElement);
199
            }
200
        }
201 3
        foreach ($this->oXMLReader->getElements('/office:document-content/office:body/office:presentation/draw:page') as $oElement) {
202 3
            if ($oElement->nodeName == 'draw:page') {
203 3
                $this->loadSlide($oElement);
204
            }
205
        }
206 3
    }
207
    
208
    /**
209
     * Extract style
210
     * @param \DOMElement $nodeStyle
211
     */
212 3
    protected function loadStyle(\DOMElement $nodeStyle)
213
    {
214 3
        $keyStyle = $nodeStyle->getAttribute('style:name');
215
216 3
        $nodeDrawingPageProps = $this->oXMLReader->getElement('style:drawing-page-properties', $nodeStyle);
217 3
        if ($nodeDrawingPageProps instanceof \DOMElement) {
218
            // Read Background Color
219 3
            if ($nodeDrawingPageProps->hasAttribute('draw:fill-color') && $nodeDrawingPageProps->getAttribute('draw:fill') == 'solid') {
220
                $oBackground = new \PhpOffice\PhpPresentation\Slide\Background\Color();
221
                $oColor = new Color();
222
                $oColor->setRGB(substr($nodeDrawingPageProps->getAttribute('draw:fill-color'), -6));
223
                $oBackground->setColor($oColor);
224
            }
225
            // Read Background Image
226 3
            if ($nodeDrawingPageProps->getAttribute('draw:fill') == 'bitmap' && $nodeDrawingPageProps->hasAttribute('draw:fill-image-name')) {
227
                $nameStyle = $nodeDrawingPageProps->getAttribute('draw:fill-image-name');
228
                if (!empty($this->arrayCommonStyles[$nameStyle]) && $this->arrayCommonStyles[$nameStyle]['type'] == 'image' && !empty($this->arrayCommonStyles[$nameStyle]['path'])) {
229
                    $tmpBkgImg = tempnam(sys_get_temp_dir(), 'PhpPresentationReaderODPBkg');
230
                    $contentImg = $this->oZip->getFromName($this->arrayCommonStyles[$nameStyle]['path']);
231
                    file_put_contents($tmpBkgImg, $contentImg);
232
233
                    $oBackground = new Image();
234
                    $oBackground->setPath($tmpBkgImg);
235
                }
236
            }
237
        }
238
239 3
        $nodeGraphicProps = $this->oXMLReader->getElement('style:graphic-properties', $nodeStyle);
240 3
        if ($nodeGraphicProps instanceof \DOMElement) {
241
            // Read Shadow
242 3
            if ($nodeGraphicProps->hasAttribute('draw:shadow') && $nodeGraphicProps->getAttribute('draw:shadow') == 'visible') {
243 1
                $oShadow = new Shadow();
244 1
                $oShadow->setVisible(true);
245 1
                if ($nodeGraphicProps->hasAttribute('draw:shadow-color')) {
246 1
                    $oShadow->getColor()->setRGB(substr($nodeGraphicProps->getAttribute('draw:shadow-color'), -6));
247
                }
248 1
                if ($nodeGraphicProps->hasAttribute('draw:shadow-opacity')) {
249 1
                    $oShadow->setAlpha(100 - (int)substr($nodeGraphicProps->getAttribute('draw:shadow-opacity'), 0, -1));
250
                }
251 1
                if ($nodeGraphicProps->hasAttribute('draw:shadow-offset-x') && $nodeGraphicProps->hasAttribute('draw:shadow-offset-y')) {
252 1
                    $offsetX = substr($nodeGraphicProps->getAttribute('draw:shadow-offset-x'), 0, -2);
253 1
                    $offsetY = substr($nodeGraphicProps->getAttribute('draw:shadow-offset-y'), 0, -2);
254 1
                    $distance = 0;
255 1
                    if ($offsetX != 0) {
256 1
                        $distance = ($offsetX < 0 ? $offsetX * -1 : $offsetX);
257
                    } elseif ($offsetY != 0) {
258
                        $distance = ($offsetY < 0 ? $offsetY * -1 : $offsetY);
259
                    }
260 1
                    $oShadow->setDirection(rad2deg(atan2($offsetY, $offsetX)));
261 1
                    $oShadow->setDistance(CommonDrawing::centimetersToPixels($distance));
262
                }
263
            }
264
            // Read Fill
265 3
            if ($nodeGraphicProps->hasAttribute('draw:fill')) {
266 1
                $value = $nodeGraphicProps->getAttribute('draw:fill');
267
268
                switch ($value) {
269 1
                    case 'none':
270 1
                        $oFill = new Fill();
271 1
                        $oFill->setFillType(Fill::FILL_NONE);
272 1
                        break;
273
                    case 'solid':
274
                        $oFill = new Fill();
275
                        $oFill->setFillType(Fill::FILL_SOLID);
276
                        if ($nodeGraphicProps->hasAttribute('draw:fill-color')) {
277
                            $oColor = new Color();
278
                            $oColor->setRGB(substr($nodeGraphicProps->getAttribute('draw:fill-color'), 1));
279
                            $oFill->setStartColor($oColor);
280
                        }
281
                        break;
282
                }
283
            }
284
        }
285
        
286 3
        $nodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $nodeStyle);
287 3
        if ($nodeTextProperties instanceof \DOMElement) {
288 1
            $oFont = new Font();
289 1
            if ($nodeTextProperties->hasAttribute('fo:color')) {
290 1
                $oFont->getColor()->setRGB(substr($nodeTextProperties->getAttribute('fo:color'), -6));
291
            }
292 1
            if ($nodeTextProperties->hasAttribute('fo:font-family')) {
293 1
                $oFont->setName($nodeTextProperties->getAttribute('fo:font-family'));
294
            }
295 1
            if ($nodeTextProperties->hasAttribute('fo:font-weight') && $nodeTextProperties->getAttribute('fo:font-weight') == 'bold') {
296 1
                $oFont->setBold(true);
297
            }
298 1
            if ($nodeTextProperties->hasAttribute('fo:font-size')) {
299 1
                $oFont->setSize(substr($nodeTextProperties->getAttribute('fo:font-size'), 0, -2));
300
            }
301
        }
302
303 3
        $nodeParagraphProps = $this->oXMLReader->getElement('style:paragraph-properties', $nodeStyle);
304 3
        if ($nodeParagraphProps instanceof \DOMElement) {
305 2
            $oAlignment = new Alignment();
306 2
            if ($nodeParagraphProps->hasAttribute('fo:text-align')) {
307 2
                $oAlignment->setHorizontal($nodeParagraphProps->getAttribute('fo:text-align'));
308
            }
309
        }
310
        
311 3
        if ($nodeStyle->nodeName == 'text:list-style') {
312 2
            $arrayListStyle = array();
313 2
            foreach ($this->oXMLReader->getElements('text:list-level-style-bullet', $nodeStyle) as $oNodeListLevel) {
314 2
                $oAlignment = new Alignment();
315 2
                $oBullet = new Bullet();
316 2
                $oBullet->setBulletType(Bullet::TYPE_NONE);
317 2
                if ($oNodeListLevel->hasAttribute('text:level')) {
318 2
                    $oAlignment->setLevel((int) $oNodeListLevel->getAttribute('text:level') - 1);
319
                }
320 2
                if ($oNodeListLevel->hasAttribute('text:bullet-char')) {
321 2
                    $oBullet->setBulletChar($oNodeListLevel->getAttribute('text:bullet-char'));
322 2
                    $oBullet->setBulletType(Bullet::TYPE_BULLET);
323
                }
324
                
325 2
                $oNodeListProperties = $this->oXMLReader->getElement('style:list-level-properties', $oNodeListLevel);
326 2
                if ($oNodeListProperties instanceof \DOMElement) {
327 2
                    if ($oNodeListProperties->hasAttribute('text:min-label-width')) {
328 2
                        $oAlignment->setIndent((int)round(CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:min-label-width'), 0, -2))));
329
                    }
330 2
                    if ($oNodeListProperties->hasAttribute('text:space-before')) {
331 2
                        $iSpaceBefore = CommonDrawing::centimetersToPixels(substr($oNodeListProperties->getAttribute('text:space-before'), 0, -2));
332 2
                        $iMarginLeft = $iSpaceBefore + $oAlignment->getIndent();
333 2
                        $oAlignment->setMarginLeft($iMarginLeft);
334
                    }
335
                }
336 2
                $oNodeTextProperties = $this->oXMLReader->getElement('style:text-properties', $oNodeListLevel);
337 2
                if ($oNodeTextProperties instanceof \DOMElement) {
338 2
                    if ($oNodeTextProperties->hasAttribute('fo:font-family')) {
339 2
                        $oBullet->setBulletFont($oNodeTextProperties->getAttribute('fo:font-family'));
340
                    }
341
                }
342
                
343 2
                $arrayListStyle[$oAlignment->getLevel()] = array(
344 2
                    'alignment' => $oAlignment,
345 2
                    'bullet' => $oBullet,
346
                );
347
            }
348
        }
349
        
350 3
        $this->arrayStyles[$keyStyle] = array(
351 3
            'alignment' => isset($oAlignment) ? $oAlignment : null,
352
            'background' => isset($oBackground) ? $oBackground : null,
353 1
            'fill' => isset($oFill) ? $oFill : null,
354 1
            'font' => isset($oFont) ? $oFont : null,
355 1
            'shadow' => isset($oShadow) ? $oShadow : null,
356 2
            'listStyle' => isset($arrayListStyle) ? $arrayListStyle : null,
357
        );
358
        
359 3
        return true;
360
    }
361
362
    /**
363
     * Read Slide
364
     *
365
     * @param \DOMElement $nodeSlide
366
     */
367 3
    protected function loadSlide(\DOMElement $nodeSlide)
368
    {
369
        // Core
370 3
        $this->oPhpPresentation->createSlide();
371 3
        $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);
372 3
        if ($nodeSlide->hasAttribute('draw:name')) {
373 3
            $this->oPhpPresentation->getActiveSlide()->setName($nodeSlide->getAttribute('draw:name'));
374
        }
375 3
        if ($nodeSlide->hasAttribute('draw:style-name')) {
376 3
            $keyStyle = $nodeSlide->getAttribute('draw:style-name');
377 3
            if (isset($this->arrayStyles[$keyStyle])) {
378 3
                $this->oPhpPresentation->getActiveSlide()->setBackground($this->arrayStyles[$keyStyle]['background']);
379
            }
380
        }
381 3
        foreach ($this->oXMLReader->getElements('draw:frame', $nodeSlide) as $oNodeFrame) {
382 3
            if ($this->oXMLReader->getElement('draw:image', $oNodeFrame)) {
383 2
                $this->loadShapeDrawing($oNodeFrame);
384 2
                continue;
385
            }
386 3
            if ($this->oXMLReader->getElement('draw:text-box', $oNodeFrame)) {
387 3
                $this->loadShapeRichText($oNodeFrame);
388 3
                continue;
389
            }
390
        }
391 3
        return true;
392
    }
393
    
394
    /**
395
     * Read Shape Drawing
396
     *
397
     * @param \DOMElement $oNodeFrame
398
     */
399 2
    protected function loadShapeDrawing(\DOMElement $oNodeFrame)
400
    {
401
        // Core
402 2
        $oShape = new Gd();
403 2
        $oShape->getShadow()->setVisible(false);
404
405 2
        $oNodeImage = $this->oXMLReader->getElement('draw:image', $oNodeFrame);
406 2
        if ($oNodeImage instanceof \DOMElement) {
407 2
            if ($oNodeImage->hasAttribute('xlink:href')) {
408 2
                $sFilename = $oNodeImage->getAttribute('xlink:href');
409
                // svm = StarView Metafile
410 2
                if (pathinfo($sFilename, PATHINFO_EXTENSION) == 'svm') {
411 1
                    return;
412
                }
413 2
                $imageFile = $this->oZip->getFromName($sFilename);
414 2
                if (!empty($imageFile)) {
415 2
                    $oShape->setImageResource(imagecreatefromstring($imageFile));
416
                }
417
            }
418
        }
419
        
420 2
        $oShape->setName($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
421 2
        $oShape->setDescription($oNodeFrame->hasAttribute('draw:name') ? $oNodeFrame->getAttribute('draw:name') : '');
422 2
        $oShape->setResizeProportional(false);
423 2
        $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
424 2
        $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
425 2
        $oShape->setResizeProportional(true);
426 2
        $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
427 2
        $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
428
        
429 2
        if ($oNodeFrame->hasAttribute('draw:style-name')) {
430 2
            $keyStyle = $oNodeFrame->getAttribute('draw:style-name');
431 2
            if (isset($this->arrayStyles[$keyStyle])) {
432 2
                $oShape->setShadow($this->arrayStyles[$keyStyle]['shadow']);
433 2
                $oShape->setFill($this->arrayStyles[$keyStyle]['fill']);
434
            }
435
        }
436
        
437 2
        $this->oPhpPresentation->getActiveSlide()->addShape($oShape);
438 2
    }
439
440
    /**
441
     * Read Shape RichText
442
     *
443
     * @param \DOMElement $oNodeFrame
444
     */
445 3
    protected function loadShapeRichText(\DOMElement $oNodeFrame)
446
    {
447
        // Core
448 3
        $oShape = $this->oPhpPresentation->getActiveSlide()->createRichTextShape();
449 3
        $oShape->setParagraphs(array());
450
        
451 3
        $oShape->setWidth($oNodeFrame->hasAttribute('svg:width') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:width'), 0, -2))) : '');
452 3
        $oShape->setHeight($oNodeFrame->hasAttribute('svg:height') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:height'), 0, -2))) : '');
453 3
        $oShape->setOffsetX($oNodeFrame->hasAttribute('svg:x') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:x'), 0, -2))) : '');
454 3
        $oShape->setOffsetY($oNodeFrame->hasAttribute('svg:y') ? (int)round(CommonDrawing::centimetersToPixels(substr($oNodeFrame->getAttribute('svg:y'), 0, -2))) : '');
455
        
456 3
        foreach ($this->oXMLReader->getElements('draw:text-box/*', $oNodeFrame) as $oNodeParagraph) {
457 2
            $this->levelParagraph = 0;
458 2
            if ($oNodeParagraph->nodeName == 'text:p') {
459 2
                $this->readParagraph($oShape, $oNodeParagraph);
460
            }
461 2
            if ($oNodeParagraph->nodeName == 'text:list') {
462 2
                $this->readList($oShape, $oNodeParagraph);
463
            }
464
        }
465
        
466 3
        if (count($oShape->getParagraphs()) > 0) {
467 2
            $oShape->setActiveParagraph(0);
468
        }
469 3
    }
470
    
471
    protected $levelParagraph = 0;
472
    
473
    /**
474
     * Read Paragraph
475
     * @param RichText $oShape
476
     * @param \DOMElement $oNodeParent
477
     */
478 2
    protected function readParagraph(RichText $oShape, \DOMElement $oNodeParent)
479
    {
480 2
        $oParagraph = $oShape->createParagraph();
481 2
        $oDomList = $this->oXMLReader->getElements('text:span', $oNodeParent);
482 2
        $oDomTextNodes = $this->oXMLReader->getElements('text()', $oNodeParent);
483 2
        foreach ($oDomTextNodes as $oDomTextNode) {
484 2
            if (trim($oDomTextNode->nodeValue) != '') {
485 1
                $oTextRun = $oParagraph->createTextRun();
486 2
                $oTextRun->setText(trim($oDomTextNode->nodeValue));
487
            }
488
        }
489 2
        foreach ($oDomList as $oNodeRichTextElement) {
490 1
            $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
491
        }
492 2
    }
493
    
494
    /**
495
     * Read Paragraph Item
496
     * @param Paragraph $oParagraph
497
     * @param \DOMElement $oNodeParent
498
     */
499 1
    protected function readParagraphItem(Paragraph $oParagraph, \DOMElement $oNodeParent)
500
    {
501 1
        if ($this->oXMLReader->elementExists('text:line-break', $oNodeParent)) {
502 1
            $oParagraph->createBreak();
503
        } else {
504 1
            $oTextRun = $oParagraph->createTextRun();
505 1
            if ($oNodeParent->hasAttribute('text:style-name')) {
506 1
                $keyStyle = $oNodeParent->getAttribute('text:style-name');
507 1
                if (isset($this->arrayStyles[$keyStyle])) {
508 1
                    $oTextRun->setFont($this->arrayStyles[$keyStyle]['font']);
509
                }
510
            }
511 1
            $oTextRunLink = $this->oXMLReader->getElement('text:a', $oNodeParent);
512 1
            if ($oTextRunLink instanceof \DOMElement) {
513 1
                $oTextRun->setText($oTextRunLink->nodeValue);
514 1
                if ($oTextRunLink->hasAttribute('xlink:href')) {
515 1
                    $oTextRun->getHyperlink()->setUrl($oTextRunLink->getAttribute('xlink:href'));
516
                }
517
            } else {
518 1
                $oTextRun->setText($oNodeParent->nodeValue);
519
            }
520
        }
521 1
    }
522
523
    /**
524
     * Read List
525
     *
526
     * @param RichText $oShape
527
     * @param \DOMElement $oNodeParent
528
     */
529 1
    protected function readList(RichText $oShape, \DOMElement $oNodeParent)
530
    {
531 1
        foreach ($this->oXMLReader->getElements('text:list-item/*', $oNodeParent) as $oNodeListItem) {
532 1
            if ($oNodeListItem->nodeName == 'text:p') {
533 1
                $this->readListItem($oShape, $oNodeListItem, $oNodeParent);
534
            }
535 1
            if ($oNodeListItem->nodeName == 'text:list') {
536 1
                $this->levelParagraph++;
537 1
                $this->readList($oShape, $oNodeListItem);
538 1
                $this->levelParagraph--;
539
            }
540
        }
541 1
    }
542
    
543
    /**
544
     * Read List Item
545
     * @param RichText $oShape
546
     * @param \DOMElement $oNodeParent
547
     * @param \DOMElement $oNodeParagraph
548
     */
549 1
    protected function readListItem(RichText $oShape, \DOMElement $oNodeParent, \DOMElement $oNodeParagraph)
550
    {
551 1
        $oParagraph = $oShape->createParagraph();
552 1
        if ($oNodeParagraph->hasAttribute('text:style-name')) {
553 1
            $keyStyle = $oNodeParagraph->getAttribute('text:style-name');
554 1
            if (isset($this->arrayStyles[$keyStyle]) && !empty($this->arrayStyles[$keyStyle]['listStyle'])) {
555 1
                $oParagraph->setAlignment($this->arrayStyles[$keyStyle]['listStyle'][$this->levelParagraph]['alignment']);
556 1
                $oParagraph->setBulletStyle($this->arrayStyles[$keyStyle]['listStyle'][$this->levelParagraph]['bullet']);
557
            }
558
        }
559 1
        foreach ($this->oXMLReader->getElements('text:span', $oNodeParent) as $oNodeRichTextElement) {
560 1
            $this->readParagraphItem($oParagraph, $oNodeRichTextElement);
561
        }
562 1
    }
563
564
    /**
565
     * Load file 'styles.xml'
566
     */
567 3
    protected function loadStylesFile()
568
    {
569 3
        foreach ($this->oXMLReader->getElements('/office:document-styles/office:styles/*') as $oElement) {
570 3
            if ($oElement->nodeName == 'draw:fill-image') {
571
                $this->arrayCommonStyles[$oElement->getAttribute('draw:name')] = array(
572 3
                    'type' => 'image',
573
                    'path' => $oElement->hasAttribute('xlink:href') ? $oElement->getAttribute('xlink:href') : null
574
                );
575
            }
576
        }
577 3
    }
578
579
    /**
580
     * @param string $filename
581
     * @return bool
582
     * @throws \Exception
583
     */
584 3
    protected function loadFileFromODP($filename)
585
    {
586 3
        $bEncrypted = false;
587
588 3
        if (!$this->oXMLMetaInfManifest) {
589 3
            $this->oXMLMetaInfManifest = new XMLReader();
590 3
            if ($this->oXMLMetaInfManifest->getDomFromZip($this->filename, 'META-INF/manifest.xml') === false) {
591
                return false;
592
            }
593
        }
594
        // Search file in META-INF/manifest.xml
595 3
        $oElement = $this->oXMLMetaInfManifest->getElement('/manifest:manifest/manifest:file-entry[@manifest:full-path=\''.$filename.'\']');
596 3
        if (!$oElement) {
597
            return false;
598
        }
599
        // Has it some manifest:encryption-data ?
600 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...
601 3
        if ($oElementEncryption) {
602
            $bEncrypted = true;
603
        }
604
605 3
        $fileContent = $this->oZip->getFromName($filename);
606 3
        if (!$fileContent){
607
            return false;
608
        }
609
610
        // No Encrypted file
611 3
        if (!$bEncrypted) {
612 3
            $this->oXMLReader = new XMLReader();
613 3
            $this->oXMLReader->getDomFromString($fileContent);
614 3
            return true;
615
        }
616
617
        //return false;
618
        /*
619
          <manifest:encryption-data
620
                manifest:checksum-type="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0#sha256-1k"
621
                manifest:checksum="BfB+taOY0kcVO/9WNi4DfqioRp3LMwVoNbqfAQ37yac=">
622
              <manifest:algorithm
623
                    manifest:algorithm-name="http://www.w3.org/2001/04/xmlenc#aes256-cbc"
624
                    manifest:initialisation-vector="I7rMXmvuynJFxJtm+EQ5qA=="/>
625
              <manifest:key-derivation
626
                    manifest:key-derivation-name="PBKDF2"
627
                    manifest:key-size="32"
628
                    manifest:iteration-count="1024"
629
                    manifest:salt="Mows9XX/YiNKNJ0qll3jgA=="/>
630
              <manifest:start-key-generation
631
                    manifest:start-key-generation-name="http://www.w3.org/2000/09/xmldsig#sha256"
632
                    manifest:key-size="32"/>
633
          </manifest:encryption-data>
634
         </manifest:file-entry>
635
         */
636
        return false;
637
        // Encrypted file
638
        $checksum = $oElementEncryption->getAttribute('manifest:checksum');
0 ignored issues
show
Unused Code introduced by
// Encrypted file $check...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...
639
640
        $oEltKeyDerivation = $this->oXMLMetaInfManifest->getElement('manifest:key-derivation', $oElementEncryption);
641
        $salt = $oEltKeyDerivation->getAttribute('manifest:salt');
642
        //$salt = base64_decode($salt);
643
        echo 'manifest:salt : ';
644
        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...
645
        $iterationCount = $oEltKeyDerivation->getAttribute('manifest:iteration-count');
646
        echo 'manifest:iteration-count : ';
647
        var_dump($iterationCount);
648
        $keySize = $oEltKeyDerivation->getAttribute('manifest:key-size');
649
        echo 'manifest:key-size : ';
650
        var_dump($keySize);
651
652
        $oEltAlgorithm = $this->oXMLMetaInfManifest->getElement('manifest:algorithm', $oElementEncryption);
653
        $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...
654
        $iv = base64_decode($iv);
655
        echo 'manifest:initialisation-vector : ';
656
        var_dump($iv);
657
658
        $pwdHash = hash('sha256', $this->getPassword());
659
        echo 'sha256('.$this->getPassword().'): ';
660
        var_dump($pwdHash);
661
        //$pwdHash = substr($pwdHash, 0 , 32);
662
        //var_dump($pwdHash);
663
664
        $key = hash_pbkdf2('sha1', $pwdHash, $salt, $iterationCount, $keySize, true);
665
        echo 'hash_pbkdf2 (sha1, hash, salt, iterationCount, $iterationCount) : ';
666
        var_dump($key);
667
        //$key = $this->hash_pbkdf2('sha1', $pwdHash, $salt, $iterationCount, $keySize, true);
668
        //echo 'hash_pbkdf2 (sha1, hash, salt, iterationCount, $iterationCount) : ';
669
        //var_dump($key);
670
671
        $data = openssl_decrypt($fileContent, 'AES-256-CBC', $key, 0, $iv);
672
        if(!$data) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
673
            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...
674
                var_dump($msg);
675
            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...
676
        } else {
677
            var_dump($data);
678
            $data = gzinflate($data);
679
            var_dump($data);
680
        }
681
682
        /*$data = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $fileContent, MCRYPT_MODE_CBC, $iv);
683
        var_dump($data);
684
        $data = gzinflate($data);
685
        if($data) {
686
            var_dump($data);
687
        }*/
688
689
        return false;
690
    }
691
692
    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...
693
    {
694
        // Derived key
695
        $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...
696
        // Create key
697
        for ($block=1; $block<=$key_length; $block++)
698
        {
699
            // Initial hash for this block
700
            $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...
701
            // Perform block iterations
702
            for ($i=1; $i<$rounds; $i++)
703
            {
704
                // XOR each iteration
705
                $ib ^= ($h = hash_hmac($a, $h, $password, true));
706
            }
707
            // Append iterated block
708
            $dk .= $ib;
709
        }
710
        // Return derived key of correct length
711
        $key = substr($dk, 0, $key_length);
712
        return $raw_output ? $key : base64_encode($key);
713
    }
714
715
}
716