Completed
Pull Request — develop (#342)
by Franck
09:03
created

PowerPoint97::canRead()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 1
crap 1
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 PhpOffice\Common\Microsoft\OLERead;
21
use PhpOffice\Common\Text;
22
use PhpOffice\PhpPresentation\PhpPresentation;
23
use PhpOffice\PhpPresentation\AbstractShape;
24
use PhpOffice\PhpPresentation\Shape;
25
use PhpOffice\PhpPresentation\Shape\Drawing;
26
use PhpOffice\PhpPresentation\Shape\Group;
27
use PhpOffice\PhpPresentation\Shape\Hyperlink;
28
use PhpOffice\PhpPresentation\Shape\Line;
29
use PhpOffice\PhpPresentation\Shape\RichText;
30
use PhpOffice\PhpPresentation\Style\Alignment;
31
use PhpOffice\PhpPresentation\Style\Color;
32
use PhpOffice\PhpPresentation\Style\Bullet;
33
34
/**
35
 * Serialized format reader
36
 */
37
class PowerPoint97 implements ReaderInterface
38
{
39
    const OFFICEARTBLIPEMF = 0xF01A;
40
    const OFFICEARTBLIPWMF = 0xF01B;
41
    const OFFICEARTBLIPPICT = 0xF01C;
42
    const OFFICEARTBLIPJPG = 0xF01D;
43
    const OFFICEARTBLIPPNG = 0xF01E;
44
    const OFFICEARTBLIPDIB = 0xF01F;
45
    const OFFICEARTBLIPTIFF = 0xF029;
46
    const OFFICEARTBLIPJPEG = 0xF02A;
47
48
    /**
49
     * @link http://msdn.microsoft.com/en-us/library/dd945336(v=office.12).aspx
50
     */
51
    const RT_ANIMATIONINFO = 0x1014;
52
    const RT_ANIMATIONINFOATOM = 0x0FF1;
53
    const RT_BINARYTAGDATABLOB = 0x138B;
54
    const RT_BLIPCOLLECTION9 = 0x07F8;
55
    const RT_BLIPENTITY9ATOM = 0x07F9;
56
    const RT_BOOKMARKCOLLECTION = 0x07E3;
57
    const RT_BOOKMARKENTITYATOM = 0x0FD0;
58
    const RT_BOOKMARKSEEDATOM = 0x07E9;
59
    const RT_BROADCASTDOCINFO9 = 0x177E;
60
    const RT_BROADCASTDOCINFO9ATOM = 0x177F;
61
    const RT_BUILDATOM = 0x2B03;
62
    const RT_BUILDLIST = 0x2B02;
63
    const RT_CHARTBUILD = 0x2B04;
64
    const RT_CHARTBUILDATOM = 0x2B05;
65
    const RT_COLORSCHEMEATOM = 0x07F0;
66
    const RT_COMMENT10 = 0x2EE0;
67
    const RT_COMMENT10ATOM = 0x2EE1;
68
    const RT_COMMENTINDEX10 = 0x2EE4;
69
    const RT_COMMENTINDEX10ATOM = 0x2EE5;
70
    const RT_CRYPTSESSION10CONTAINER = 0x2F14;
71
    const RT_CURRENTUSERATOM = 0x0FF6;
72
    const RT_CSTRING = 0x0FBA;
73
    const RT_DATETIMEMETACHARATOM = 0x0FF7;
74
    const RT_DEFAULTRULERATOM = 0x0FAB;
75
    const RT_DOCROUTINGSLIPATOM = 0x0406;
76
    const RT_DIAGRAMBUILD = 0x2B06;
77
    const RT_DIAGRAMBUILDATOM = 0x2B07;
78
    const RT_DIFF10 = 0x2EED;
79
    const RT_DIFF10ATOM = 0x2EEE;
80
    const RT_DIFFTREE10 = 0x2EEC;
81
    const RT_DOCTOOLBARSTATES10ATOM = 0x36B1;
82
    const RT_DOCUMENT = 0x03E8;
83
    const RT_DOCUMENTATOM = 0x03E9;
84
    const RT_DRAWING = 0x040C;
85
    const RT_DRAWINGGROUP = 0x040B;
86
    const RT_ENDDOCUMENTATOM = 0x03EA;
87
    const RT_EXTERNALAVIMOVIE = 0x1006;
88
    const RT_EXTERNALCDAUDIO = 0x100E;
89
    const RT_EXTERNALCDAUDIOATOM = 0x1012;
90
    const RT_EXTERNALHYPERLINK = 0x0FD7;
91
    const RT_EXTERNALHYPERLINK9 = 0x0FE4;
92
    const RT_EXTERNALHYPERLINKATOM = 0x0FD3;
93
    const RT_EXTERNALHYPERLINKFLAGSATOM = 0x1018;
94
    const RT_EXTERNALMCIMOVIE = 0x1007;
95
    const RT_EXTERNALMEDIAATOM = 0x1004;
96
    const RT_EXTERNALMIDIAUDIO = 0x100D;
97
    const RT_EXTERNALOBJECTLIST = 0x0409;
98
    const RT_EXTERNALOBJECTLISTATOM = 0x040A;
99
    const RT_EXTERNALOBJECTREFATOM = 0x0BC1;
100
    const RT_EXTERNALOLECONTROL = 0x0FEE;
101
    const RT_EXTERNALOLECONTROLATOM = 0x0FFB;
102
    const RT_EXTERNALOLEEMBED = 0x0FCC;
103
    const RT_EXTERNALOLEEMBEDATOM = 0x0FCD;
104
    const RT_EXTERNALOLELINK = 0x0FCE;
105
    const RT_EXTERNALOLELINKATOM = 0x0FD1;
106
    const RT_EXTERNALOLEOBJECTATOM = 0x0FC3;
107
    const RT_EXTERNALOLEOBJECTSTG = 0x1011;
108
    const RT_EXTERNALVIDEO = 0x1005;
109
    const RT_EXTERNALWAVAUDIOEMBEDDED = 0x100F;
110
    const RT_EXTERNALWAVAUDIOEMBEDDEDATOM = 0x1013;
111
    const RT_EXTERNALWAVAUDIOLINK = 0x1010;
112
    const RT_ENVELOPEDATA9ATOM = 0x1785;
113
    const RT_ENVELOPEFLAGS9ATOM = 0x1784;
114
    const RT_ENVIRONMENT = 0x03F2;
115
    const RT_FONTCOLLECTION = 0x07D5;
116
    const RT_FONTCOLLECTION10 = 0x07D6;
117
    const RT_FONTEMBEDDATABLOB = 0x0FB8;
118
    const RT_FONTEMBEDFLAGS10ATOM = 0x32C8;
119
    const RT_FILTERPRIVACYFLAGS10ATOM = 0x36B0;
120
    const RT_FONTENTITYATOM = 0x0FB7;
121
    const RT_FOOTERMETACHARATOM = 0x0FFA;
122
    const RT_GENERICDATEMETACHARATOM = 0x0FF8;
123
    const RT_GRIDSPACING10ATOM = 0x040D;
124
    const RT_GUIDEATOM = 0x03FB;
125
    const RT_HANDOUT = 0x0FC9;
126
    const RT_HASHCODEATOM = 0x2B00;
127
    const RT_HEADERSFOOTERS = 0x0FD9;
128
    const RT_HEADERSFOOTERSATOM = 0x0FDA;
129
    const RT_HEADERMETACHARATOM = 0x0FF9;
130
    const RT_HTMLDOCINFO9ATOM = 0x177B;
131
    const RT_HTMLPUBLISHINFOATOM = 0x177C;
132
    const RT_HTMLPUBLISHINFO9 = 0x177D;
133
    const RT_INTERACTIVEINFO = 0x0FF2;
134
    const RT_INTERACTIVEINFOATOM = 0x0FF3;
135
    const RT_KINSOKU = 0x0FC8;
136
    const RT_KINSOKUATOM = 0x0FD2;
137
    const RT_LEVELINFOATOM = 0x2B0A;
138
    const RT_LINKEDSHAPE10ATOM = 0x2EE6;
139
    const RT_LINKEDSLIDE10ATOM = 0x2EE7;
140
    const RT_LIST = 0x07D0;
141
    const RT_MAINMASTER = 0x03F8;
142
    const RT_MASTERTEXTPROPATOM = 0x0FA2;
143
    const RT_METAFILE = 0x0FC1;
144
    const RT_NAMEDSHOW = 0x0411;
145
    const RT_NAMEDSHOWS = 0x0410;
146
    const RT_NAMEDSHOWSLIDESATOM = 0x0412;
147
    const RT_NORMALVIEWSETINFO9 = 0x0414;
148
    const RT_NORMALVIEWSETINFO9ATOM = 0x0415;
149
    const RT_NOTES= 0x03F0;
150
    const RT_NOTESATOM = 0x03F1;
151
    const RT_NOTESTEXTVIEWINFO9 = 0x0413;
152
    const RT_OUTLINETEXTPROPS9 = 0x0FAE;
153
    const RT_OUTLINETEXTPROPS10 = 0x0FB3;
154
    const RT_OUTLINETEXTPROPS11 = 0x0FB5;
155
    const RT_OUTLINETEXTPROPSHEADER9ATOM = 0x0FAF;
156
    const RT_OUTLINETEXTREFATOM = 0x0F9E;
157
    const RT_OUTLINEVIEWINFO = 0x0407;
158
    const RT_PERSISTDIRECTORYATOM = 0x1772;
159
    const RT_PARABUILD = 0x2B08;
160
    const RT_PARABUILDATOM = 0x2B09;
161
    const RT_PHOTOALBUMINFO10ATOM = 0x36B2;
162
    const RT_PLACEHOLDERATOM = 0x0BC3;
163
    const RT_PRESENTATIONADVISORFLAGS9ATOM = 0x177A;
164
    const RT_PRINTOPTIONSATOM = 0x1770;
165
    const RT_PROGBINARYTAG = 0x138A;
166
    const RT_PROGSTRINGTAG = 0x1389;
167
    const RT_PROGTAGS = 0x1388;
168
    const RT_RECOLORINFOATOM = 0x0FE7;
169
    const RT_RTFDATETIMEMETACHARATOM = 0x1015;
170
    const RT_ROUNDTRIPANIMATIONATOM12ATOM = 0x2B0B;
171
    const RT_ROUNDTRIPANIMATIONHASHATOM12ATOM = 0x2B0D;
172
    const RT_ROUNDTRIPCOLORMAPPING12ATOM = 0x040F;
173
    const RT_ROUNDTRIPCOMPOSITEMASTERID12ATOM = 0x041D;
174
    const RT_ROUNDTRIPCONTENTMASTERID12ATOM = 0x0422;
175
    const RT_ROUNDTRIPCONTENTMASTERINFO12ATOM = 0x041E;
176
    const RT_ROUNDTRIPCUSTOMTABLESTYLES12ATOM = 0x0428;
177
    const RT_ROUNDTRIPDOCFLAGS12ATOM = 0x0425;
178
    const RT_ROUNDTRIPHEADERFOOTERDEFAULTS12ATOM = 0x0424;
179
    const RT_ROUNDTRIPHFPLACEHOLDER12ATOM = 0x0420;
180
    const RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM = 0x0BDD;
181
    const RT_ROUNDTRIPNOTESMASTERTEXTSTYLES12ATOM = 0x0427;
182
    const RT_ROUNDTRIPOARTTEXTSTYLES12ATOM = 0x0423;
183
    const RT_ROUNDTRIPORIGINALMAINMASTERID12ATOM = 0x041C;
184
    const RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM = 0x0426;
185
    const RT_ROUNDTRIPSHAPEID12ATOM = 0x041F;
186
    const RT_ROUNDTRIPSLIDESYNCINFO12 = 0x3714;
187
    const RT_ROUNDTRIPSLIDESYNCINFOATOM12 = 0x3715;
188
    const RT_ROUNDTRIPTHEME12ATOM = 0x040E;
189
    const RT_SHAPEATOM = 0x0BDB;
190
    const RT_SHAPEFLAGS10ATOM = 0x0BDC;
191
    const RT_SLIDE = 0x03EE;
192
    const RT_SLIDEATOM = 0x03EF;
193
    const RT_SLIDEFLAGS10ATOM = 0x2EEA;
194
    const RT_SLIDELISTENTRY10ATOM = 0x2EF0;
195
    const RT_SLIDELISTTABLE10 = 0x2EF1;
196
    const RT_SLIDELISTWITHTEXT = 0x0FF0;
197
    const RT_SLIDELISTTABLESIZE10ATOM = 0x2EEF;
198
    const RT_SLIDENUMBERMETACHARATOM = 0x0FD8;
199
    const RT_SLIDEPERSISTATOM = 0x03F3;
200
    const RT_SLIDESHOWDOCINFOATOM = 0x0401;
201
    const RT_SLIDESHOWSLIDEINFOATOM = 0x03F9;
202
    const RT_SLIDETIME10ATOM = 0x2EEB;
203
    const RT_SLIDEVIEWINFO = 0x03FA;
204
    const RT_SLIDEVIEWINFOATOM = 0x03FE;
205
    const RT_SMARTTAGSTORE11CONTAINER = 0x36B3;
206
    const RT_SOUND = 0x07E6;
207
    const RT_SOUNDCOLLECTION = 0x07E4;
208
    const RT_SOUNDCOLLECTIONATOM = 0x07E5;
209
    const RT_SOUNDDATABLOB = 0x07E7;
210
    const RT_SORTERVIEWINFO = 0x0408;
211
    const RT_STYLETEXTPROPATOM = 0x0FA1;
212
    const RT_STYLETEXTPROP10ATOM = 0x0FB1;
213
    const RT_STYLETEXTPROP11ATOM = 0x0FB6;
214
    const RT_STYLETEXTPROP9ATOM = 0x0FAC;
215
    const RT_SUMMARY = 0x0402;
216
    const RT_TEXTBOOKMARKATOM = 0x0FA7;
217
    const RT_TEXTBYTESATOM = 0x0FA8;
218
    const RT_TEXTCHARFORMATEXCEPTIONATOM = 0x0FA4;
219
    const RT_TEXTCHARSATOM = 0x0FA0;
220
    const RT_TEXTDEFAULTS10ATOM = 0x0FB4;
221
    const RT_TEXTDEFAULTS9ATOM = 0x0FB0;
222
    const RT_TEXTHEADERATOM = 0x0F9F;
223
    const RT_TEXTINTERACTIVEINFOATOM = 0x0FDF;
224
    const RT_TEXTMASTERSTYLEATOM = 0x0FA3;
225
    const RT_TEXTMASTERSTYLE10ATOM = 0x0FB2;
226
    const RT_TEXTMASTERSTYLE9ATOM = 0x0FAD;
227
    const RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM = 0x0FA5;
228
    const RT_TEXTRULERATOM = 0x0FA6;
229
    const RT_TEXTSPECIALINFOATOM = 0x0FAA;
230
    const RT_TEXTSPECIALINFODEFAULTATOM = 0x0FA9;
231
    const RT_TIMEANIMATEBEHAVIOR = 0xF134;
232
    const RT_TIMEANIMATEBEHAVIORCONTAINER = 0xF12B;
233
    const RT_TIMEANIMATIONVALUE = 0xF143;
234
    const RT_TIMEANIMATIONVALUELIST = 0xF13F;
235
    const RT_TIMEBEHAVIOR = 0xF133;
236
    const RT_TIMEBEHAVIORCONTAINER = 0xF12A;
237
    const RT_TIMECOLORBEHAVIOR = 0xF135;
238
    const RT_TIMECOLORBEHAVIORCONTAINER = 0xF12C;
239
    const RT_TIMECLIENTVISUALELEMENT = 0xF13C;
240
    const RT_TIMECOMMANDBEHAVIOR = 0xF13B;
241
    const RT_TIMECOMMANDBEHAVIORCONTAINER = 0xF132;
242
    const RT_TIMECONDITION = 0xF128;
243
    const RT_TIMECONDITIONCONTAINER = 0xF125;
244
    const RT_TIMEEFFECTBEHAVIOR = 0xF136;
245
    const RT_TIMEEFFECTBEHAVIORCONTAINER = 0xF12D;
246
    const RT_TIMEEXTTIMENODECONTAINER = 0xF144;
247
    const RT_TIMEITERATEDATA = 0xF140;
248
    const RT_TIMEMODIFIER = 0xF129;
249
    const RT_TIMEMOTIONBEHAVIOR = 0xF137;
250
    const RT_TIMEMOTIONBEHAVIORCONTAINER = 0xF12E;
251
    const RT_TIMENODE = 0xF127;
252
    const RT_TIMEPROPERTYLIST = 0xF13D;
253
    const RT_TIMEROTATIONBEHAVIOR = 0xF138;
254
    const RT_TIMEROTATIONBEHAVIORCONTAINER = 0xF12F;
255
    const RT_TIMESCALEBEHAVIOR = 0xF139;
256
    const RT_TIMESCALEBEHAVIORCONTAINER = 0xF130;
257
    const RT_TIMESEQUENCEDATA = 0xF141;
258
    const RT_TIMESETBEHAVIOR = 0xF13A;
259
    const RT_TIMESETBEHAVIORCONTAINER = 0xF131;
260
    const RT_TIMESUBEFFECTCONTAINER = 0xF145;
261
    const RT_TIMEVARIANT = 0xF142;
262
    const RT_TIMEVARIANTLIST = 0xF13E;
263
    const RT_USEREDITATOM = 0x0FF5;
264
    const RT_VBAINFO = 0x03FF;
265
    const RT_VBAINFOATOM = 0x0400;
266
    const RT_VIEWINFOATOM = 0x03FD;
267
    const RT_VISUALPAGEATOM = 0x2B01;
268
    const RT_VISUALSHAPEATOM = 0x2AFB;
269
270
    /**
271
     * @var http://msdn.microsoft.com/en-us/library/dd926394(v=office.12).aspx
272
     */
273
    const SL_BIGOBJECT = 0x0000000F;
274
    const SL_BLANK = 0x00000010;
275
    const SL_COLUMNTWOROWS = 0x0000000A;
276
    const SL_FOUROBJECTS = 0x0000000E;
277
    const SL_MASTERTITLE = 0x00000002;
278
    const SL_TITLEBODY = 0x00000001;
279
    const SL_TITLEONLY = 0x00000007;
280
    const SL_TITLESLIDE = 0x00000000;
281
    const SL_TWOCOLUMNS = 0x00000008;
282
    const SL_TWOCOLUMNSROW = 0x0000000D;
283
    const SL_TWOROWS = 0x00000009;
284
    const SL_TWOROWSCOLUMN = 0x0000000B;
285
    const SL_VERTICALTITLEBODY = 0x00000011;
286
    const SL_VERTICALTWOROWS = 0x00000012;
287
288
    /**
289
     * Array with Fonts
290
     */
291
    private $arrayFonts = array();
292
    /**
293
     * Array with Hyperlinks
294
     */
295
    private $arrayHyperlinks = array();
296
    /**
297
     * Array with Notes
298
     */
299
    private $arrayNotes = array();
300
    /**
301
     * Array with Pictures
302
     */
303
    private $arrayPictures = array();
304
    /**
305
     * Offset (in bytes) from the beginning of the PowerPoint Document Stream to the UserEditAtom record for the most recent user edit.
306
     * @var int
307
     */
308
    private $offsetToCurrentEdit;
309
    /**
310
     * A structure that specifies a compressed table of sequential persist object identifiers and stream offsets to associated persist objects.
311
     * @var int[]
312
     */
313
    private $rgPersistDirEntry;
314
    /**
315
     * Offset (in bytes) from the beginning of the PowerPoint Document Stream to the PersistDirectoryAtom record for this user edit
316
     * @var int
317
     */
318
    private $offsetPersistDirectory;
319
    /**
320
     * Output Object
321
     * @var PhpPresentation
322
     */
323
    private $oPhpPresentation;
324
    /**
325
     * Group Object
326
     * @var Group
327
     */
328
    private $oCurrentGroup;
329
    /**
330
     * @var boolean
331
     */
332
    private $bFirstShapeGroup = false;
333
    /**
334
     * Stream "Powerpoint Document"
335
     * @var string
336
     */
337
    private $streamPowerpointDocument;
338
    /**
339
     * Stream "Current User"
340
     * @var string
341
     */
342
    private $streamCurrentUser;
343
    /**
344
     * Stream "Summary Information"
345
     * @var string
346
     */
347
    private $streamSummaryInformation;
348
    /**
349
     * Stream "Document Summary Information"
350
     * @var string
351
     */
352
    private $streamDocumentSummaryInformation;
353
    /**
354
     * Stream "Pictures"
355
     * @var string
356
     */
357
    private $streamPictures;
358
    /**
359
     * @var integer
360
     */
361
    private $inMainType;
362
    /**
363
     * @var integer
364
     */
365
    private $currentNote;
366
367
    /**
368
     * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
369
     *
370
     * @param  string $pFilename
371
     * @throws \Exception
372
     * @return boolean
373
     */
374 3
    public function canRead($pFilename)
375
    {
376 3
        return $this->fileSupportsUnserializePhpPresentation($pFilename);
377
    }
378
379
    /**
380
     * Does a file support UnserializePhpPresentation ?
381
     *
382
     * @param  string    $pFilename
383
     * @throws \Exception
384
     * @return boolean
385
     */
386 10
    public function fileSupportsUnserializePhpPresentation($pFilename = '')
387
    {
388
        // Check if file exists
389 10
        if (!file_exists($pFilename)) {
390 2
            throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
391
        }
392
393
        try {
394
            // Use ParseXL for the hard work.
395 8
            $ole = new OLERead();
396
            // get excel data
397 8
            $ole->read($pFilename);
398 5
            return true;
399 3
        } catch (\Exception $e) {
400 3
            return false;
401
        }
402
    }
403
404
    /**
405
     * Loads PhpPresentation Serialized file
406
     *
407
     * @param  string        $pFilename
408
     * @return \PhpOffice\PhpPresentation\PhpPresentation
409
     * @throws \Exception
410
     */
411 6
    public function load($pFilename)
412
    {
413
        // Unserialize... First make sure the file supports it!
414 6
        if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
415 1
            throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\PowerPoint97: " . $pFilename . ".");
416
        }
417
418 4
        return $this->loadFile($pFilename);
419
    }
420
421
    /**
422
     * Load PhpPresentation Serialized file
423
     *
424
     * @param  string        $pFilename
425
     * @return \PhpOffice\PhpPresentation\PhpPresentation
426
     */
427 4
    private function loadFile($pFilename)
428
    {
429 4
        $this->oPhpPresentation = new PhpPresentation();
430 4
        $this->oPhpPresentation->removeSlideByIndex();
431
432
        // Read OLE Blocks
433 4
        $this->loadOLE($pFilename);
434
        // Read pictures in the Pictures Stream
435 4
        $this->loadPicturesStream();
436
        // Read information in the Current User Stream
437 4
        $this->loadCurrentUserStream();
438
        // Read information in the PowerPoint Document Stream
439 4
        $this->loadPowerpointDocumentStream();
440
441 3
        return $this->oPhpPresentation;
442
    }
443
444
    /**
445
     * Read OLE Part
446
     * @param string $pFilename
447
     */
448 4
    private function loadOLE($pFilename)
449
    {
450
        // OLE reader
451 4
        $oOLE = new OLERead();
452 4
        $oOLE->read($pFilename);
453
454
        // PowerPoint Document Stream
455 4
        $this->streamPowerpointDocument = $oOLE->getStream($oOLE->powerpointDocument);
456
457
        // Current User Stream
458 4
        $this->streamCurrentUser = $oOLE->getStream($oOLE->currentUser);
459
460
        // Get summary information data
461 4
        $this->streamSummaryInformation = $oOLE->getStream($oOLE->summaryInformation);
462
463
        // Get additional document summary information data
464 4
        $this->streamDocumentSummaryInformation = $oOLE->getStream($oOLE->docSummaryInfos);
465
466
        // Get pictures data
467 4
        $this->streamPictures = $oOLE->getStream($oOLE->pictures);
468 4
    }
469
470
    /**
471
     * Stream Pictures
472
     * @link http://msdn.microsoft.com/en-us/library/dd920746(v=office.12).aspx
473
     */
474 4
    private function loadPicturesStream()
475
    {
476 4
        $stream = $this->streamPictures;
477
478 4
        $pos = 0;
479
        do {
480 4
            $arrayRH = $this->loadRecordHeader($stream, $pos);
481 4
            $pos += 8;
482 4
            $readSuccess = false;
483 4
            if ($arrayRH['recVer'] == 0x00 && ($arrayRH['recType'] == 0xF007 || ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117))) {
484
                //@link : http://msdn.microsoft.com/en-us/library/dd950560(v=office.12).aspx
485 4
                if ($arrayRH['recType'] == 0xF007) {
486
                    // OfficeArtFBSE
487
                    throw new \Exception('Feature not implemented (l.'.__LINE__.')');
488
                }
489 4
                if ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117) {
490 4
                    $arrayRecord = $this->readRecordOfficeArtBlip($stream, $pos - 8);
491 4
                    if ($arrayRecord['length'] > 0) {
492 4
                        $pos += $arrayRecord['length'];
493 4
                        $this->arrayPictures[] = $arrayRecord['picture'];
494 4
                    }
495 4
                }
496 4
                $readSuccess = true;
497 4
            }
498 4
        } while ($readSuccess === true);
499 4
    }
500
501
    /**
502
     * Stream Current User
503
     * @link http://msdn.microsoft.com/en-us/library/dd908567(v=office.12).aspx
504
     */
505 4
    private function loadCurrentUserStream()
506
    {
507 4
        $pos = 0;
508
509
        /**
510
         * CurrentUserAtom : http://msdn.microsoft.com/en-us/library/dd948895(v=office.12).aspx
511
         */
512
        // RecordHeader : http://msdn.microsoft.com/en-us/library/dd926377(v=office.12).aspx
513 4
        $rHeader = $this->loadRecordHeader($this->streamCurrentUser, $pos);
514 4
        $pos += 8;
515 4
        if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_CURRENTUSERATOM) {
516
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > RecordHeader).');
517
        }
518
519
        // Size
520 4
        $size = self::getInt4d($this->streamCurrentUser, $pos);
521 4
        $pos += 4;
522 4
        if ($size !=  0x00000014) {
523
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > Size).');
524
        }
525
526
        // headerToken
527 4
        $headerToken = self::getInt4d($this->streamCurrentUser, $pos);
528 4
        $pos += 4;
529 4
        if ($headerToken == 0xF3D1C4DF && $headerToken != 0xE391C05F) {
530
            throw new \Exception('Feature not implemented (l.'.__LINE__.') : Encrypted file');
531
        }
532
533
        // offsetToCurrentEdit
534 4
        $this->offsetToCurrentEdit = self::getInt4d($this->streamCurrentUser, $pos);
535 4
        $pos += 4;
536
537
        // lenUserName
538 4
        $lenUserName = self::getInt2d($this->streamCurrentUser, $pos);
539 4
        $pos += 2;
540 4
        if ($lenUserName > 255) {
541
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > lenUserName).');
542
        }
543
544
        // docFileVersion
545 4
        $docFileVersion = self::getInt2d($this->streamCurrentUser, $pos);
546 4
        $pos += 2;
547 4
        if ($docFileVersion != 0x03F4) {
548
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > docFileVersion).');
549
        }
550
551
        // majorVersion
552 4
        $majorVersion = self::getInt1d($this->streamCurrentUser, $pos);
553 4
        $pos += 1;
554 4
        if ($majorVersion != 0x03) {
555
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > majorVersion).');
556
        }
557
558
        // minorVersion
559 4
        $minorVersion = self::getInt1d($this->streamCurrentUser, $pos);
560 4
        $pos += 1;
561 4
        if ($minorVersion != 0x00) {
562
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > minorVersion).');
563
        }
564
565
        // unused
566 4
        $pos += 2;
567
568
        // ansiUserName
569 4
        $ansiUserName = '';
570
        do {
571 4
            $char = self::getInt1d($this->streamCurrentUser, $pos);
572 4
            if (($char >= 0x00 && $char <= 0x1F) || ($char >= 0x7F && $char <= 0x9F)) {
573 4
                $char = false;
574 4
            } else {
575 4
                $ansiUserName .= chr($char);
576 4
                $pos += 1;
577
            }
578 4
        } while ($char !== false);
579
580
        // relVersion
581 4
        $relVersion = self::getInt4d($this->streamCurrentUser, $pos);
582 4
        $pos += 4;
583 4
        if ($relVersion != 0x00000008 && $relVersion != 0x00000009) {
584
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > relVersion).');
585
        }
586
587
        // unicodeUserName
588 4
        $unicodeUserName = '';
589 4
        for ($inc = 0; $inc < $lenUserName; $inc++) {
590 4
            $char = self::getInt2d($this->streamCurrentUser, $pos);
591 4
            if (($char >= 0x00 && $char <= 0x1F) || ($char >= 0x7F && $char <= 0x9F)) {
592 4
                break;
593
            }
594
            $unicodeUserName .= chr($char);
595
            $pos += 2;
596
        }
597 4
    }
598
599
    /**
600
     * Stream Powerpoint Document
601
     * @link http://msdn.microsoft.com/en-us/library/dd921564(v=office.12).aspx
602
     */
603 4
    private function loadPowerpointDocumentStream()
604
    {
605 4
        $this->readRecordUserEditAtom($this->streamPowerpointDocument, $this->offsetToCurrentEdit);
606
607 4
        $this->readRecordPersistDirectoryAtom($this->streamPowerpointDocument, $this->offsetPersistDirectory);
608
609 4
        foreach ($this->rgPersistDirEntry as $offsetDir) {
610 4
            $pos = $offsetDir;
611
612 4
            $rh = $this->loadRecordHeader($this->streamPowerpointDocument, $pos);
613 4
            $pos += 8;
614 4
            $this->inMainType = $rh['recType'];
615 4
            $this->currentNote = null;
616 4
            switch ($rh['recType']) {
617 4
                case self::RT_DOCUMENT:
618 4
                    $this->readRecordDocumentContainer($this->streamPowerpointDocument, $pos);
619 4
                    break;
620 4
                case self::RT_NOTES:
621 4
                    $this->readRecordNotesContainer($this->streamPowerpointDocument, $pos);
622 3
                    break;
623 4
                case self::RT_SLIDE:
624 3
                    $this->readRecordSlideContainer($this->streamPowerpointDocument, $pos);
625 3
                    break;
626 4
                case self::RT_MAINMASTER:
627 4
                    break;
628
                default:
629
                    throw new \Exception('Feature not implemented : l.' . __LINE__ . '(' . dechex($rh['recType']) . ')');
630
                    break;
0 ignored issues
show
Unused Code introduced by
break; 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...
631 4
            }
632 4
        }
633 3
    }
634
635
    /**
636
     * Read a record header
637
     * @param string $stream
638
     * @param integer $pos
639
     * @return array
640
     */
641 4
    private function loadRecordHeader($stream, $pos)
642
    {
643 4
        $rec = self::getInt2d($stream, $pos);
644 4
        $recType = self::getInt2d($stream, $pos + 2);
645 4
        $recLen = self::getInt4d($stream, $pos + 4);
646
        return array(
647 4
            'recVer' => ($rec >> 0) & bindec('1111'),
648 4
            'recInstance' => ($rec >> 4) & bindec('111111111111'),
649 4
            'recType' => $recType,
650 4
            'recLen' => $recLen,
651 4
        );
652
    }
653
654
    /**
655
     * Read 8-bit unsigned integer
656
     *
657
     * @param string $data
658
     * @param int $pos
659
     * @return int
660
     */
661 4
    public static function getInt1d($data, $pos)
662
    {
663 4
        return ord($data[$pos]);
664
    }
665
666
    /**
667
     * Read 16-bit unsigned integer
668
     *
669
     * @param string $data
670
     * @param int $pos
671
     * @return int
672
     */
673 4
    public static function getInt2d($data, $pos)
674
    {
675 4
        return ord($data[$pos]) | (ord($data[$pos+1]) << 8);
676
    }
677
678
    /**
679
     * Read 32-bit signed integer
680
     *
681
     * @param string $data
682
     * @param int $pos
683
     * @return int
684
     */
685 4
    public static function getInt4d($data, $pos)
686
    {
687
        // FIX: represent numbers correctly on 64-bit system
688
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
689
        // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
690 4
        $or24 = ord($data[$pos + 3]);
691
692 4
        $ord24 = ($or24 & 127) << 24;
693 4
        if ($or24 >= 128) {
694
            // negative number
695 4
            $ord24 = -abs((256 - $or24) << 24);
696 4
        }
697 4
        return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $ord24;
698
    }
699
700
    /**
701
     * A container record that specifies the animation and sound information for a shape.
702
     * @param string $stream
703
     * @param integer $pos
704
     * @link https://msdn.microsoft.com/en-us/library/dd772900(v=office.12).aspx
705
     */
706 3
    private function readRecordAnimationInfoContainer($stream, $pos)
707
    {
708
        $arrayReturn = array(
709 3
            'length' => 0,
710 3
        );
711
712 3
        $data = $this->loadRecordHeader($stream, $pos);
713 3
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ANIMATIONINFO) {
714
            // Record Header
715
            $arrayReturn['length'] += 8;
716
            // animationAtom
717
            // animationSound
718
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
719
        }
720
721 3
        return $arrayReturn;
722
    }
723
724
    /**
725
     * A container record that specifies information about the document.
726
     * @param string $stream
727
     * @param integer $pos
728
     * @link http://msdn.microsoft.com/en-us/library/dd947357(v=office.12).aspx
729
     */
730 4
    private function readRecordDocumentContainer($stream, $pos)
731
    {
732 4
        $documentAtom = $this->loadRecordHeader($stream, $pos);
733 4
        $pos += 8;
734 4
        if ($documentAtom['recVer'] != 0x1 || $documentAtom['recInstance'] != 0x000 || $documentAtom['recType'] != self::RT_DOCUMENTATOM) {
735
            throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom).');
736
        }
737 4
        $pos += $documentAtom['recLen'];
738
739 4
        $exObjList = $this->loadRecordHeader($stream, $pos);
740 4
        if ($exObjList['recVer'] == 0xF && $exObjList['recInstance'] == 0x000 && $exObjList['recType'] == self::RT_EXTERNALOBJECTLIST) {
741 1
            $pos += 8;
742
            // exObjListAtom > rh
743 1
            $exObjListAtom = $this->loadRecordHeader($stream, $pos);
744 1
            if ($exObjListAtom['recVer'] != 0x0 || $exObjListAtom['recInstance'] != 0x000 || $exObjListAtom['recType'] != self::RT_EXTERNALOBJECTLISTATOM || $exObjListAtom['recLen'] != 0x00000004) {
745
                throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom > exObjList > exObjListAtom).');
746
            }
747 1
            $pos += 8;
748
            // exObjListAtom > exObjIdSeed
749 1
            $pos += 4;
750
            // rgChildRec
751 1
            $exObjList['recLen'] -= 12;
752
            do {
753 1
                $childRec = $this->loadRecordHeader($stream, $pos);
754 1
                $pos += 8;
755 1
                $exObjList['recLen'] -= 8;
756 1
                switch ($childRec['recType']) {
757 1
                    case self::RT_EXTERNALHYPERLINK:
758
                        //@link : http://msdn.microsoft.com/en-us/library/dd944995(v=office.12).aspx
759
                        // exHyperlinkAtom > rh
760 1
                        $exHyperlinkAtom = $this->loadRecordHeader($stream, $pos);
761 1
                        if ($exHyperlinkAtom['recVer'] != 0x0 || $exHyperlinkAtom['recInstance'] != 0x000 || $exHyperlinkAtom['recType'] != self::RT_EXTERNALHYPERLINKATOM || $exObjListAtom['recLen'] != 0x00000004) {
762
                            throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom > exObjList > rgChildRec > RT_ExternalHyperlink).');
763
                        }
764 1
                        $pos += 8;
765 1
                        $exObjList['recLen'] -= 8;
766
                        // exHyperlinkAtom > exHyperlinkId
767 1
                        $exHyperlinkId = self::getInt4d($stream, $pos);
768 1
                        $pos += 4;
769 1
                        $exObjList['recLen'] -= 4;
770
771 1
                        $this->arrayHyperlinks[$exHyperlinkId] = array();
772
                        // friendlyNameAtom
773 1
                        $friendlyNameAtom  = $this->loadRecordHeader($stream, $pos);
774 1
                        if ($friendlyNameAtom['recVer'] == 0x0 && $friendlyNameAtom['recInstance'] == 0x000 && $friendlyNameAtom['recType'] == self::RT_CSTRING && $friendlyNameAtom['recLen'] % 2 == 0) {
775 1
                            $pos += 8;
776 1
                            $exObjList['recLen'] -= 8;
777 1
                            $this->arrayHyperlinks[$exHyperlinkId]['text'] = '';
778 1
                            for ($inc = 0; $inc < ($friendlyNameAtom['recLen'] / 2); $inc++) {
779 1
                                $char = self::getInt2d($stream, $pos);
780 1
                                $pos += 2;
781 1
                                $exObjList['recLen'] -= 2;
782 1
                                $this->arrayHyperlinks[$exHyperlinkId]['text'] .= chr($char);
783 1
                            }
784 1
                        }
785
                        // targetAtom
786 1
                        $targetAtom  = $this->loadRecordHeader($stream, $pos);
787 1
                        if ($targetAtom['recVer'] == 0x0 && $targetAtom['recInstance'] == 0x001 && $targetAtom['recType'] == self::RT_CSTRING && $targetAtom['recLen'] % 2 == 0) {
788 1
                            $pos += 8;
789 1
                            $exObjList['recLen'] -= 8;
790 1
                            $this->arrayHyperlinks[$exHyperlinkId]['url'] = '';
791 1
                            for ($inc = 0; $inc < ($targetAtom['recLen'] / 2); $inc++) {
792 1
                                $char = self::getInt2d($stream, $pos);
793 1
                                $pos += 2;
794 1
                                $exObjList['recLen'] -= 2;
795 1
                                $this->arrayHyperlinks[$exHyperlinkId]['url'] .= chr($char);
796 1
                            }
797 1
                        }
798
                        // locationAtom
799 1
                        $locationAtom  = $this->loadRecordHeader($stream, $pos);
800 1
                        if ($locationAtom['recVer'] == 0x0 && $locationAtom['recInstance'] == 0x003 && $locationAtom['recType'] == self::RT_CSTRING && $locationAtom['recLen'] % 2 == 0) {
801
                            $pos += 8;
802
                            $exObjList['recLen'] -= 8;
803
                            $string = '';
804
                            for ($inc = 0; $inc < ($locationAtom['recLen'] / 2); $inc++) {
805
                                $char = self::getInt2d($stream, $pos);
806
                                $pos += 2;
807
                                $exObjList['recLen'] -= 2;
808
                                $string .= chr($char);
809
                            }
810
                        }
811 1
                        break;
812
                    default:
813
                        throw new \Exception('Feature not implemented (l.'.__LINE__.' : '.dechex($childRec['recType'].')'));
814 1
                }
815 1
            } while ($exObjList['recLen'] > 0);
816 1
        }
817
818
        //@link : http://msdn.microsoft.com/en-us/library/dd907813(v=office.12).aspx
819 4
        $documentTextInfo = $this->loadRecordHeader($stream, $pos);
820 4
        if ($documentTextInfo['recVer'] == 0xF && $documentTextInfo['recInstance'] == 0x000 && $documentTextInfo['recType'] == self::RT_ENVIRONMENT) {
821 4
            $pos += 8;
822
            //@link : http://msdn.microsoft.com/en-us/library/dd952717(v=office.12).aspx
823 4
            $kinsoku = $this->loadRecordHeader($stream, $pos);
824 4
            if ($kinsoku['recVer'] == 0xF && $kinsoku['recInstance'] == 0x002 && $kinsoku['recType'] == self::RT_KINSOKU) {
825 4
                $pos += 8;
826 4
                $pos += $kinsoku['recLen'];
827 4
            }
828
829
            //@link : http://msdn.microsoft.com/en-us/library/dd948152(v=office.12).aspx
830 4
            $fontCollection = $this->loadRecordHeader($stream, $pos);
831 4
            if ($fontCollection['recVer'] == 0xF && $fontCollection['recInstance'] == 0x000 && $fontCollection['recType'] == self::RT_FONTCOLLECTION) {
832 4
                $pos += 8;
833
                do {
834 4
                    $fontEntityAtom = $this->loadRecordHeader($stream, $pos);
835 4
                    $pos += 8;
836 4
                    $fontCollection['recLen'] -= 8;
837 4
                    if ($fontEntityAtom['recVer'] != 0x0 || $fontEntityAtom['recInstance'] > 128 || $fontEntityAtom['recType'] != self::RT_FONTENTITYATOM) {
838
                        throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > RT_Environment > RT_FontCollection > RT_FontEntityAtom).');
839
                    }
840 4
                    $string = '';
841 4
                    for ($inc = 0; $inc < 32; $inc++) {
842 4
                        $char = self::getInt2d($stream, $pos);
843 4
                        $pos += 2;
844 4
                        $fontCollection['recLen'] -= 2;
845 4
                        $string .= chr($char);
846 4
                    }
847 4
                    $this->arrayFonts[] = $string;
848
849
                    // lfCharSet (1 byte)
850 4
                    $pos += 1;
851 4
                    $fontCollection['recLen'] -= 1;
852
853
                    // fEmbedSubsetted (1 bit)
854
                    // unused (7 bits)
855 4
                    $pos += 1;
856 4
                    $fontCollection['recLen'] -= 1;
857
858
                    // rasterFontType (1 bit)
859
                    // deviceFontType (1 bit)
860
                    // truetypeFontType (1 bit)
861
                    // fNoFontSubstitution (1 bit)
862
                    // reserved (4 bits)
863 4
                    $pos += 1;
864 4
                    $fontCollection['recLen'] -= 1;
865
866
                    // lfPitchAndFamily (1 byte)
867 4
                    $pos += 1;
868 4
                    $fontCollection['recLen'] -= 1;
869
870 4
                    $fontEmbedData1 = $this->loadRecordHeader($stream, $pos);
871 4
                    if ($fontEmbedData1['recVer'] == 0x0 && $fontEmbedData1['recInstance'] >= 0x000 && $fontEmbedData1['recInstance'] <= 0x003 && $fontEmbedData1['recType'] == self::RT_FONTEMBEDDATABLOB) {
872
                        $pos += 8;
873
                        $fontCollection['recLen'] -= 8;
874
                        $pos += $fontEmbedData1['recLen'];
875
                        $fontCollection['recLen'] -= $fontEmbedData1['recLen'];
876
                    }
877
878 4
                    $fontEmbedData2 = $this->loadRecordHeader($stream, $pos);
879 4
                    if ($fontEmbedData2['recVer'] == 0x0 && $fontEmbedData2['recInstance'] >= 0x000 && $fontEmbedData2['recInstance'] <= 0x003 && $fontEmbedData2['recType'] == self::RT_FONTEMBEDDATABLOB) {
880
                        $pos += 8;
881
                        $fontCollection['recLen'] -= 8;
882
                        $pos += $fontEmbedData2['recLen'];
883
                        $fontCollection['recLen'] -= $fontEmbedData2['recLen'];
884
                    }
885
886 4
                    $fontEmbedData3 = $this->loadRecordHeader($stream, $pos);
887 4
                    if ($fontEmbedData3['recVer'] == 0x0 && $fontEmbedData3['recInstance'] >= 0x000 && $fontEmbedData3['recInstance'] <= 0x003 && $fontEmbedData3['recType'] == self::RT_FONTEMBEDDATABLOB) {
888
                        $pos += 8;
889
                        $fontCollection['recLen'] -= 8;
890
                        $pos += $fontEmbedData3['recLen'];
891
                        $fontCollection['recLen'] -= $fontEmbedData3['recLen'];
892
                    }
893
894 4
                    $fontEmbedData4 = $this->loadRecordHeader($stream, $pos);
895 4
                    if ($fontEmbedData4['recVer'] == 0x0 && $fontEmbedData4['recInstance'] >= 0x000 && $fontEmbedData4['recInstance'] <= 0x003 && $fontEmbedData4['recType'] == self::RT_FONTEMBEDDATABLOB) {
896
                        $pos += 8;
897
                        $fontCollection['recLen'] -= 8;
898
                        $pos += $fontEmbedData4['recLen'];
899
                        $fontCollection['recLen'] -= $fontEmbedData4['recLen'];
900
                    }
901 4
                } while ($fontCollection['recLen'] > 0);
902 4
            }
903
904 4
            $textCFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
905 4
            if ($textCFDefaultsAtom['recVer'] == 0x0 && $textCFDefaultsAtom['recInstance'] == 0x000 && $textCFDefaultsAtom['recType'] == self::RT_TEXTCHARFORMATEXCEPTIONATOM) {
906 4
                $pos += 8;
907 4
                $pos += $textCFDefaultsAtom['recLen'];
908 4
            }
909
910 4
            $textPFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
911 4
            if ($textPFDefaultsAtom['recVer'] == 0x0 && $textPFDefaultsAtom['recInstance'] == 0x000 && $textPFDefaultsAtom['recType'] == self::RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM) {
912
                $pos += 8;
913
                $pos += $textPFDefaultsAtom['recLen'];
914
            }
915
916 4
            $defaultRulerAtom = $this->loadRecordHeader($stream, $pos);
917 4
            if ($defaultRulerAtom['recVer'] == 0x0 && $defaultRulerAtom['recInstance'] == 0x000 && $defaultRulerAtom['recType'] == self::RT_DEFAULTRULERATOM) {
918
                $pos += 8;
919
                $pos += $defaultRulerAtom['recLen'];
920
            }
921
922 4
            $textSIDefaultsAtom = $this->loadRecordHeader($stream, $pos);
923 4
            if ($textSIDefaultsAtom['recVer'] == 0x0 && $textSIDefaultsAtom['recInstance'] == 0x000 && $textSIDefaultsAtom['recType'] == self::RT_TEXTSPECIALINFODEFAULTATOM) {
924 4
                $pos += 8;
925 4
                $pos += $textSIDefaultsAtom['recLen'];
926 4
            }
927
928 4
            $textMasterStyleAtom = $this->loadRecordHeader($stream, $pos);
929 4
            if ($textMasterStyleAtom['recVer'] == 0x0 && $textMasterStyleAtom['recType'] == self::RT_TEXTMASTERSTYLEATOM) {
930 4
                $pos += 8;
931 4
                $pos += $textMasterStyleAtom['recLen'];
932 4
            }
933 4
        }
934
935 4
        $soundCollection = $this->loadRecordHeader($stream, $pos);
936 4
        if ($soundCollection['recVer'] == 0xF && $soundCollection['recInstance'] == 0x005 && $soundCollection['recType'] == self::RT_SOUNDCOLLECTION) {
937
            $pos += 8;
938
            $pos += $soundCollection['recLen'];
939
        }
940
941 4
        $drawingGroup = $this->loadRecordHeader($stream, $pos);
942 4
        if ($drawingGroup['recVer'] == 0xF && $drawingGroup['recInstance'] == 0x000 && $drawingGroup['recType'] == self::RT_DRAWINGGROUP) {
943 4
            $drawing = $this->readRecordDrawingGroupContainer($stream, $pos);
944 4
            $pos += 8;
945 4
            $pos += $drawing['length'];
946 4
        }
947
948 4
        $masterList = $this->loadRecordHeader($stream, $pos);
949 4
        if ($masterList['recVer'] == 0xF && $masterList['recInstance'] == 0x001 && $masterList['recType'] == self::RT_SLIDELISTWITHTEXT) {
950
            $pos += 8;
951
            $pos += $masterList['recLen'];
952
        }
953
954 4
        $docInfoList = $this->loadRecordHeader($stream, $pos);
955 4
        if ($docInfoList['recVer'] == 0xF && $docInfoList['recInstance'] == 0x000 && $docInfoList['recType'] == self::RT_LIST) {
956
            $pos += 8;
957
            $pos += $docInfoList['recLen'];
958
        }
959
960 4
        $slideHF = $this->loadRecordHeader($stream, $pos);
961 4
        if ($slideHF['recVer'] == 0xF && $slideHF['recInstance'] == 0x003 && $slideHF['recType'] == self::RT_HEADERSFOOTERS) {
962
            $pos += 8;
963
            $pos += $slideHF['recLen'];
964
        }
965
966 4
        $notesHF = $this->loadRecordHeader($stream, $pos);
967 4
        if ($notesHF['recVer'] == 0xF && $notesHF['recInstance'] == 0x004 && $notesHF['recType'] == self::RT_HEADERSFOOTERS) {
968
            $pos += 8;
969
            $pos += $notesHF['recLen'];
970
        }
971
972
        // SlideListWithTextContainer
973 4
        $slideList = $this->loadRecordHeader($stream, $pos);
974 4
        if ($slideList['recVer'] == 0xF && $slideList['recInstance'] == 0x000 && $slideList['recType'] == self::RT_SLIDELISTWITHTEXT) {
975
            $pos += 8;
976
            do {
977
                // SlideListWithTextSubContainerOrAtom
978
                $rhSlideList = $this->loadRecordHeader($stream, $pos);
979
                if ($rhSlideList['recVer'] == 0x0 && $rhSlideList['recInstance'] == 0x000 && $rhSlideList['recType'] == self::RT_SLIDEPERSISTATOM && $rhSlideList['recLen'] == 0x00000014) {
980
                    $pos += 8;
981
                    $slideList['recLen'] -= 8;
982
                    // persistIdRef
983
                    $pos += 4;
984
                    $slideList['recLen'] -= 4;
985
                    // reserved1 - fShouldCollapse - fNonOutlineData - reserved2
986
                    $pos += 4;
987
                    $slideList['recLen'] -= 4;
988
                    // cTexts
989
                    $pos += 4;
990
                    $slideList['recLen'] -= 4;
991
                    // slideId
992
                    $slideId = self::getInt4d($stream, $pos);
993
                    if ($slideId == -2147483648) {
994
                        $slideId = 0;
995
                    }
996
                    if ($slideId > 0) {
997
                        $this->arrayNotes[$this->oPhpPresentation->getActiveSlideIndex()] = $slideId;
998
                    }
999
                    $pos += 4;
1000
                    $slideList['recLen'] -= 4;
1001
                    // reserved3
1002
                    $pos += 4;
1003
                    $slideList['recLen'] -= 4;
1004
                }
1005
            } while ($slideList['recLen'] > 0);
1006
        }
1007 4
    }
1008
1009
    /**
1010
     * An atom record that specifies information about a slide.
1011
     * @param string $stream
1012
     * @param integer $pos
1013
     * @return array
1014
     * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
1015
     */
1016 4
    private function readRecordDrawingContainer($stream, $pos)
1017
    {
1018
        $arrayReturn = array(
1019 4
            'length' => 0,
1020 4
        );
1021
1022 4
        $data = $this->loadRecordHeader($stream, $pos);
1023 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_DRAWING) {
1024
            // Record Header
1025 4
            $arrayReturn['length'] += 8;
1026
1027 4
            $officeArtDg = $this->readRecordOfficeArtDgContainer($stream, $pos + $arrayReturn['length']);
1028 3
            $arrayReturn['length'] += $officeArtDg['length'];
1029 3
        }
1030 3
        return $arrayReturn;
1031
    }
1032
1033 4
    private function readRecordDrawingGroupContainer($stream, $pos)
0 ignored issues
show
Unused Code introduced by
This method is not used, and could be removed.
Loading history...
1034
    {
1035
1036
        $arrayReturn = array(
1037 4
            'length' => 0,
1038 4
        );
1039
1040 4
        $data = $this->loadRecordHeader($stream, $pos);
1041 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_DRAWINGGROUP) {
1042
            // Record Header
1043 4
            $arrayReturn['length'] += 8;
1044 4
            $arrayReturn['length'] += $data['recLen'];
1045 4
        }
1046 4
        return $arrayReturn;
1047
    }
1048
1049
    /**
1050
     * An atom record that specifies a reference to an external object.
1051
     * @param string $stream
1052
     * @param integer $pos
1053
     * @return array
1054
     * @link https://msdn.microsoft.com/en-us/library/dd910388(v=office.12).aspx
1055
     */
1056 3
    private function readRecordExObjRefAtom($stream, $pos)
1057
    {
1058
        $arrayReturn = array(
1059 3
            'length' => 0,
1060 3
        );
1061
1062 3
        $data = $this->loadRecordHeader($stream, $pos);
1063 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_EXTERNALOBJECTREFATOM && $data['recLen'] == 0x00000004) {
1064
            // Record Header
1065
            $arrayReturn['length'] += 8;
1066
            // Datas
1067
            $arrayReturn['length'] += $data['recLen'];
1068
        }
1069
1070 3
        return $arrayReturn;
1071
    }
1072
1073
    /**
1074
     * An atom record that specifies a type of action to be performed.
1075
     * @param string $stream
1076
     * @param integer $pos
1077
     * @return array
1078
     * @link https://msdn.microsoft.com/en-us/library/dd953300(v=office.12).aspx
1079
     */
1080 1
    private function readRecordInteractiveInfoAtom($stream, $pos)
1081
    {
1082
        $arrayReturn = array(
1083 1
            'length' => 0,
1084 1
        );
1085
1086 1
        $data = $this->loadRecordHeader($stream, $pos);
1087 1
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_INTERACTIVEINFOATOM && $data['recLen'] == 0x00000010) {
1088
            // Record Header
1089 1
            $arrayReturn['length'] += 8;
1090
            // soundIdRef
1091 1
            $arrayReturn['length'] += 4;
1092
            // exHyperlinkIdRef
1093 1
            $arrayReturn['exHyperlinkIdRef'] = self::getInt4d($stream, $pos + $arrayReturn['length']);
1094 1
            $arrayReturn['length'] += 4;
1095
            // action
1096 1
            $arrayReturn['length'] += 1;
1097
            // oleVerb
1098 1
            $arrayReturn['length'] += 1;
1099
            // jump
1100 1
            $arrayReturn['length'] += 1;
1101
            // fAnimated (1 bit)
1102
            // fStopSound (1 bit)
1103
            // fCustomShowReturn (1 bit)
1104
            // fVisited (1 bit)
1105
            // reserved (4 bits)
1106 1
            $arrayReturn['length'] += 1;
1107
            // hyperlinkType
1108 1
            $arrayReturn['length'] += 1;
1109
            // unused
1110 1
            $arrayReturn['length'] += 3;
1111 1
        }
1112
1113 1
        return $arrayReturn;
1114
    }
1115
1116
    /**
1117
     * An atom record that specifies the name of a macro, a file name, or a named show.
1118
     * @param string $stream
1119
     * @param integer $pos
1120
     * @return array
1121
     * @link https://msdn.microsoft.com/en-us/library/dd925121(v=office.12).aspx
1122
     */
1123 1
    private function readRecordMacroNameAtom($stream, $pos)
1124
    {
1125
        $arrayReturn = array(
1126 1
            'length' => 0,
1127 1
        );
1128
1129 1
        $data = $this->loadRecordHeader($stream, $pos);
1130 1
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x002 && $data['recType'] == self::RT_CSTRING && $data['recLen'] % 2 == 0) {
1131
            // Record Header
1132
            $arrayReturn['length'] += 8;
1133
            // Datas
1134
            $arrayReturn['length'] += $data['recLen'];
1135
        }
1136
1137 1
        return $arrayReturn;
1138
    }
1139
1140
    /**
1141
     * A container record that specifies what actions to perform when interacting with an object by means of a mouse click.
1142
     * @param string $stream
1143
     * @param integer $pos
1144
     * @return array
1145
     * @link https://msdn.microsoft.com/en-us/library/dd952348(v=office.12).aspx
1146
     */
1147 3
    private function readRecordMouseClickInteractiveInfoContainer($stream, $pos)
1148
    {
1149
        $arrayReturn = array(
1150 3
            'length' => 0,
1151 3
        );
1152
1153 3
        $data = $this->loadRecordHeader($stream, $pos);
1154 3
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_INTERACTIVEINFO) {
1155
            // Record Header
1156 1
            $arrayReturn['length'] += 8;
1157
            // interactiveInfoAtom
1158 1
            $interactiveInfoAtom = $this->readRecordInteractiveInfoAtom($stream, $pos + $arrayReturn['length']);
1159 1
            $arrayReturn['length'] += $interactiveInfoAtom['length'];
1160 1
            if ($interactiveInfoAtom['length'] > 0) {
1161 1
                $arrayReturn['exHyperlinkIdRef'] = $interactiveInfoAtom['exHyperlinkIdRef'];
1162 1
            }
1163
            // macroNameAtom
1164 1
            $macroNameAtom = $this->readRecordMacroNameAtom($stream, $pos + $arrayReturn['length']);
1165 1
            $arrayReturn['length'] += $macroNameAtom['length'];
1166 1
        }
1167
1168 3
        return $arrayReturn;
1169
    }
1170
1171
    /**
1172
     * A container record that specifies what actions to perform when interacting with an object by moving the mouse cursor over it.
1173
     * @param string $stream
1174
     * @param integer $pos
1175
     * @return array
1176
     * @throws \Exception
1177
     * @link https://msdn.microsoft.com/en-us/library/dd925811(v=office.12).aspx
1178
     */
1179 3
    private function readRecordMouseOverInteractiveInfoContainer($stream, $pos)
1180
    {
1181
        $arrayReturn = array(
1182 3
            'length' => 0,
1183 3
        );
1184
1185 3
        $data = $this->loadRecordHeader($stream, $pos);
1186 3
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x001 && $data['recType'] == self::RT_INTERACTIVEINFO) {
1187
            // Record Header
1188
            $arrayReturn['length'] += 8;
1189
            // interactiveInfoAtom
1190
            // macroNameAtom
1191
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
1192
        }
1193
1194 3
        return $arrayReturn;
1195
    }
1196
1197
    /**
1198
     * The OfficeArtBlip record specifies BLIP file data.
1199
     * @param string $stream
1200
     * @param integer $pos
1201
     * @return array
1202
     * @throws \Exception
1203
     * @link https://msdn.microsoft.com/en-us/library/dd910081(v=office.12).aspx
1204
     */
1205 4
    private function readRecordOfficeArtBlip($stream, $pos)
1206
    {
1207
        $arrayReturn = array(
1208 4
            'length' => 0,
1209
            'picture' => null
1210 4
        );
1211
1212 4
        $data = $this->loadRecordHeader($stream, $pos);
1213 4
        if ($data['recVer'] == 0x0 && ($data['recType'] >= 0xF018 && $data['recType'] <= 0xF117)) {
1214
            // Record Header
1215 4
            $arrayReturn['length'] += 8;
1216
            // Datas
1217 4
            switch ($data['recType']) {
1218 4
                case self::OFFICEARTBLIPJPG:
1219 4
                case self::OFFICEARTBLIPPNG:
1220
                    // rgbUid1
1221 4
                    $arrayReturn['length'] += 16;
1222 4
                    $data['recLen'] -= 16;
1223 4
                    if ($data['recInstance'] == 0x6E1) {
1224
                        // rgbUid2
1225
                        $arrayReturn['length'] += 16;
1226
                        $data['recLen'] -= 16;
1227
                    }
1228
                    // tag
1229 4
                    $arrayReturn['length'] += 1;
1230 4
                    $data['recLen'] -= 1;
1231
                    // BLIPFileData
1232 4
                    $arrayReturn['picture'] = substr($this->streamPictures, $pos + $arrayReturn['length'], $data['recLen']);
1233 4
                    $arrayReturn['length'] += $data['recLen'];
1234 4
                    break;
1235
                default:
1236
                    throw new \Exception('Feature not implemented (l.'.__LINE__.' : '.dechex($data['recType'].')'));
1237 4
            }
1238 4
        }
1239
1240 4
        return $arrayReturn;
1241
    }
1242
1243
    /**
1244
     * The OfficeArtChildAnchor record specifies four signed integers that specify the anchor for the shape that contains this record.
1245
     * @param string $stream
1246
     * @param integer $pos
1247
     * @return array
1248
     * @link https://msdn.microsoft.com/en-us/library/dd922720(v=office.12).aspx
1249
     */
1250 4
    private function readRecordOfficeArtChildAnchor($stream, $pos)
1251
    {
1252
        $arrayReturn = array(
1253
            'length' => 0
1254 4
        );
1255
1256 4
        $data = $this->loadRecordHeader($stream, $pos);
1257 4
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF00F && $data['recLen'] == 0x00000010) {
1258
            // Record Header
1259
            $arrayReturn['length'] += 8;
1260
            // Datas
1261
            $arrayReturn['left'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']);
1262
            $arrayReturn['length'] += 4;
1263
            $arrayReturn['top'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']);
1264
            $arrayReturn['length'] += 4;
1265
            $arrayReturn['width'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']) - $arrayReturn['left'];
1266
            $arrayReturn['length'] += 4;
1267
            $arrayReturn['height'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']) - $arrayReturn['top'];
1268
            $arrayReturn['length'] += 4;
1269
        }
1270
1271 4
        return $arrayReturn;
1272
    }
1273
1274
    /**
1275
     * An atom record that specifies the location of a shape.
1276
     * @param string $stream
1277
     * @param integer $pos
1278
     * @return array
1279
     * @throws \Exception
1280
     * @link https://msdn.microsoft.com/en-us/library/dd922797(v=office.12).aspx
1281
     */
1282 4
    private function readRecordOfficeArtClientAnchor($stream, $pos)
1283
    {
1284
        $arrayReturn = array(
1285
            'length' => 0
1286 4
        );
1287
1288 4
        $data = $this->loadRecordHeader($stream, $pos);
1289 4
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF010 && ($data['recLen'] == 0x00000008 || $data['recLen'] == 0x00000010)) {
1290
            // Record Header
1291 3
            $arrayReturn['length'] += 8;
1292
            // Datas
1293 3
            switch ($data['recLen']) {
1294 3
                case 0x00000008:
1295 3
                    $arrayReturn['top'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6);
1296 3
                    $arrayReturn['length'] += 2;
1297 3
                    $arrayReturn['left'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6);
1298 3
                    $arrayReturn['length'] += 2;
1299 3
                    $arrayReturn['width'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6) - $arrayReturn['left'];
1300 3
                    $arrayReturn['length'] += 2;
1301 3
                    $arrayReturn['height'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6) - $arrayReturn['left'];
1302 3
                    $arrayReturn['length'] += 2;
1303 3
                    $pos += 8;
1304 3
                    break;
1305
                case 0x00000010:
1306
                    throw new \Exception('PowerPoint97 Reader : record OfficeArtClientAnchor (0x00000010)');
1307 3
            }
1308 3
        }
1309
1310 4
        return $arrayReturn;
1311
    }
1312
1313
    /**
1314
     * A container record that specifies text related data for a shape.
1315
     * @param string $stream
1316
     * @param integer $pos
1317
     * @return array
1318
     * @link https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
1319
     */
1320 4
    private function readRecordOfficeArtClientTextbox($stream, $pos)
1321
    {
1322
        $arrayReturn = array(
1323 4
            'length' => 0,
1324 4
            'text' => '',
1325 4
            'numParts' => 0,
1326 4
            'numTexts' => 0,
1327 4
            'hyperlink' => array(),
1328 4
        );
1329
1330 4
        $data = $this->loadRecordHeader($stream, $pos);
1331
        // recVer 0xF
1332
        // Doc : 0x0    https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
1333
        // Sample : 0xF https://msdn.microsoft.com/en-us/library/dd953497(v=office.12).aspx
1334 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF00D) {
1335
            // Record Header
1336 3
            $arrayReturn['length'] += 8;
1337
            // Datas
1338 3
            $strLen = 0;
1339
            do {
1340 3
                $rhChild = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
1341
                /**
1342
                 * @link : https://msdn.microsoft.com/en-us/library/dd947039(v=office.12).aspx
1343
                 */
1344
                // echo dechex($rhChild['recType']).'-'.$rhChild['recType'].EOL;
1345 3
                switch ($rhChild['recType']) {
1346 3
                    case self::RT_INTERACTIVEINFO:
1347
                        //@link : http://msdn.microsoft.com/en-us/library/dd948623(v=office.12).aspx
1348 1
                        if ($rhChild['recInstance'] == 0x0000) {
1349 1
                            $mouseClickInfo = $this->readRecordMouseClickInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
1350 1
                            $arrayReturn['length'] += $mouseClickInfo['length'];
1351 1
                            $arrayReturn['hyperlink'][]['id'] = $mouseClickInfo['exHyperlinkIdRef'];
1352 1
                        }
1353 1
                        if ($rhChild['recInstance'] == 0x0001) {
1354
                            $mouseOverInfo = $this->readRecordMouseOverInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
1355
                            $arrayReturn['length'] += $mouseOverInfo['length'];
1356
                        }
1357 1
                        break;
1358 3
                    case self::RT_STYLETEXTPROPATOM:
1359 3
                        $arrayReturn['length'] += 8;
1360
                        // @link : http://msdn.microsoft.com/en-us/library/dd950647(v=office.12).aspx
1361
                        // rgTextPFRun
1362 3
                        $strLenRT = $strLen + 1;
1363
                        do {
1364 3
                            $strucTextPFRun = $this->readStructureTextPFRun($stream, $pos + $arrayReturn['length'], $strLenRT);
1365 3
                            $arrayReturn['numTexts']++;
1366 3
                            $arrayReturn['text'.$arrayReturn['numTexts']] = $strucTextPFRun;
1367 3
                            if (isset($strucTextPFRun['alignH'])) {
1368 3
                                $arrayReturn['alignH'] = $strucTextPFRun['alignH'];
1369 3
                            }
1370 3
                            $strLenRT = $strucTextPFRun['strLenRT'];
1371 3
                            $arrayReturn['length'] += $strucTextPFRun['length'];
1372 3
                        } while ($strLenRT > 0);
1373
                        // rgTextCFRun
1374 3
                        $strLenRT = $strLen + 1;
1375
                        do {
1376 3
                            $strucTextCFRun = $this->readStructureTextCFRun($stream, $pos + $arrayReturn['length'], $strLenRT);
1377 3
                            $arrayReturn['numParts']++;
1378 3
                            $arrayReturn['part'.$arrayReturn['numParts']] = $strucTextCFRun;
1379 3
                            $strLenRT = $strucTextCFRun['strLenRT'];
1380 3
                            $arrayReturn['length'] += $strucTextCFRun['length'];
1381 3
                        } while ($strLenRT > 0);
1382 3
                        break;
1383 3
                    case self::RT_TEXTBYTESATOM:
1384
                        $arrayReturn['length'] += 8;
1385
                        // @link : https://msdn.microsoft.com/en-us/library/dd947905(v=office.12).aspx
1386
                        $strLen = (int)$rhChild['recLen'];
1387
                        for ($inc = 0; $inc < $strLen; $inc++) {
1388
                            $char = self::getInt1d($stream, $pos + $arrayReturn['length']);
1389
                            if ($char == 0x0B) {
1390
                                $char = 0x20;
1391
                            }
1392
                            $arrayReturn['text'] .= Text::chr($char);
1393
                            $arrayReturn['length'] += 1;
1394
                        }
1395
                        break;
1396 3
                    case self::RT_TEXTCHARSATOM:
1397 3
                        $arrayReturn['length'] += 8;
1398
                        // @link : http://msdn.microsoft.com/en-us/library/dd772921(v=office.12).aspx
1399 3
                        $strLen = (int)($rhChild['recLen']/2);
1400 3
                        for ($inc = 0; $inc < $strLen; $inc++) {
1401 3
                            $char = self::getInt2d($stream, $pos + $arrayReturn['length']);
1402 3
                            if ($char == 0x0B) {
1403 1
                                $char = 0x20;
1404 1
                            }
1405 3
                            $arrayReturn['text'] .= Text::chr($char);
1406 3
                            $arrayReturn['length'] += 2;
1407 3
                        }
1408 3
                        break;
1409 3
                    case self::RT_TEXTHEADERATOM:
1410 3
                        $arrayReturn['length'] += 8;
1411
                        // @link : http://msdn.microsoft.com/en-us/library/dd905272(v=office.12).aspx
1412
                        // textType
1413 3
                        $arrayReturn['length'] += 4;
1414 3
                        break;
1415 3
                    case self::RT_TEXTINTERACTIVEINFOATOM:
1416 1
                        $arrayReturn['length'] += 8;
1417
                        //@link : http://msdn.microsoft.com/en-us/library/dd947973(v=office.12).aspx
1418 1
                        if ($rhChild['recInstance'] == 0x0000) {
1419
                            //@todo : MouseClickTextInteractiveInfoAtom
1420 1
                            $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['start'] = self::getInt4d($stream, $pos +  + $arrayReturn['length']);
1421 1
                            $arrayReturn['length'] += 4;
1422
1423 1
                            $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['end'] = self::getInt4d($stream, $pos +  + $arrayReturn['length']);
1424 1
                            $arrayReturn['length'] += 4;
1425 1
                        }
1426 1
                        if ($rhChild['recInstance'] == 0x0001) {
1427
                            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
1428
                        }
1429 1
                        break;
1430 3
                    case self::RT_TEXTSPECIALINFOATOM:
1431 3
                        $arrayReturn['length'] += 8;
1432
                        // @link : http://msdn.microsoft.com/en-us/library/dd945296(v=office.12).aspx
1433 3
                        $strLenRT = $strLen + 1;
1434
                        do {
1435 3
                            $structTextSIRun = $this->readStructureTextSIRun($stream, $pos + $arrayReturn['length'], $strLenRT);
1436 3
                            $strLenRT = $structTextSIRun['strLenRT'];
1437 3
                            $arrayReturn['length'] += $structTextSIRun['length'];
1438 3
                        } while ($strLenRT > 0);
1439 3
                        break;
1440 3
                    case self::RT_TEXTRULERATOM:
1441 3
                        $arrayReturn['length'] += 8;
1442
                        // @link : http://msdn.microsoft.com/en-us/library/dd953212(v=office.12).aspx
1443 3
                        $structRuler = $this->readStructureTextRuler($stream, $pos + $arrayReturn['length']);
1444 3
                        $arrayReturn['length'] += $structRuler['length'];
1445 3
                        break;
1446 3
                    case self::RT_SLIDENUMBERMETACHARATOM:
1447 3
                        $datasRecord = $this->readRecordSlideNumberMCAtom($stream, $pos + $arrayReturn['length']);
1448 3
                        $arrayReturn['length'] += $datasRecord['length'];
1449 3
                        break;
1450 3
                    default:
1451 3
                        $arrayReturn['length'] += 8;
1452 3
                        $arrayReturn['length'] += $rhChild['recLen'];
1453
                    // throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($rhChild['recType']).')');
1454 3
                }
1455 3
            } while (($data['recLen'] - $arrayReturn['length']) > 0);
1456 3
        }
1457 4
        return $arrayReturn;
1458
    }
1459
1460
    /**
1461
     * The OfficeArtSpContainer record specifies a shape container.
1462
     * @param string $stream
1463
     * @param integer $pos
1464
     * @return array
1465
     * @link https://msdn.microsoft.com/en-us/library/dd943794(v=office.12).aspx
1466
     */
1467 4
    private function readRecordOfficeArtSpContainer($stream, $pos)
1468
    {
1469
        $arrayReturn = array(
1470 4
            'length' => 0,
1471 4
            'shape' => null,
1472 4
        );
1473
1474 4
        $data = $this->loadRecordHeader($stream, $pos);
1475 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF004) {
1476
            // Record Header
1477 4
            $arrayReturn['length'] += 8;
1478
            // shapeGroup
1479 4
            $shapeGroup = $this->readRecordOfficeArtFSPGR($stream, $pos + $arrayReturn['length']);
1480 4
            $arrayReturn['length'] += $shapeGroup['length'];
1481
1482
            // shapeProp
1483 4
            $shapeProp = $this->readRecordOfficeArtFSP($stream, $pos + $arrayReturn['length']);
1484 4
            if ($shapeProp['length'] == 0) {
1485
                throw new \Exception('PowerPoint97 Reader : record OfficeArtFSP');
1486
            }
1487 4
            $arrayReturn['length'] += $shapeProp['length'];
1488
1489 4
            if ($shapeProp['fDeleted'] == 0x1 && $shapeProp['fChild'] == 0x0) {
1490
                // deletedShape
1491
                $deletedShape = $this->readRecordOfficeArtFPSPL($stream, $pos + $arrayReturn['length']);
1492
                $arrayReturn['length'] += $deletedShape['length'];
1493
            }
1494
1495
            // shapePrimaryOptions
1496 4
            $shpPrimaryOptions = $this->readRecordOfficeArtFOPT($stream, $pos + $arrayReturn['length']);
1497 4
            $arrayReturn['length'] += $shpPrimaryOptions['length'];
1498
1499
            // shapeSecondaryOptions1
1500 4
            $shpSecondaryOptions1 = $this->readRecordOfficeArtSecondaryFOPT($stream, $pos + $arrayReturn['length']);
1501 4
            $arrayReturn['length'] += $shpSecondaryOptions1['length'];
1502
1503
            // shapeTertiaryOptions1
1504 4
            $shpTertiaryOptions1 = $this->readRecordOfficeArtTertiaryFOPT($stream, $pos + $arrayReturn['length']);
1505 4
            $arrayReturn['length'] += $shpTertiaryOptions1['length'];
1506
1507
            // childAnchor
1508 4
            $childAnchor = $this->readRecordOfficeArtChildAnchor($stream, $pos + $arrayReturn['length']);
1509 4
            $arrayReturn['length'] += $childAnchor['length'];
1510
1511
            // clientAnchor
1512 4
            $clientAnchor = $this->readRecordOfficeArtClientAnchor($stream, $pos + $arrayReturn['length']);
1513 4
            $arrayReturn['length'] += $clientAnchor['length'];
1514
1515
            // clientData
1516 4
            $clientData = $this->readRecordOfficeArtClientData($stream, $pos + $arrayReturn['length']);
1517 4
            $arrayReturn['length'] += $clientData['length'];
1518
1519
            // clientTextbox
1520 4
            $clientTextbox = $this->readRecordOfficeArtClientTextbox($stream, $pos + $arrayReturn['length']);
1521 4
            $arrayReturn['length'] += $clientTextbox['length'];
1522
1523
            // shapeSecondaryOptions2
1524 4
            if ($shpSecondaryOptions1['length'] == 0) {
1525 4
                $shpSecondaryOptions2 = $this->readRecordOfficeArtSecondaryFOPT($stream, $pos + $arrayReturn['length']);
1526 4
                $arrayReturn['length'] += $shpSecondaryOptions2['length'];
1527 4
            }
1528
1529
            // shapeTertiaryOptions2
1530 4
            if ($shpTertiaryOptions1['length'] == 0) {
1531 4
                $shpTertiaryOptions2 = $this->readRecordOfficeArtTertiaryFOPT($stream, $pos + $arrayReturn['length']);
1532 4
                $arrayReturn['length'] += $shpTertiaryOptions2['length'];
1533 4
            }
1534
1535
            // Core : Shape
1536
            // Informations about group are not defined
1537 4
            $arrayDimensions = array();
1538 4
            $bIsGroup = false;
1539 4
            if (is_object($this->oCurrentGroup)) {
1540
                if (!$this->bFirstShapeGroup) {
1541
                    if ($clientAnchor['length'] > 0) {
1542
                        // $this->oCurrentGroup->setOffsetX($clientAnchor['left']);
1543
                        // $this->oCurrentGroup->setOffsetY($clientAnchor['top']);
1544
                        // $this->oCurrentGroup->setHeight($clientAnchor['height']);
1545
                        // $this->oCurrentGroup->setWidth($clientAnchor['width']);
1546
                    }
1547
                    $bIsGroup = true;
1548
                    $this->bFirstShapeGroup = true;
1549
                } else {
1550
                    if ($childAnchor['length'] > 0) {
1551
                        $arrayDimensions = $childAnchor;
1552
                    }
1553
                }
1554
            } else {
1555 4
                if ($clientAnchor['length'] > 0) {
1556 3
                    $arrayDimensions = $clientAnchor;
1557 3
                }
1558
            }
1559 4
            if (!$bIsGroup) {
1560
                // *** Shape ***
1561 4
                if (isset($shpPrimaryOptions['pib'])) {
1562
                    // isDrawing
1563 3
                    $drawingPib = $shpPrimaryOptions['pib'];
1564 3
                    if (isset($this->arrayPictures[$drawingPib - 1])) {
1565 3
                        $gdImage = imagecreatefromstring($this->arrayPictures[$drawingPib - 1]);
1566 3
                        $arrayReturn['shape'] = new Drawing\Gd();
1567 3
                        $arrayReturn['shape']->setImageResource($gdImage);
1568 3
                    }
1569 4
                } elseif (isset($shpPrimaryOptions['line']) && $shpPrimaryOptions['line']) {
1570
                    // isLine
1571 1
                    $arrayReturn['shape'] = new Line(0, 0, 0, 0);
1572 4
                } elseif ($clientTextbox['length'] > 0) {
1573 3
                    $arrayReturn['shape'] = new RichText();
1574 3
                    if (isset($clientTextbox['alignH'])) {
1575 3
                        $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setHorizontal($clientTextbox['alignH']);
1576 3
                    }
1577
1578 3
                    $start = 0;
1579 3
                    $lastLevel = -1;
1580 3
                    $lastMarginLeft = 0;
1581 3
                    for ($inc = 1; $inc <= $clientTextbox['numParts']; $inc++) {
1582 3
                        if ($clientTextbox['numParts'] == $clientTextbox['numTexts'] && isset($clientTextbox['text'.$inc])) {
1583 3
                            if (isset($clientTextbox['text'.$inc]['bulletChar'])) {
1584 1
                                $arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
1585 1
                                $arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletChar($clientTextbox['text'.$inc]['bulletChar']);
1586 1
                            }
1587
                            // Indent
1588 3
                            $indent = 0;
1589 3
                            if (isset($clientTextbox['text'.$inc]['indent'])) {
1590 1
                                $indent = $clientTextbox['text'.$inc]['indent'];
1591 1
                            }
1592 3
                            if (isset($clientTextbox['text'.$inc]['leftMargin'])) {
1593 1
                                if ($lastMarginLeft > $clientTextbox['text'.$inc]['leftMargin']) {
1594 1
                                    $lastLevel--;
1595 1
                                }
1596 1
                                if ($lastMarginLeft < $clientTextbox['text'.$inc]['leftMargin']) {
1597 1
                                    $lastLevel++;
1598 1
                                }
1599 1
                                $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setLevel($lastLevel);
1600 1
                                $lastMarginLeft = $clientTextbox['text'.$inc]['leftMargin'];
1601
1602 1
                                $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setMarginLeft($clientTextbox['text'.$inc]['leftMargin']);
1603 1
                                $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setIndent($indent - $clientTextbox['text'.$inc]['leftMargin']);
1604 1
                            }
1605 3
                        }
1606
                        // Texte
1607 3
                        $sText = substr(isset($clientTextbox['text']) ? $clientTextbox['text'] : '', $start, $clientTextbox['part'.$inc]['partLength']);
1608 3
                        $sHyperlinkURL = '';
1609 3
                        if (empty($sText)) {
1610
                            // Is there a hyperlink ?
1611 1
                            if (isset($clientTextbox['hyperlink']) && is_array($clientTextbox['hyperlink']) && !empty($clientTextbox['hyperlink'])) {
1612 1
                                foreach ($clientTextbox['hyperlink'] as $itmHyperlink) {
1613 1
                                    if ($itmHyperlink['start'] == $start && ($itmHyperlink['end'] - $itmHyperlink['start']) == $clientTextbox['part'.$inc]['partLength']) {
1614 1
                                        $sText = $this->arrayHyperlinks[$itmHyperlink['id']]['text'];
1615 1
                                        $sHyperlinkURL = $this->arrayHyperlinks[$itmHyperlink['id']]['url'];
1616 1
                                        break;
1617
                                    }
1618 1
                                }
1619 1
                            }
1620 1
                        }
1621
                        // New paragraph
1622 3
                        $bCreateParagraph = false;
1623 3
                        if (strpos($sText, "\r") !== false) {
1624 1
                            $bCreateParagraph = true;
1625 1
                            $sText = str_replace("\r", '', $sText);
1626 1
                        }
1627
                        // TextRun
1628 3
                        $txtRun = $arrayReturn['shape']->createTextRun($sText);
1629 3
                        if (isset($clientTextbox['part'.$inc]['bold'])) {
1630 2
                            $txtRun->getFont()->setBold($clientTextbox['part'.$inc]['bold']);
1631 2
                        }
1632 3
                        if (isset($clientTextbox['part'.$inc]['italic'])) {
1633 2
                            $txtRun->getFont()->setItalic($clientTextbox['part'.$inc]['italic']);
1634 2
                        }
1635 3
                        if (isset($clientTextbox['part'.$inc]['underline'])) {
1636 2
                            $txtRun->getFont()->setUnderline($clientTextbox['part'.$inc]['underline']);
1637 2
                        }
1638 3
                        if (isset($clientTextbox['part'.$inc]['fontName'])) {
1639 3
                            $txtRun->getFont()->setName($clientTextbox['part'.$inc]['fontName']);
1640 3
                        }
1641 3
                        if (isset($clientTextbox['part'.$inc]['fontSize'])) {
1642 3
                            $txtRun->getFont()->setSize($clientTextbox['part'.$inc]['fontSize']);
1643 3
                        }
1644 3
                        if (isset($clientTextbox['part'.$inc]['color'])) {
1645 3
                            $txtRun->getFont()->setColor($clientTextbox['part'.$inc]['color']);
1646 3
                        }
1647
                        // Hyperlink
1648 3
                        if (!empty($sHyperlinkURL)) {
1649 1
                            $txtRun->setHyperlink(new Hyperlink($sHyperlinkURL));
1650 1
                        }
1651
1652 3
                        $start += $clientTextbox['part'.$inc]['partLength'];
1653 3
                        if ($bCreateParagraph) {
1654 1
                            $arrayReturn['shape']->createParagraph();
1655 1
                        }
1656 3
                    }
1657 3
                }
1658
1659
                // *** Properties ***
1660
                // Dimensions
1661 4
                if ($arrayReturn['shape'] instanceof AbstractShape) {
1662 3
                    if (!empty($arrayDimensions)) {
1663 3
                        $arrayReturn['shape']->setOffsetX($arrayDimensions['left']);
1664 3
                        $arrayReturn['shape']->setOffsetY($arrayDimensions['top']);
1665 3
                        $arrayReturn['shape']->setHeight($arrayDimensions['height']);
1666 3
                        $arrayReturn['shape']->setWidth($arrayDimensions['width']);
1667 3
                    }
1668
                    // Rotation
1669 3
                    if (isset($shpPrimaryOptions['rotation'])) {
1670
                        $rotation = $shpPrimaryOptions['rotation'];
1671
                        $arrayReturn['shape']->setRotation($rotation);
1672
                    }
1673
                    // Shadow
1674 3
                    if (isset($shpPrimaryOptions['shadowOffsetX']) && isset($shpPrimaryOptions['shadowOffsetY'])) {
1675 2
                        $shadowOffsetX = $shpPrimaryOptions['shadowOffsetX'];
1676 2
                        $shadowOffsetY = $shpPrimaryOptions['shadowOffsetY'];
1677 2
                        if ($shadowOffsetX != 0 && $shadowOffsetX != 0) {
1678 2
                            $arrayReturn['shape']->getShadow()->setVisible(true);
1679 2
                            if ($shadowOffsetX > 0 && $shadowOffsetX == $shadowOffsetY) {
1680 2
                                $arrayReturn['shape']->getShadow()->setDistance($shadowOffsetX)->setDirection(45);
1681 2
                            }
1682 2
                        }
1683 2
                    }
1684
                    // Specific Line
1685 3
                    if ($arrayReturn['shape'] instanceof Line) {
1686 1
                        if (isset($shpPrimaryOptions['lineColor'])) {
1687 1
                            $arrayReturn['shape']->getBorder()->getColor()->setARGB('FF'.$shpPrimaryOptions['lineColor']);
1688 1
                        }
1689 1
                        if (isset($shpPrimaryOptions['lineWidth'])) {
1690 1
                            $arrayReturn['shape']->setHeight($shpPrimaryOptions['lineWidth']);
1691 1
                        }
1692 1
                    }
1693
                    // Specific RichText
1694 3
                    if ($arrayReturn['shape'] instanceof RichText) {
1695 3
                        if (isset($shpPrimaryOptions['insetBottom'])) {
1696 3
                            $arrayReturn['shape']->setInsetBottom($shpPrimaryOptions['insetBottom']);
1697 3
                        }
1698 3
                        if (isset($shpPrimaryOptions['insetLeft'])) {
1699 3
                            $arrayReturn['shape']->setInsetLeft($shpPrimaryOptions['insetLeft']);
1700 3
                        }
1701 3
                        if (isset($shpPrimaryOptions['insetRight'])) {
1702 3
                            $arrayReturn['shape']->setInsetRight($shpPrimaryOptions['insetRight']);
1703 3
                        }
1704 3
                        if (isset($shpPrimaryOptions['insetTop'])) {
1705 3
                            $arrayReturn['shape']->setInsetTop($shpPrimaryOptions['insetTop']);
1706 3
                        }
1707 3
                    }
1708 3
                }
1709 4
            } else {
1710
                // Rotation
1711
                if (isset($shpPrimaryOptions['rotation'])) {
1712
                    $rotation = $shpPrimaryOptions['rotation'];
1713
                    $this->oCurrentGroup->setRotation($rotation);
1714
                }
1715
            }
1716 4
        }
1717
1718 4
        return $arrayReturn;
1719
    }
1720
1721
    /**
1722
     * The OfficeArtSpgrContainer record specifies a container for groups of shapes.
1723
     * @param string $stream
1724
     * @param integer $pos
1725
     * @param boolean $bInGroup
1726
     * @return array
1727
     * @link : https://msdn.microsoft.com/en-us/library/dd910416(v=office.12).aspx
1728
     */
1729 4
    private function readRecordOfficeArtSpgrContainer($stream, $pos, $bInGroup = false)
1730
    {
1731
        $arrayReturn = array(
1732 4
            'length' => 0,
1733 4
        );
1734
1735 4
        $data = $this->loadRecordHeader($stream, $pos);
1736 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF003) {
1737 4
            $arrayReturn['length'] += 8;
1738
1739
            do {
1740 4
                $rhFileBlock = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
1741 4
                if (!($rhFileBlock['recVer'] == 0xF && $rhFileBlock['recInstance'] == 0x0000 && ($rhFileBlock['recType'] == 0xF003 || $rhFileBlock['recType'] == 0xF004))) {
1742
                    throw new \Exception('PowerPoint97 Reader : readRecordOfficeArtSpgrContainer.');
1743
                }
1744
1745 4
                switch ($rhFileBlock['recType']) {
1746 4
                    case 0xF003:
1747
                        // Core
1748
                        $this->oCurrentGroup = $this->oPhpPresentation->getActiveSlide()->createGroup();
1749
                        $this->bFirstShapeGroup = false;
1750
                        // OfficeArtSpgrContainer
1751
                        $fileBlock = $this->readRecordOfficeArtSpgrContainer($stream, $pos + $arrayReturn['length'], true);
1752
                        $arrayReturn['length'] += $fileBlock['length'];
1753
                        $data['recLen'] -= $fileBlock['length'];
1754
                        break;
1755 4
                    case 0xF004:
1756
                        // Core
1757 4
                        if (!$bInGroup) {
1758 4
                            $this->oCurrentGroup = null;
1759 4
                        }
1760
                        // OfficeArtSpContainer
1761 4
                        $fileBlock = $this->readRecordOfficeArtSpContainer($stream, $pos + $arrayReturn['length']);
1762 4
                        $arrayReturn['length'] += $fileBlock['length'];
1763 4
                        $data['recLen'] -= $fileBlock['length'];
1764
                        // Core
1765
                        //@todo
1766 4
                        if (!is_null($fileBlock['shape'])) {
1767 3
                            switch ($this->inMainType) {
1768 3
                                case self::RT_NOTES:
1769 3
                                    $arrayIdxSlide = array_flip($this->arrayNotes);
1770 3
                                    if ($this->currentNote > 0 && isset($arrayIdxSlide[$this->currentNote])) {
1771
                                        $oSlide = $this->oPhpPresentation->getSlide($arrayIdxSlide[$this->currentNote]);
1772
                                        if ($oSlide->getNote()->getShapeCollection()->count() == 0) {
1773
                                            $oSlide->getNote()->addShape($fileBlock['shape']);
1774
                                        }
1775
                                    }
1776 3
                                    break;
1777 3
                                case self::RT_SLIDE:
1778 3
                                    if ($bInGroup) {
1779
                                        $this->oCurrentGroup->addShape($fileBlock['shape']);
1780
                                    } else {
1781 3
                                        $this->oPhpPresentation->getActiveSlide()->addShape($fileBlock['shape']);
1782
                                    }
1783 3
                                    break;
1784 3
                            }
1785 3
                        }
1786 4
                        break;
1787 4
                }
1788 4
            } while ($data['recLen'] > 0);
1789 3
        }
1790 3
        return $arrayReturn;
1791
    }
1792
1793
    /**
1794
     * The OfficeArtTertiaryFOPT record specifies a table of OfficeArtRGFOPTE records,.
1795
     * @param string $stream
1796
     * @param integer $pos
1797
     * @return array
1798
     * @link https://msdn.microsoft.com/en-us/library/dd950206(v=office.12).aspx
1799
     */
1800 4
    private function readRecordOfficeArtTertiaryFOPT($stream, $pos)
1801
    {
1802
        $arrayReturn = array(
1803 4
            'length' => 0,
1804 4
        );
1805
1806 4
        $data = $this->loadRecordHeader($stream, $pos);
1807 4
        if ($data['recVer'] == 0x3 && $data['recType'] == 0xF122) {
1808
            // Record Header
1809
            $arrayReturn['length'] += 8;
1810
1811
            $officeArtFOPTE = array();
1812
            for ($inc = 0; $inc < $data['recInstance']; $inc++) {
1813
                $opid = self::getInt2d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
1814
                $arrayReturn['length'] += 2;
1815
                $optOp = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
1816
                $arrayReturn['length'] += 4;
1817
                $officeArtFOPTE[] = array(
1818
                    'opid' => ($opid >> 0) & bindec('11111111111111'),
1819
                    'fBid' => ($opid >> 14) & bindec('1'),
1820
                    'fComplex' => ($opid >> 15) & bindec('1'),
1821
                    'op' => $optOp,
1822
                );
1823
            }
1824
            //@link : http://code.metager.de/source/xref/kde/calligra/filters/libmso/OPID
1825
            foreach ($officeArtFOPTE as $opt) {
1826
                switch ($opt['opid']) {
1827
                    case 0x039F:
1828
                        // Table properties
1829
                        //@link : https://msdn.microsoft.com/en-us/library/dd922773(v=office.12).aspx
1830
                        break;
1831
                    case 0x03A0:
1832
                        // Table Row Properties
1833
                        //@link : https://msdn.microsoft.com/en-us/library/dd923419(v=office.12).aspx
1834
                        if ($opt['fComplex'] == 0x1) {
1835
                            $arrayReturn['length'] += $opt['op'];
1836
                        }
1837
                        break;
1838
                    case 0x03A9:
1839
                        // GroupShape : metroBlob
1840
                        //@link : https://msdn.microsoft.com/en-us/library/dd943388(v=office.12).aspx
1841
                        if ($opt['fComplex'] == 0x1) {
1842
                            $arrayReturn['length'] += $opt['op'];
1843
                        }
1844
                        break;
1845
                    case 0x01FF:
1846
                        // Line Style Boolean
1847
                        //@link : https://msdn.microsoft.com/en-us/library/dd951605(v=office.12).aspx
1848
                        break;
1849
                    default:
1850
                        throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($opt['opid']).')');
1851
                }
1852
            }
1853
        }
1854 4
        return $arrayReturn;
1855
    }
1856
1857
    /**
1858
     * The OfficeArtDgContainer record specifies the container for all the file records for the objects in a drawing.
1859
     * @param string $stream
1860
     * @param integer $pos
1861
     * @return array
1862
     * @link : https://msdn.microsoft.com/en-us/library/dd924455(v=office.12).aspx
1863
     */
1864 4
    private function readRecordOfficeArtDgContainer($stream, $pos)
1865
    {
1866
        $arrayReturn = array(
1867 4
            'length' => 0,
1868 4
        );
1869
1870 4
        $data = $this->loadRecordHeader($stream, $pos);
1871 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF002) {
1872
            // Record Header
1873 4
            $arrayReturn['length'] += 8;
1874
            // drawingData
1875 4
            $drawingData  = $this->readRecordOfficeArtFDG($stream, $pos + $arrayReturn['length']);
1876 4
            $arrayReturn['length'] += $drawingData['length'];
1877
            // regroupItems
1878
            //@todo
1879
            // groupShape
1880 4
            $groupShape = $this->readRecordOfficeArtSpgrContainer($stream, $pos + $arrayReturn['length']);
1881 3
            $arrayReturn['length'] += $groupShape['length'];
1882
            // shape
1883 3
            $shape = $this->readRecordOfficeArtSpContainer($stream, $pos + $arrayReturn['length']);
1884 3
            $arrayReturn['length'] += $shape['length'];
1885
            // solvers1
1886
            //@todo
1887
            // deletedShapes
1888
            //@todo
1889
            // solvers1
1890
            //@todo
1891 3
        }
1892
1893 3
        return $arrayReturn;
1894
    }
1895
1896
    /**
1897
     * The OfficeArtFDG record specifies the number of shapes, the drawing identifier, and the shape identifier of the last shape in a drawing.
1898
     * @param string $stream
1899
     * @param integer $pos
1900
     * @return array
1901
     * @link : https://msdn.microsoft.com/en-us/library/dd946757(v=office.12).aspx
1902
     */
1903 4
    private function readRecordOfficeArtFDG($stream, $pos)
1904
    {
1905
        $arrayReturn = array(
1906 4
            'length' => 0,
1907 4
        );
1908
1909 4
        $data = $this->loadRecordHeader($stream, $pos);
1910 4
        if ($data['recVer'] == 0x0 && $data['recInstance'] <= 0xFFE && $data['recType'] == 0xF008 && $data['recLen'] == 0x00000008) {
1911
            // Record Header
1912 4
            $arrayReturn['length'] += 8;
1913
            // Length
1914 4
            $arrayReturn['length'] += $data['recLen'];
1915 4
        }
1916
1917 4
        return $arrayReturn;
1918
    }
1919
1920
    /**
1921
     * The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records.
1922
     * @param string $stream
1923
     * @param integer $pos
1924
     * @return array
1925
     * @link https://msdn.microsoft.com/en-us/library/dd943404(v=office.12).aspx
1926
     */
1927 4
    private function readRecordOfficeArtFOPT($stream, $pos)
1928
    {
1929
        $arrayReturn = array(
1930 4
            'length' => 0,
1931 4
        );
1932
1933 4
        $data = $this->loadRecordHeader($stream, $pos);
1934 4
        if ($data['recVer'] == 0x3 && $data['recType'] == 0xF00B) {
1935
            // Record Header
1936 4
            $arrayReturn['length'] += 8;
1937
1938
            //@link : http://msdn.microsoft.com/en-us/library/dd906086(v=office.12).aspx
1939 4
            $officeArtFOPTE = array();
1940 4
            for ($inc = 0; $inc < $data['recInstance']; $inc++) {
1941 4
                $opid = self::getInt2d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
1942 4
                $arrayReturn['length'] += 2;
1943 4
                $data['recLen'] -= 2;
1944 4
                $optOp = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
1945 4
                $arrayReturn['length'] += 4;
1946 4
                $data['recLen'] -= 4;
1947 4
                $officeArtFOPTE[] = array(
1948 4
                    'opid' => ($opid >> 0) & bindec('11111111111111'),
1949 4
                    'fBid' => ($opid >> 14) & bindec('1'),
1950 4
                    'fComplex' => ($opid >> 15) & bindec('1'),
1951 4
                    'op' => $optOp,
1952
                );
1953 4
            }
1954
            //@link : http://code.metager.de/source/xref/kde/calligra/filters/libmso/OPID
1955 4
            foreach ($officeArtFOPTE as $opt) {
1956
                // echo $opt['opid'].'-0x'.dechex($opt['opid']).EOL;
1957 4
                switch ($opt['opid']) {
1958 4
                    case 0x0004:
1959
                        // Transform : rotation
1960
                        //@link : https://msdn.microsoft.com/en-us/library/dd949750(v=office.12).aspx
1961
                        $arrayReturn['rotation'] = $opt['op'];
1962
                        break;
1963 4
                    case 0x007F:
1964
                        // Transform : Protection Boolean Properties
1965
                        //@link : http://msdn.microsoft.com/en-us/library/dd909131(v=office.12).aspx
1966 3
                        break;
1967 4
                    case 0x0080:
1968
                        // Text : ltxid
1969
                        //@link : http://msdn.microsoft.com/en-us/library/dd947446(v=office.12).aspx
1970 3
                        break;
1971 4
                    case 0x0081:
1972
                        // Text : dxTextLeft
1973
                        //@link : http://msdn.microsoft.com/en-us/library/dd953234(v=office.12).aspx
1974 3
                        $arrayReturn['insetLeft'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
1975 3
                        break;
1976 4
                    case 0x0082:
1977
                        // Text : dyTextTop
1978
                        //@link : http://msdn.microsoft.com/en-us/library/dd925068(v=office.12).aspx
1979 3
                        $arrayReturn['insetTop'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
1980 3
                        break;
1981 4
                    case 0x0083:
1982
                        // Text : dxTextRight
1983
                        //@link : http://msdn.microsoft.com/en-us/library/dd906782(v=office.12).aspx
1984 3
                        $arrayReturn['insetRight'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
1985 3
                        break;
1986 4
                    case 0x0084:
1987
                        // Text : dyTextBottom
1988
                        //@link : http://msdn.microsoft.com/en-us/library/dd772858(v=office.12).aspx
1989 3
                        $arrayReturn['insetBottom'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
1990 3
                        break;
1991 4
                    case 0x0085:
1992
                        // Text : WrapText
1993
                        //@link : http://msdn.microsoft.com/en-us/library/dd924770(v=office.12).aspx
1994 4
                        break;
1995 4
                    case 0x0087:
1996
                        // Text : anchorText
1997
                        //@link : http://msdn.microsoft.com/en-us/library/dd948575(v=office.12).aspx
1998 4
                        break;
1999 4
                    case 0x00BF:
2000
                        // Text : Text Boolean Properties
2001
                        //@link : http://msdn.microsoft.com/en-us/library/dd950905(v=office.12).aspx
2002 3
                        break;
2003 4
                    case 0x0104:
2004
                        // Blip : pib
2005
                        //@link : http://msdn.microsoft.com/en-us/library/dd772837(v=office.12).aspx
2006 3
                        if ($opt['fComplex'] == 0) {
2007 3
                            $arrayReturn['pib'] = $opt['op'];
2008 3
                            $data['recLen'] -= $opt['op'];
2009 3
                        } else {
2010
                            // pib Complex
2011
                        }
2012 3
                        break;
2013 4
                    case 0x0105:
2014
                        // Blip : pibName - specifies the comment, file name, or URL, as specified by the pibFlags property, for this BLIP.
2015
                        //@link : https://msdn.microsoft.com/en-us/library/dd945885(v=office.12).aspx
2016
                        break;
2017 4
                    case 0x013F:
2018
                        // Blip Boolean Properties
2019
                        //@link : https://msdn.microsoft.com/en-us/library/dd944215(v=office.12).aspx
2020
                        break;
2021 4
                    case 0x0140:
2022
                        // Geometry : geoLeft
2023
                        //@link : http://msdn.microsoft.com/en-us/library/dd947489(v=office.12).aspx
2024
                        // print_r('geoLeft : '.$opt['op'].EOL);
2025 2
                        break;
2026 4
                    case 0x0141:
2027
                        // Geometry : geoTop
2028
                        //@link : http://msdn.microsoft.com/en-us/library/dd949459(v=office.12).aspx
2029
                        // print_r('geoTop : '.$opt['op'].EOL);
2030 2
                        break;
2031 4
                    case 0x0142:
2032
                        // Geometry : geoRight
2033
                        //@link : http://msdn.microsoft.com/en-us/library/dd947117(v=office.12).aspx
2034
                        // print_r('geoRight : '.$opt['op'].EOL);
2035 2
                        break;
2036 4
                    case 0x0143:
2037
                        // Geometry : geoBottom
2038
                        //@link : http://msdn.microsoft.com/en-us/library/dd948602(v=office.12).aspx
2039
                        // print_r('geoBottom : '.$opt['op'].EOL);
2040 2
                        break;
2041 4
                    case 0x0144:
2042
                        // Geometry : shapePath
2043
                        //@link : http://msdn.microsoft.com/en-us/library/dd945249(v=office.12).aspx
2044 1
                        $arrayReturn['line'] = true;
2045 1
                        break;
2046 4
                    case 0x0145:
2047
                        // Geometry : pVertices
2048
                        //@link : http://msdn.microsoft.com/en-us/library/dd949814(v=office.12).aspx
2049 2
                        if ($opt['fComplex'] == 1) {
2050 2
                            $arrayReturn['length'] += $opt['op'];
2051 2
                            $data['recLen'] -= $opt['op'];
2052 2
                        }
2053 2
                        break;
2054 4
                    case 0x0146:
2055
                        // Geometry : pSegmentInfo
2056
                        //@link : http://msdn.microsoft.com/en-us/library/dd905742(v=office.12).aspx
2057 2
                        if ($opt['fComplex'] == 1) {
2058 2
                            $arrayReturn['length'] += $opt['op'];
2059 2
                            $data['recLen'] -= $opt['op'];
2060 2
                        }
2061 2
                        break;
2062 4
                    case 0x0155:
2063
                        // Geometry : pAdjustHandles
2064
                        //@link : http://msdn.microsoft.com/en-us/library/dd905890(v=office.12).aspx
2065 2
                        if ($opt['fComplex'] == 1) {
2066 2
                            $arrayReturn['length'] += $opt['op'];
2067 2
                            $data['recLen'] -= $opt['op'];
2068 2
                        }
2069 2
                        break;
2070 4
                    case 0x0156:
2071
                        // Geometry : pGuides
2072
                        //@link : http://msdn.microsoft.com/en-us/library/dd910801(v=office.12).aspx
2073 2
                        if ($opt['fComplex'] == 1) {
2074 2
                            $arrayReturn['length'] += $opt['op'];
2075 2
                            $data['recLen'] -= $opt['op'];
2076 2
                        }
2077 2
                        break;
2078 4
                    case 0x0157:
2079
                        // Geometry : pInscribe
2080
                        //@link : http://msdn.microsoft.com/en-us/library/dd904889(v=office.12).aspx
2081 2
                        if ($opt['fComplex'] == 1) {
2082 2
                            $arrayReturn['length'] += $opt['op'];
2083 2
                            $data['recLen'] -= $opt['op'];
2084 2
                        }
2085 2
                        break;
2086 4
                    case 0x017F:
2087
                        // Geometry Boolean Properties
2088
                        //@link : http://msdn.microsoft.com/en-us/library/dd944968(v=office.12).aspx
2089 1
                        break;
2090 4
                    case 0x0180:
2091
                        // Fill : fillType
2092
                        //@link : http://msdn.microsoft.com/en-us/library/dd947909(v=office.12).aspx
2093 3
                        break;
2094 4
                    case 0x0181:
2095
                        // Fill : fillColor
2096
                        //@link : http://msdn.microsoft.com/en-us/library/dd921332(v=office.12).aspx
2097 3
                        $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2098 3
                        $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2099 3
                        $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2100
                        // echo 'fillColor  : '.$strColor.EOL;
2101 3
                        break;
2102 4
                    case 0x0183:
2103
                        // Fill : fillBackColor
2104
                        //@link : http://msdn.microsoft.com/en-us/library/dd950634(v=office.12).aspx
2105 3
                        $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2106 3
                        $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2107 3
                        $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2108
                        // echo 'fillBackColor  : '.$strColor.EOL;
2109 3
                        break;
2110 4
                    case 0x0193:
2111
                        // Fill : fillRectRight
2112
                        //@link : http://msdn.microsoft.com/en-us/library/dd951294(v=office.12).aspx
2113
                        // echo 'fillRectRight  : '.\PhpOffice\Common\Drawing::emuToPixels($opt['op']).EOL;
2114 3
                        break;
2115 4
                    case 0x0194:
2116
                        // Fill : fillRectBottom
2117
                        //@link : http://msdn.microsoft.com/en-us/library/dd910194(v=office.12).aspx
2118
                        // echo 'fillRectBottom   : '.\PhpOffice\Common\Drawing::emuToPixels($opt['op']).EOL;
2119 3
                        break;
2120 4
                    case 0x01BF:
2121
                        // Fill : Fill Style Boolean Properties
2122
                        //@link : http://msdn.microsoft.com/en-us/library/dd909380(v=office.12).aspx
2123 3
                        break;
2124 4
                    case 0x01C0:
2125
                        // Line Style : lineColor
2126
                        //@link : http://msdn.microsoft.com/en-us/library/dd920397(v=office.12).aspx
2127 3
                        $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2128 3
                        $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2129 3
                        $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
2130 3
                        $arrayReturn['lineColor'] = $strColor;
2131 3
                        break;
2132 4
                    case 0x01C1:
2133
                        // Line Style : lineOpacity
2134
                        //@link : http://msdn.microsoft.com/en-us/library/dd923433(v=office.12).aspx
2135
                        // echo 'lineOpacity : '.dechex($opt['op']).EOL;
2136 3
                        break;
2137 4
                    case 0x01C2:
2138
                        // Line Style : lineBackColor
2139
                        //@link : http://msdn.microsoft.com/en-us/library/dd947669(v=office.12).aspx
2140 3
                        break;
2141 4
                    case 0x01CB:
2142
                        // Line Style : lineWidth
2143
                        //@link : http://msdn.microsoft.com/en-us/library/dd926964(v=office.12).aspx
2144 1
                        $arrayReturn['lineWidth'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
2145 1
                        break;
2146 4
                    case 0x01D6:
2147
                        // Line Style : lineJoinStyle
2148
                        //@link : http://msdn.microsoft.com/en-us/library/dd909643(v=office.12).aspx
2149 3
                        break;
2150 4
                    case 0x01D7:
2151
                        // Line Style : lineEndCapStyle
2152
                        //@link : http://msdn.microsoft.com/en-us/library/dd925071(v=office.12).aspx
2153 3
                        break;
2154 4
                    case 0x01FF:
2155
                        // Line Style : Line Style Boolean Properties
2156
                        //@link : http://msdn.microsoft.com/en-us/library/dd951605(v=office.12).aspx
2157 3
                        break;
2158 4
                    case 0x0201:
2159
                        // Shadow Style : shadowColor
2160
                        //@link : http://msdn.microsoft.com/en-us/library/dd923454(v=office.12).aspx
2161 3
                        break;
2162 4
                    case 0x0204:
2163
                        // Shadow Style : shadowOpacity
2164
                        //@link : http://msdn.microsoft.com/en-us/library/dd920720(v=office.12).aspx
2165 2
                        break;
2166 4
                    case 0x0205:
2167
                        // Shadow Style : shadowOffsetX
2168
                        //@link : http://msdn.microsoft.com/en-us/library/dd945280(v=office.12).aspx
2169 2
                        $arrayReturn['shadowOffsetX'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
2170 2
                        break;
2171 4
                    case 0x0206:
2172
                        // Shadow Style : shadowOffsetY
2173
                        //@link : http://msdn.microsoft.com/en-us/library/dd907855(v=office.12).aspx
2174 2
                        $arrayReturn['shadowOffsetY'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
2175 2
                        break;
2176 4
                    case 0x0210:
2177
                        // Shadow Style : shadowOriginX
2178
                        //@link : https://msdn.microsoft.com/en-us/library/dd921662(v=office.12).aspx
2179
                        break;
2180 4
                    case 0x0211:
2181
                        // Shadow Style : shadowOriginY
2182
                        //@link : https://msdn.microsoft.com/en-us/library/dd945853(v=office.12).aspx
2183
                        break;
2184 4
                    case 0x023F:
2185
                        // Shadow Style : Shadow Style Boolean Properties
2186
                        //@link : http://msdn.microsoft.com/en-us/library/dd947887(v=office.12).aspx
2187 3
                        break;
2188 4
                    case 0x0304:
2189
                        // Shape : bWMode
2190
                        //@link : http://msdn.microsoft.com/en-us/library/dd947659(v=office.12).aspx
2191 3
                        break;
2192 4
                    case 0x033F:
2193
                        // Shape Boolean Properties
2194
                        //@link : http://msdn.microsoft.com/en-us/library/dd951345(v=office.12).aspx
2195 3
                        break;
2196 1
                    case 0x0380:
2197
                        // Group Shape Property Set : wzName
2198
                        //@link : http://msdn.microsoft.com/en-us/library/dd950681(v=office.12).aspx
2199
                        if ($opt['fComplex'] == 1) {
2200
                            $arrayReturn['length'] += $opt['op'];
2201
                            $data['recLen'] -= $opt['op'];
2202
                        }
2203
                        break;
2204 1
                    case 0x0381:
2205
                        // Group Shape Property Set : wzDescription
2206
                        //@link : https://msdn.microsoft.com/en-us/library/dd945733(v=office.12).aspx
2207
                        if ($opt['fComplex'] == 1) {
2208
                            $arrayReturn['length'] += $opt['op'];
2209
                            $data['recLen'] -= $opt['op'];
2210
                        }
2211
                        break;
2212 1
                    case 0x03BF:
2213
                        // Group Shape Property Set : Group Shape Boolean Properties
2214
                        //@link : http://msdn.microsoft.com/en-us/library/dd949807(v=office.12).aspx
2215
                        break;
2216 1
                    default:
2217 1
                        throw new \Exception('Feature not implemented (l.' . __LINE__ . ' : 0x' . dechex($opt['opid']) . ')');
2218 4
                }
2219 4
            }
2220 3
            if ($data['recLen'] > 0) {
2221 2
                $arrayReturn['length'] += $data['recLen'];
2222 2
            }
2223 3
        }
2224
2225 4
        return $arrayReturn;
2226
    }
2227
2228
    /**
2229
     * The OfficeArtFPSPL record specifies the former hierarchical position of the containing object that is either a shape or a group of shapes.
2230
     * @param string $stream
2231
     * @param integer $pos
2232
     * @return array
2233
     * @link https://msdn.microsoft.com/en-us/library/dd947479(v=office.12).aspx
2234
     */
2235
    private function readRecordOfficeArtFPSPL($stream, $pos)
2236
    {
2237
        $arrayReturn = array(
2238
            'length' => 0,
2239
        );
2240
2241
        $data = $this->loadRecordHeader($stream, $pos);
2242
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF11D && $data['recLen'] == 0x00000004) {
2243
            $arrayReturn['length'] += 8;
2244
            $arrayReturn['length'] += $data['recLen'];
2245
        }
2246
2247
        return $arrayReturn;
2248
    }
2249
2250
    /**
2251
     * The OfficeArtFSP record specifies an instance of a shape.
2252
     * @param string $stream
2253
     * @param integer $pos
2254
     * @return array
2255
     * @link https://msdn.microsoft.com/en-us/library/dd925898(v=office.12).aspx
2256
     */
2257 4
    private function readRecordOfficeArtFSP($stream, $pos)
2258
    {
2259
        $arrayReturn = array(
2260 4
            'length' => 0,
2261 4
        );
2262
2263 4
        $data = $this->loadRecordHeader($stream, $pos);
2264 4
        if ($data['recVer'] == 0x2 && $data['recType'] == 0xF00A && $data['recLen'] == 0x00000008) {
2265 4
            $arrayReturn['length'] += 8;
2266
            // spid
2267 4
            $arrayReturn['length'] += 4;
2268
            // data
2269 4
            $data = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
2270 4
            $arrayReturn['length'] += 4;
2271 4
            $arrayReturn['fGroup'] = ($data >> 0) & bindec('1');
2272 4
            $arrayReturn['fChild'] = ($data >> 1) & bindec('1');
2273 4
            $arrayReturn['fPatriarch'] = ($data >> 2) & bindec('1');
2274 4
            $arrayReturn['fDeleted'] = ($data >> 3) & bindec('1');
2275 4
        }
2276
2277 4
        return $arrayReturn;
2278
    }
2279
2280
    /**
2281
     * The OfficeArtFSPGR record specifies the coordinate system of the group shape that the anchors of the child shape are expressed in.
2282
     * @param string $stream
2283
     * @param integer $pos
2284
     * @return array
2285
     * @link https://msdn.microsoft.com/en-us/library/dd925381(v=office.12).aspx
2286
     */
2287 4
    private function readRecordOfficeArtFSPGR($stream, $pos)
2288
    {
2289
        $arrayReturn = array(
2290 4
            'length' => 0,
2291 4
        );
2292
2293 4
        $data = $this->loadRecordHeader($stream, $pos);
2294 4
        if ($data['recVer'] == 0x1 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF009 && $data['recLen'] == 0x00000010) {
2295 4
            $arrayReturn['length'] += 8;
2296
            //$arrShapeGroup['xLeft'] = self::getInt4d($this->streamPowerpointDocument, $pos);
2297 4
            $arrayReturn['length'] += 4;
2298
            //$arrShapeGroup['yTop'] = self::getInt4d($this->streamPowerpointDocument, $pos);
2299 4
            $arrayReturn['length'] += 4;
2300
            //$arrShapeGroup['xRight'] = self::getInt4d($this->streamPowerpointDocument, $pos);
2301 4
            $arrayReturn['length'] += 4;
2302
            //$arrShapeGroup['yBottom'] = self::getInt4d($this->streamPowerpointDocument, $pos);
2303 4
            $arrayReturn['length'] += 4;
2304 4
        }
2305
2306 4
        return $arrayReturn;
2307
    }
2308
2309
    /**
2310
     * The OfficeArtSecondaryFOPT record specifies a table of OfficeArtRGFOPTE records.
2311
     * @param string $stream
2312
     * @param integer $pos
2313
     * @return array
2314
     * @link https://msdn.microsoft.com/en-us/library/dd950259(v=office.12).aspx
2315
     */
2316 4
    private function readRecordOfficeArtSecondaryFOPT($stream, $pos)
2317
    {
2318
        $arrayReturn = array(
2319 4
            'length' => 0,
2320 4
        );
2321
2322 4
        $data = $this->loadRecordHeader($stream, $pos);
2323 4
        if ($data['recVer'] == 0x3 && $data['recType'] == 0xF121) {
2324
            // Record Header
2325
            $arrayReturn['length'] += 8;
2326
            // Length
2327
            $arrayReturn['length'] += $data['recLen'];
2328
        }
2329 4
        return $arrayReturn;
2330
    }
2331
2332
    /**
2333
     * A container record that specifies information about a shape.
2334
     * @param string $stream
2335
     * @param integer $pos
2336
     * @link : https://msdn.microsoft.com/en-us/library/dd950927(v=office.12).aspx
2337
     */
2338 4
    private function readRecordOfficeArtClientData($stream, $pos)
2339
    {
2340
        $arrayReturn = array(
2341 4
            'length' => 0,
2342 4
        );
2343
2344 4
        $data = $this->loadRecordHeader($stream, $pos);
2345 4
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF011) {
2346 3
            $arrayReturn['length'] += 8;
2347
            // shapeFlagsAtom (9 bytes)
2348 3
            $dataShapeFlagsAtom = $this->readRecordShapeFlagsAtom($stream, $pos + $arrayReturn['length']);
2349 3
            $arrayReturn['length'] += $dataShapeFlagsAtom['length'];
2350
2351
            // shapeFlags10Atom (9 bytes)
2352 3
            $dataShapeFlags10Atom = $this->readRecordShapeFlags10Atom($stream, $pos + $arrayReturn['length']);
2353 3
            $arrayReturn['length'] += $dataShapeFlags10Atom['length'];
2354
2355
            // exObjRefAtom (12 bytes)
2356 3
            $dataExObjRefAtom = $this->readRecordExObjRefAtom($stream, $pos + $arrayReturn['length']);
2357 3
            $arrayReturn['length'] += $dataExObjRefAtom['length'];
2358
2359
            // animationInfo (variable)
2360 3
            $dataAnimationInfo = $this->readRecordAnimationInfoContainer($stream, $pos + $arrayReturn['length']);
2361 3
            $arrayReturn['length'] += $dataAnimationInfo['length'];
2362
2363
            // mouseClickInteractiveInfo (variable)
2364 3
            $mouseClickInfo = $this->readRecordMouseClickInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
2365 3
            $arrayReturn['length'] += $mouseClickInfo['length'];
2366
2367
            // mouseOverInteractiveInfo (variable)
2368 3
            $mouseOverInfo = $this->readRecordMouseOverInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
2369 3
            $arrayReturn['length'] += $mouseOverInfo['length'];
2370
2371
            // placeholderAtom (16 bytes)
2372 3
            $dataPlaceholderAtom = $this->readRecordPlaceholderAtom($stream, $pos + $arrayReturn['length']);
2373 3
            $arrayReturn['length'] += $dataPlaceholderAtom['length'];
2374
2375
            // recolorInfoAtom (variable)
2376 3
            $dataRecolorInfo = $this->readRecordRecolorInfoAtom($stream, $pos + $arrayReturn['length']);
2377 3
            $arrayReturn['length'] += $dataRecolorInfo['length'];
2378
2379
            // rgShapeClientRoundtripData (variable)
2380
            $array = array(
2381 3
                self::RT_PROGTAGS,
2382 3
                self::RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM,
2383 3
                self::RT_ROUNDTRIPSHAPEID12ATOM,
2384 3
                self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM,
2385 3
                self::RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM,
2386 3
            );
2387
            do {
2388 3
                $dataHeaderRG = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
2389 3
                if (in_array($dataHeaderRG['recType'], $array)) {
2390
                    switch ($dataHeaderRG['recType']) {
2391
                        case self::RT_PROGTAGS:
2392
                            $dataRG = $this->readRecordShapeProgTagsContainer($stream, $pos + $arrayReturn['length']);
2393
                            $arrayReturn['length'] += $dataRG['length'];
2394
                            break;
2395
                        case self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM:
2396
                            $dataRG = $this->readRecordRoundTripHFPlaceholder12Atom($stream, $pos + $arrayReturn['length']);
2397
                            $arrayReturn['length'] += $dataRG['length'];
2398
                            break;
2399
                        case self::RT_ROUNDTRIPSHAPEID12ATOM:
2400
                            $dataRG = $this->readRecordRoundTripShapeId12Atom($stream, $pos + $arrayReturn['length']);
2401
                            $arrayReturn['length'] += $dataRG['length'];
2402
                            break;
2403
                        default:
2404
                            throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($dataHeaderRG['recType']).')');
2405
                    }
2406
                }
2407 3
            } while (in_array($dataHeaderRG['recType'], $array));
2408 3
        }
2409
2410 4
        return $arrayReturn;
2411
    }
2412
2413
    /**
2414
     * An atom record that specifies a persist object directory. Each persist object identifier specified MUST be unique in that persist object directory.
2415
     * @link http://msdn.microsoft.com/en-us/library/dd952680(v=office.12).aspx
2416
     * @param string $stream
2417
     * @param integer $pos
2418
     * @throws \Exception
2419
     */
2420 4
    private function readRecordPersistDirectoryAtom($stream, $pos)
2421
    {
2422 4
        $rHeader = $this->loadRecordHeader($stream, $pos);
2423 4
        $pos += 8;
2424 4
        if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_PERSISTDIRECTORYATOM) {
2425
            throw new \Exception('File PowerPoint 97 in error (Location : PersistDirectoryAtom > RecordHeader).');
2426
        }
2427
        // rgPersistDirEntry
2428
        // @link : http://msdn.microsoft.com/en-us/library/dd947347(v=office.12).aspx
2429
        do {
2430 4
            $data = self::getInt4d($stream, $pos);
2431 4
            $pos += 4;
2432 4
            $rHeader['recLen'] -= 4;
2433
            //$persistId  = ($data >> 0) & bindec('11111111111111111111');
2434 4
            $cPersist  = ($data >> 20) & bindec('111111111111');
2435
2436 4
            $rgPersistOffset = array();
2437 4
            for ($inc = 0; $inc < $cPersist; $inc++) {
2438 4
                $rgPersistOffset[] = self::getInt4d($stream, $pos);
2439 4
                $pos += 4;
2440 4
                $rHeader['recLen'] -= 4;
2441 4
            }
2442 4
        } while ($rHeader['recLen'] > 0);
2443 4
        $this->rgPersistDirEntry = $rgPersistOffset;
2444 4
    }
2445
2446
    /**
2447
     * A container record that specifies information about the headers (1) and footers within a slide.
2448
     * @param string $stream
2449
     * @param integer $pos
2450
     * @link https://msdn.microsoft.com/en-us/library/dd904856(v=office.12).aspx
2451
     */
2452 3
    private function readRecordPerSlideHeadersFootersContainer($stream, $pos)
2453
    {
2454
        $arrayReturn = array(
2455 3
            'length' => 0,
2456 3
        );
2457
2458 3
        $data = $this->loadRecordHeader($stream, $pos);
2459 3
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_HEADERSFOOTERS) {
2460
            // Record Header
2461 3
            $arrayReturn['length'] += 8;
2462
            // Length
2463 3
            $arrayReturn['length'] += $data['recLen'];
2464 3
        }
2465
2466 3
        return $arrayReturn;
2467
    }
2468
2469
    /**
2470
     * An atom record that specifies whether a shape is a placeholder shape.
2471
     * @param string $stream
2472
     * @param integer $pos
2473
     * @link https://msdn.microsoft.com/en-us/library/dd923930(v=office.12).aspx
2474
     */
2475 3
    private function readRecordPlaceholderAtom($stream, $pos)
2476
    {
2477
        $arrayReturn = array(
2478 3
            'length' => 0,
2479 3
        );
2480
2481 3
        $data = $this->loadRecordHeader($stream, $pos);
2482 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PLACEHOLDERATOM && $data['recLen'] == 0x00000008) {
2483
            // Record Header
2484 3
            $arrayReturn['length'] += 8;
2485
            // Datas
2486 3
            $arrayReturn['length'] += $data['recLen'];
2487 3
        }
2488
2489 3
        return $arrayReturn;
2490
    }
2491
2492
    /**
2493
     * An atom record that specifies a collection of re-color mappings for a metafile ([MS-WMF]).
2494
     * @param string $stream
2495
     * @param integer $pos
2496
     * @link https://msdn.microsoft.com/en-us/library/dd904899(v=office.12).aspx
2497
     */
2498 3
    private function readRecordRecolorInfoAtom($stream, $pos)
2499
    {
2500
        $arrayReturn = array(
2501 3
            'length' => 0,
2502 3
        );
2503
2504 3
        $data = $this->loadRecordHeader($stream, $pos);
2505 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_RECOLORINFOATOM) {
2506
            // Record Header
2507
            $arrayReturn['length'] += 8;
2508
            // Datas
2509
            $arrayReturn['length'] += $data['recLen'];
2510
        }
2511
2512 3
        return $arrayReturn;
2513
    }
2514
2515
    /**
2516
     * An atom record that specifies that a shape is a header or footerplaceholder shape.
2517
     * @param string $stream
2518
     * @param integer $pos
2519
     * @link https://msdn.microsoft.com/en-us/library/dd910800(v=office.12).aspx
2520
     */
2521
    private function readRecordRoundTripHFPlaceholder12Atom($stream, $pos)
2522
    {
2523
        $arrayReturn = array(
2524
            'length' => 0,
2525
        );
2526
2527
        $data = $this->loadRecordHeader($stream, $pos);
2528
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM && $data['recLen'] == 0x00000001) {
2529
            // Record Header
2530
            $arrayReturn['length'] += 8;
2531
            // Datas
2532
            $arrayReturn['length'] += $data['recLen'];
2533
        }
2534
2535
        return $arrayReturn;
2536
    }
2537
2538
    /**
2539
     * An atom record that specifies a shape identifier.
2540
     * @param string $stream
2541
     * @param integer $pos
2542
     * @link https://msdn.microsoft.com/en-us/library/dd772926(v=office.12).aspx
2543
     */
2544
    private function readRecordRoundTripShapeId12Atom($stream, $pos)
2545
    {
2546
        $arrayReturn = array(
2547
            'length' => 0,
2548
        );
2549
2550
        $data = $this->loadRecordHeader($stream, $pos);
2551
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPSHAPEID12ATOM && $data['recLen'] == 0x00000004) {
2552
            // Record Header
2553
            $arrayReturn['length'] += 8;
2554
            // Length
2555
            $arrayReturn['length'] += $data['recLen'];
2556
        }
2557
2558
        return $arrayReturn;
2559
    }
2560
2561
    /**
2562
     * A container record that specifies information about a slide that synchronizes to a slide in a slide library.
2563
     * @param string $stream
2564
     * @param integer $pos
2565
     * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
2566
     */
2567 3
    private function readRecordRoundTripSlideSyncInfo12Container($stream, $pos)
2568
    {
2569
        $arrayReturn = array(
2570 3
            'length' => 0,
2571 3
        );
2572
2573 3
        $data = $this->loadRecordHeader($stream, $pos);
2574 3
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPSLIDESYNCINFO12) {
2575
            // Record Header
2576
            $arrayReturn['length'] += 8;
2577
            // Length
2578
            $arrayReturn['length'] += $data['recLen'];
2579
        }
2580
2581 3
        return $arrayReturn;
2582
    }
2583
2584
    /**
2585
     * An atom record that specifies shape-level Boolean flags.
2586
     * @param string $stream
2587
     * @param integer $pos
2588
     * @link https://msdn.microsoft.com/en-us/library/dd908949(v=office.12).aspx
2589
     */
2590 3
    private function readRecordShapeFlags10Atom($stream, $pos)
2591
    {
2592
        $arrayReturn = array(
2593 3
            'length' => 0,
2594 3
        );
2595
2596 3
        $data = $this->loadRecordHeader($stream, $pos);
2597 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SHAPEFLAGS10ATOM && $data['recLen'] == 0x00000001) {
2598
            // Record Header
2599
            $arrayReturn['length'] += 8;
2600
            // Datas
2601
            $arrayReturn['length'] += $data['recLen'];
2602
        }
2603
2604 3
        return $arrayReturn;
2605
    }
2606
2607
    /**
2608
     * An atom record that specifies shape-level Boolean flags.
2609
     * @param string $stream
2610
     * @param integer $pos
2611
     * @link https://msdn.microsoft.com/en-us/library/dd925824(v=office.12).aspx
2612
     */
2613 3
    private function readRecordShapeFlagsAtom($stream, $pos)
2614
    {
2615
        $arrayReturn = array(
2616 3
                'length' => 0,
2617 3
        );
2618
2619 3
        $data = $this->loadRecordHeader($stream, $pos);
2620 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SHAPEATOM && $data['recLen'] == 0x00000001) {
2621
            // Record Header
2622
            $arrayReturn['length'] += 8;
2623
            // Datas
2624
            $arrayReturn['length'] += $data['recLen'];
2625
        }
2626
2627 3
        return $arrayReturn;
2628
    }
2629
2630
    /**
2631
     * A container record that specifies programmable tags with additional binary shape data.
2632
     * @param string $stream
2633
     * @param integer $pos
2634
     * @link https://msdn.microsoft.com/en-us/library/dd911033(v=office.12).aspx
2635
     */
2636
    private function readRecordShapeProgBinaryTagContainer($stream, $pos)
2637
    {
2638
        $arrayReturn = array(
2639
            'length' => 0,
2640
        );
2641
2642
        $data = $this->loadRecordHeader($stream, $pos);
2643
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGBINARYTAG) {
2644
            // Record Header
2645
            $arrayReturn['length'] += 8;
2646
            // Datas
2647
            $arrayReturn['length'] += $data['recLen'];
2648
        }
2649
2650
        return $arrayReturn;
2651
    }
2652
2653
    /**
2654
     * A container record that specifies programmable tags with additional shape data.
2655
     * @param string $stream
2656
     * @param integer $pos
2657
     * @link https://msdn.microsoft.com/en-us/library/dd911266(v=office.12).aspx
2658
     */
2659
    private function readRecordShapeProgTagsContainer($stream, $pos)
2660
    {
2661
        $arrayReturn = array(
2662
            'length' => 0,
2663
        );
2664
2665
        $data = $this->loadRecordHeader($stream, $pos);
2666
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGTAGS) {
2667
            // Record Header
2668
            $arrayReturn['length'] += 8;
2669
2670
            $length = 0;
2671
            do {
2672
                $dataHeaderRG = $this->loadRecordHeader($stream, $pos + $arrayReturn['length'] + $length);
2673
                switch ($dataHeaderRG['recType']) {
2674
                    case self::RT_PROGBINARYTAG:
2675
                        $dataRG = $this->readRecordShapeProgBinaryTagContainer($stream, $pos + $arrayReturn['length'] + $length);
2676
                        $length += $dataRG['length'];
2677
                        break;
2678
                    //case self::RT_PROGSTRINGTAG:
2679
                    default:
2680
                        throw new \Exception('Feature not implemented (l.'.__LINE__.')');
2681
                }
2682
            } while ($length < $data['recLen']);
2683
            // Datas
2684
            $arrayReturn['length'] += $data['recLen'];
2685
        }
2686
2687
        return $arrayReturn;
2688
    }
2689
2690
    /**
2691
     * An atom record that specifies information about a slide.
2692
     * @param string $stream
2693
     * @param integer $pos
2694
     * @return array
2695
     * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
2696
     */
2697 3
    private function readRecordSlideAtom($stream, $pos)
2698
    {
2699
        $arrayReturn = array(
2700 3
            'length' => 0,
2701 3
        );
2702
2703 3
        $data = $this->loadRecordHeader($stream, $pos);
2704 3
        if ($data['recVer'] == 0x2 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDEATOM) {
2705
            // Record Header
2706 3
            $arrayReturn['length'] += 8;
2707
            // slideAtom > geom
2708 3
            $arrayReturn['length'] += 4;
2709
            // slideAtom > rgPlaceholderTypes
2710 3
            $rgPlaceholderTypes = array();
2711 3
            for ($inc = 0; $inc < 8; $inc++) {
2712 3
                $rgPlaceholderTypes[] = self::getInt1d($this->streamPowerpointDocument, $pos);
2713 3
                $arrayReturn['length'] += 1;
2714 3
            }
2715
2716
            // slideAtom > masterIdRef
2717 3
            $arrayReturn['length'] += 4;
2718
            // slideAtom > notesIdRef
2719 3
            $arrayReturn['length'] += 4;
2720
            // slideAtom > slideFlags
2721 3
            $arrayReturn['length'] += 2;
2722
            // slideAtom > unused;
2723 3
            $arrayReturn['length'] += 2;
2724 3
        }
2725
2726 3
        return $arrayReturn;
2727
    }
2728
2729
    /**
2730
     * A container record that specifies a presentation slide or title master slide.
2731
     * @param string $stream
2732
     * @param int $pos
2733
     * @link http://msdn.microsoft.com/en-us/library/dd946323(v=office.12).aspx
2734
     */
2735 3
    private function readRecordSlideContainer($stream, $pos)
2736
    {
2737
        // Core
2738 3
        $this->oPhpPresentation->createSlide();
2739 3
        $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);
2740
2741
        // *** slideAtom (32 bytes)
2742 3
        $slideAtom = $this->readRecordSlideAtom($stream, $pos);
2743 3
        if ($slideAtom['length'] == 0) {
2744
            throw new \Exception('PowerPoint97 Reader : record SlideAtom');
2745
        }
2746 3
        $pos += $slideAtom['length'];
2747
2748
        // *** slideShowSlideInfoAtom (24 bytes)
2749 3
        $slideShowInfoAtom = $this->readRecordSlideShowSlideInfoAtom($stream, $pos);
2750 3
        $pos += $slideShowInfoAtom['length'];
2751
2752
        // *** perSlideHFContainer (variable) : optional
2753 3
        $perSlideHFContainer = $this->readRecordPerSlideHeadersFootersContainer($stream, $pos);
2754 3
        $pos += $perSlideHFContainer['length'];
2755
2756
        // *** rtSlideSyncInfo12 (variable) : optional
2757 3
        $rtSlideSyncInfo12 = $this->readRecordRoundTripSlideSyncInfo12Container($stream, $pos);
2758 3
        $pos += $rtSlideSyncInfo12['length'];
2759
2760
        // *** drawing (variable)
2761 3
        $drawing = $this->readRecordDrawingContainer($stream, $pos);
2762 3
        $pos += $drawing['length'];
2763
2764
        // *** slideSchemeColorSchemeAtom (40 bytes)
2765 3
        $slideSchemeColorAtom = $this->readRecordSlideSchemeColorSchemeAtom($stream, $pos);
2766 3
        if ($slideSchemeColorAtom['length'] == 0) {
2767
            throw new \Exception('PowerPoint97 Reader : record SlideSchemeColorSchemeAtom');
2768
        }
2769 3
        $pos += $slideSchemeColorAtom['length'];
2770
2771
        // *** slideNameAtom (variable)
2772 3
        $slideNameAtom = $this->readRecordSlideNameAtom($stream, $pos);
2773 3
        $pos += $slideNameAtom['length'];
2774
2775
        // *** slideProgTagsContainer (variable).
2776 3
        $slideProgTags = $this->readRecordSlideProgTagsContainer($stream, $pos);
2777 3
        $pos += $slideProgTags['length'];
2778
2779
        // *** rgRoundTripSlide (variable)
2780 3
    }
2781
2782
    /**
2783
     * An atom record that specifies the name of a slide.
2784
     * @param string $stream
2785
     * @param integer $pos
2786
     * @link https://msdn.microsoft.com/en-us/library/dd906297(v=office.12).aspx
2787
     */
2788 3
    private function readRecordSlideNameAtom($stream, $pos)
2789
    {
2790
        $arrayReturn = array(
2791 3
            'length' => 0,
2792 3
            'slideName' => '',
2793 3
        );
2794
2795 3
        $data = $this->loadRecordHeader($stream, $pos);
2796 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x003 && $data['recType'] == self::RT_CSTRING && $data['recLen'] % 2 == 0) {
2797
            // Record Header
2798
            $arrayReturn['length'] += 8;
2799
            // Length
2800
            $strLen = ($data['recLen'] / 2);
2801
            for ($inc = 0; $inc < $strLen; $inc++) {
2802
                $char = self::getInt2d($stream, $pos + $arrayReturn['length']);
2803
                $arrayReturn['length'] += 2;
2804
                $arrayReturn['slideName'] .= Text::chr($char);
2805
            }
2806
        }
2807
2808 3
        return $arrayReturn;
2809
    }
2810
2811
    /**
2812
     * An atom record that specifies a slide number metacharacter.
2813
     * @param string $stream
2814
     * @param integer $pos
2815
     * @link https://msdn.microsoft.com/en-us/library/dd945703(v=office.12).aspx
2816
     */
2817 3
    private function readRecordSlideNumberMCAtom($stream, $pos)
2818
    {
2819
        $arrayReturn = array(
2820 3
            'length' => 0,
2821 3
        );
2822
2823 3
        $data = $this->loadRecordHeader($stream, $pos);
2824 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDENUMBERMETACHARATOM && $data['recLen'] == 0x00000004) {
2825
            // Record Header
2826 3
            $arrayReturn['length'] += 8;
2827
            // Datas
2828 3
            $arrayReturn['length'] += $data['recLen'];
2829 3
        }
2830
2831 3
        return $arrayReturn;
2832
    }
2833
2834
    /**
2835
     * A container record that specifies programmable tags with additional slide data.
2836
     * @param string $stream
2837
     * @param integer $pos
2838
     * @link https://msdn.microsoft.com/en-us/library/dd951946(v=office.12).aspx
2839
     */
2840 3
    private function readRecordSlideProgTagsContainer($stream, $pos)
2841
    {
2842
        $arrayReturn = array(
2843 3
            'length' => 0,
2844 3
        );
2845
2846 3
        $data = $this->loadRecordHeader($stream, $pos);
2847 3
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGTAGS) {
2848
            // Record Header
2849 3
            $arrayReturn['length'] += 8;
2850
            // Length
2851 3
            $arrayReturn['length'] += $data['recLen'];
2852 3
        }
2853
2854 3
        return $arrayReturn;
2855
    }
2856
2857
    /**
2858
     * A container record that specifies the color scheme used by a slide.
2859
     * @param string $stream
2860
     * @param integer $pos
2861
     * @link https://msdn.microsoft.com/en-us/library/dd949420(v=office.12).aspx
2862
     */
2863 3
    private function readRecordSlideSchemeColorSchemeAtom($stream, $pos)
2864
    {
2865
        $arrayReturn = array(
2866 3
            'length' => 0,
2867 3
        );
2868
2869 3
        $data = $this->loadRecordHeader($stream, $pos);
2870 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x001 && $data['recType'] == self::RT_COLORSCHEMEATOM && $data['recLen'] == 0x00000020) {
2871
            // Record Header
2872 3
            $arrayReturn['length'] += 8;
2873
            // Length
2874 3
            $rgSchemeColor = array();
2875 3
            for ($inc = 0; $inc <= 7; $inc++) {
2876 3
                $rgSchemeColor[] = array(
2877 3
                    'red' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4),
2878 3
                    'green' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4 + 1),
2879 3
                    'blue' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4 + 2),
2880
                );
2881 3
            }
2882 3
            $arrayReturn['length'] += (8 * 4);
2883 3
        }
2884
2885 3
        return $arrayReturn;
2886
    }
2887
2888
    /**
2889
     * An atom record that specifies what transition effect to perform during a slide show, and how to advance to the next presentation slide.
2890
     * @param string $stream
2891
     * @param integer $pos
2892
     * @link https://msdn.microsoft.com/en-us/library/dd943408(v=office.12).aspx
2893
     */
2894 3
    private function readRecordSlideShowSlideInfoAtom($stream, $pos)
2895
    {
2896
        $arrayReturn = array(
2897 3
            'length' => 0,
2898 3
        );
2899
2900 3
        $data = $this->loadRecordHeader($stream, $pos);
2901 3
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDESHOWSLIDEINFOATOM && $data['recLen'] == 0x00000010) {
2902
            // Record Header
2903 3
            $arrayReturn['length'] += 8;
2904
            // Length;
2905 3
            $arrayReturn['length'] += $data['recLen'];
2906 3
        }
2907
2908 3
        return $arrayReturn;
2909
    }
2910
2911
    /**
2912
     * UserEditAtom
2913
     * @link http://msdn.microsoft.com/en-us/library/dd945746(v=office.12).aspx
2914
     * @param string $stream
2915
     * @param integer $pos
2916
     * @throws \Exception
2917
     */
2918 4
    private function readRecordUserEditAtom($stream, $pos)
2919
    {
2920 4
        $rHeader = $this->loadRecordHeader($stream, $pos);
2921 4
        $pos += 8;
2922 4
        if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_USEREDITATOM || ($rHeader['recLen'] != 0x0000001C && $rHeader['recLen'] != 0x00000020)) {
2923
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > RecordHeader).');
2924
        }
2925
2926
        // lastSlideIdRef
2927 4
        $pos += 4;
2928
        // version
2929 4
        $pos += 2;
2930
2931
        // minorVersion
2932 4
        $minorVersion = self::getInt1d($stream, $pos);
2933 4
        $pos += 1;
2934 4
        if ($minorVersion != 0x00) {
2935
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > minorVersion).');
2936
        }
2937
2938
        // majorVersion
2939 4
        $majorVersion = self::getInt1d($stream, $pos);
2940 4
        $pos += 1;
2941 4
        if ($majorVersion != 0x03) {
2942
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > majorVersion).');
2943
        }
2944
2945
        // offsetLastEdit
2946 4
        $pos += 4;
2947
        // offsetPersistDirectory
2948 4
        $this->offsetPersistDirectory = self::getInt4d($stream, $pos);
2949 4
        $pos += 4;
2950
2951
        // docPersistIdRef
2952 4
        $docPersistIdRef  = self::getInt4d($stream, $pos);
2953 4
        $pos += 4;
2954 4
        if ($docPersistIdRef != 0x00000001) {
2955
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > docPersistIdRef).');
2956
        }
2957
2958
        // persistIdSeed
2959 4
        $pos += 4;
2960
        // lastView
2961 4
        $pos += 2;
2962
        // unused
2963 4
        $pos += 2;
2964 4
    }
2965
2966
    /**
2967
     * A structure that specifies the character-level formatting of a run of text.
2968
     * @param string $stream
2969
     * @param int $pos
2970
     * @param int $strLenRT
2971
     * @link https://msdn.microsoft.com/en-us/library/dd945870(v=office.12).aspx
2972
     */
2973 3
    private function readStructureTextCFRun($stream, $pos, $strLenRT)
2974
    {
2975
        $arrayReturn = array(
2976 3
            'length' => 0,
2977 3
            'strLenRT' => $strLenRT,
2978 3
        );
2979
2980
        // rgTextCFRun
2981 3
        $countRgTextCFRun = self::getInt4d($stream, $pos + $arrayReturn['length']);
2982 3
        $arrayReturn['strLenRT'] -= $countRgTextCFRun;
2983 3
        $arrayReturn['length'] += 4;
2984 3
        $arrayReturn['partLength'] = $countRgTextCFRun;
2985
2986 3
        $masks = self::getInt4d($stream, $pos + $arrayReturn['length']);
2987 3
        $arrayReturn['length'] += 4;
2988
2989 3
        $masksData = array();
2990 3
        $masksData['bold'] = ($masks >> 0) & bindec('1');
2991 3
        $masksData['italic'] = ($masks >> 1) & bindec('1');
2992 3
        $masksData['underline'] = ($masks >> 2) & bindec('1');
2993 3
        $masksData['unused1'] = ($masks >> 3) & bindec('1');
2994 3
        $masksData['shadow'] = ($masks >> 4) & bindec('1');
2995 3
        $masksData['fehint'] = ($masks >> 5) & bindec('1');
2996 3
        $masksData['unused2'] = ($masks >> 6) & bindec('1');
2997 3
        $masksData['kumi'] = ($masks >> 7) & bindec('1');
2998 3
        $masksData['unused3'] = ($masks >> 8) & bindec('1');
2999 3
        $masksData['emboss'] = ($masks >> 9) & bindec('1');
3000 3
        $masksData['fHasStyle'] = ($masks >> 10) & bindec('1111');
3001 3
        $masksData['unused4'] = ($masks >> 14) & bindec('11');
3002 3
        $masksData['typeface'] = ($masks >> 16) & bindec('1');
3003 3
        $masksData['size'] = ($masks >> 17) & bindec('1');
3004 3
        $masksData['color'] = ($masks >> 18) & bindec('1');
3005 3
        $masksData['position'] = ($masks >> 19) & bindec('1');
3006 3
        $masksData['pp10ext'] = ($masks >> 20) & bindec('1');
3007 3
        $masksData['oldEATypeface'] = ($masks >> 21) & bindec('1');
3008 3
        $masksData['ansiTypeface'] = ($masks >> 22) & bindec('1');
3009 3
        $masksData['symbolTypeface'] = ($masks >> 23) & bindec('1');
3010 3
        $masksData['newEATypeface'] = ($masks >> 24) & bindec('1');
3011 3
        $masksData['csTypeface'] = ($masks >> 25) & bindec('1');
3012 3
        $masksData['pp11ext'] = ($masks >> 26) & bindec('1');
3013 3
        if ($masksData['bold'] == 1 || $masksData['italic'] == 1 || $masksData['underline'] == 1 || $masksData['shadow'] == 1 || $masksData['fehint'] == 1 ||  $masksData['kumi'] == 1 ||  $masksData['emboss'] == 1 ||  $masksData['fHasStyle'] == 1) {
3014 2
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3015 2
            $arrayReturn['length'] += 2;
3016
3017 2
            $fontStyleFlags = array();
3018 2
            $fontStyleFlags['bold'] = ($data >> 0) & bindec('1');
3019 2
            $fontStyleFlags['italic'] = ($data >> 1) & bindec('1');
3020 2
            $fontStyleFlags['underline'] = ($data >> 2) & bindec('1');
3021 2
            $fontStyleFlags['unused1'] = ($data >> 3) & bindec('1');
3022 2
            $fontStyleFlags['shadow'] = ($data >> 4) & bindec('1');
3023 2
            $fontStyleFlags['fehint'] = ($data >> 5) & bindec('1');
3024 2
            $fontStyleFlags['unused2'] = ($data >> 6) & bindec('1');
3025 2
            $fontStyleFlags['kumi'] = ($data >> 7) & bindec('1');
3026 2
            $fontStyleFlags['unused3'] = ($data >> 8) & bindec('1');
3027 2
            $fontStyleFlags['emboss'] = ($data >> 9) & bindec('1');
3028 2
            $fontStyleFlags['pp9rt'] = ($data >> 10) & bindec('1111');
3029 2
            $fontStyleFlags['unused4'] = ($data >> 14) & bindec('11');
3030
3031 2
            $arrayReturn['bold'] = ($fontStyleFlags['bold'] == 1) ? true : false;
3032 2
            $arrayReturn['italic'] = ($fontStyleFlags['italic'] == 1) ? true : false;
3033 2
            $arrayReturn['underline'] = ($fontStyleFlags['underline'] == 1) ? true : false;
3034 2
        }
3035 3
        if ($masksData['typeface'] == 1) {
3036 3
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3037 3
            $arrayReturn['length'] += 2;
3038 3
            $arrayReturn['fontName'] = isset($this->arrayFonts[$data]) ? $this->arrayFonts[$data] : '';
3039 3
        }
3040 3
        if ($masksData['oldEATypeface'] == 1) {
3041
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3042 3
            $arrayReturn['length'] += 2;
3043 3
        }
3044 3
        if ($masksData['ansiTypeface'] == 1) {
3045
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3046
            $arrayReturn['length'] += 2;
3047
        }
3048 3
        if ($masksData['symbolTypeface'] == 1) {
3049
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3050
            $arrayReturn['length'] += 2;
3051
        }
3052 3
        if ($masksData['size'] == 1) {
3053 3
            $arrayReturn['fontSize'] = self::getInt2d($stream, $pos + $arrayReturn['length']);
3054 3
            $arrayReturn['length'] += 2;
3055 3
        }
3056 3
        if ($masksData['color'] == 1) {
3057 3
            $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
3058 3
            $arrayReturn['length'] += 1;
3059 3
            $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
3060 3
            $arrayReturn['length'] += 1;
3061 3
            $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
3062 3
            $arrayReturn['length'] += 1;
3063 3
            $index = self::getInt1d($stream, $pos + $arrayReturn['length']);
3064 3
            $arrayReturn['length'] += 1;
3065
3066 3
            if ($index == 0xFE) {
3067 3
                $strColor = str_pad(dechex($red), 2, STR_PAD_LEFT, '0');
3068 3
                $strColor .= str_pad(dechex($green), 2, STR_PAD_LEFT, '0');
3069 3
                $strColor .= str_pad(dechex($blue), 2, STR_PAD_LEFT, '0');
3070
3071 3
                $arrayReturn['color'] = new Color('FF'.$strColor);
3072 3
            }
3073 3
        }
3074 3
        if ($masksData['position'] == 1) {
3075
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3076
        }
3077
3078 3
        return $arrayReturn;
3079
    }
3080
3081
    /**
3082
     * A structure that specifies the paragraph-level formatting of a run of text.
3083
     * @param string $stream
3084
     * @param integer $pos
3085
     * @link https://msdn.microsoft.com/en-us/library/dd923535(v=office.12).aspx
3086
     */
3087 3
    private function readStructureTextPFRun($stream, $pos, $strLenRT)
3088
    {
3089
        $arrayReturn = array(
3090 3
            'length' => 0,
3091 3
            'strLenRT' => $strLenRT,
3092 3
        );
3093
3094
        // rgTextPFRun
3095 3
        $countRgTextPFRun = self::getInt4d($stream, $pos + $arrayReturn['length']);
3096 3
        $arrayReturn['strLenRT'] -= $countRgTextPFRun;
3097 3
        $arrayReturn['length'] += 4;
3098
3099
        // indent
3100 3
        $arrayReturn['length'] += 2;
3101
3102 3
        $masks = self::getInt4d($stream, $pos + $arrayReturn['length']);
3103 3
        $arrayReturn['length'] += 4;
3104
3105 3
        $masksData = array();
3106 3
        $masksData['hasBullet'] = ($masks >> 0) & bindec('1');
3107 3
        $masksData['bulletHasFont'] = ($masks >> 1) & bindec('1');
3108 3
        $masksData['bulletHasColor'] = ($masks >> 2) & bindec('1');
3109 3
        $masksData['bulletHasSize'] = ($masks >> 3) & bindec('1');
3110 3
        $masksData['bulletFont'] = ($masks >> 4) & bindec('1');
3111 3
        $masksData['bulletColor'] = ($masks >> 5) & bindec('1');
3112 3
        $masksData['bulletSize'] = ($masks >> 6) & bindec('1');
3113 3
        $masksData['bulletChar'] = ($masks >> 7) & bindec('1');
3114 3
        $masksData['leftMargin'] = ($masks >> 8) & bindec('1');
3115 3
        $masksData['unused'] = ($masks >> 9) & bindec('1');
3116 3
        $masksData['indent'] = ($masks >> 10) & bindec('1');
3117 3
        $masksData['align'] = ($masks >> 11) & bindec('1');
3118 3
        $masksData['lineSpacing'] = ($masks >> 12) & bindec('1');
3119 3
        $masksData['spaceBefore'] = ($masks >> 13) & bindec('1');
3120 3
        $masksData['spaceAfter'] = ($masks >> 14) & bindec('1');
3121 3
        $masksData['defaultTabSize'] = ($masks >> 15) & bindec('1');
3122 3
        $masksData['fontAlign'] = ($masks >> 16) & bindec('1');
3123 3
        $masksData['charWrap'] = ($masks >> 17) & bindec('1');
3124 3
        $masksData['wordWrap'] = ($masks >> 18) & bindec('1');
3125 3
        $masksData['overflow'] = ($masks >> 19) & bindec('1');
3126 3
        $masksData['tabStops'] = ($masks >> 20) & bindec('1');
3127 3
        $masksData['textDirection'] = ($masks >> 21) & bindec('1');
3128 3
        $masksData['reserved1'] = ($masks >> 22) & bindec('1');
3129 3
        $masksData['bulletBlip'] = ($masks >> 23) & bindec('1');
3130 3
        $masksData['bulletScheme'] = ($masks >> 24) & bindec('1');
3131 3
        $masksData['bulletHasScheme'] = ($masks >> 25) & bindec('1');
3132
3133 3
        $bulletFlags = array();
3134 3
        if ($masksData['hasBullet'] == 1 || $masksData['bulletHasFont'] == 1  || $masksData['bulletHasColor'] == 1  || $masksData['bulletHasSize'] == 1) {
3135 3
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3136 3
            $arrayReturn['length'] += 2;
3137
3138 3
            $bulletFlags['fHasBullet'] = ($data >> 0) & bindec('1');
3139 3
            $bulletFlags['fBulletHasFont'] = ($data >> 1) & bindec('1');
3140 3
            $bulletFlags['fBulletHasColor'] = ($data >> 2) & bindec('1');
3141 3
            $bulletFlags['fBulletHasSize'] = ($data >> 3) & bindec('1');
3142 3
        }
3143 3
        if ($masksData['bulletChar'] == 1) {
3144 1
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3145 1
            $arrayReturn['length'] += 2;
3146 1
            $arrayReturn['bulletChar'] = chr($data);
3147 1
        }
3148 3
        if ($masksData['bulletFont'] == 1) {
3149
            // $data = self::getInt2d($stream, $pos);
3150 1
            $arrayReturn['length'] += 2;
3151 1
        }
3152 3
        if ($masksData['bulletSize'] == 1) {
3153
            // $data = self::getInt2d($stream, $pos);
3154 1
            $arrayReturn['length'] += 2;
3155 1
        }
3156 3
        if ($masksData['bulletColor'] == 1) {
3157 1
            $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
3158 1
            $arrayReturn['length'] += 1;
3159 1
            $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
3160 1
            $arrayReturn['length'] += 1;
3161 1
            $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
3162 1
            $arrayReturn['length'] += 1;
3163 1
            $index = self::getInt1d($stream, $pos + $arrayReturn['length']);
3164 1
            $arrayReturn['length'] += 1;
3165
3166 1
            if ($index == 0xFE) {
3167 1
                $strColor = str_pad(dechex($red), 2, STR_PAD_LEFT, '0');
3168 1
                $strColor .= str_pad(dechex($green), 2, STR_PAD_LEFT, '0');
3169 1
                $strColor .= str_pad(dechex($blue), 2, STR_PAD_LEFT, '0');
3170 1
            }
3171 1
        }
3172 3
        if ($masksData['align'] == 1) {
3173 3
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3174 3
            $arrayReturn['length'] += 2;
3175
            switch ($data) {
3176 3
                case 0x0000:
3177 1
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_LEFT;
3178 1
                    break;
3179 3
                case 0x0001:
3180 1
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_CENTER;
3181 1
                    break;
3182 3
                case 0x0002:
3183 3
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_RIGHT;
3184 3
                    break;
3185
                case 0x0003:
3186
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_JUSTIFY;
3187
                    break;
3188
                case 0x0004:
3189
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_DISTRIBUTED;
3190
                    break;
3191
                case 0x0005:
3192
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_DISTRIBUTED;
3193
                    break;
3194
                case 0x0006:
3195
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_JUSTIFY;
3196
                    break;
3197
                default:
3198
                    break;
3199
            }
3200 3
        }
3201 3
        if ($masksData['lineSpacing'] == 1) {
3202
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3203 3
            $arrayReturn['length'] += 2;
3204 3
        }
3205 3
        if ($masksData['spaceBefore'] == 1) {
3206
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3207 2
            $arrayReturn['length'] += 2;
3208 2
        }
3209 3
        if ($masksData['spaceAfter'] == 1) {
3210
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3211 2
            $arrayReturn['length'] += 2;
3212 2
        }
3213 3
        if ($masksData['leftMargin'] == 1) {
3214 1
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3215 1
            $arrayReturn['length'] += 2;
3216 1
            $arrayReturn['leftMargin'] = (int)round($data/6);
3217 1
        }
3218 3
        if ($masksData['indent'] == 1) {
3219 1
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3220 1
            $arrayReturn['length'] += 2;
3221 1
            $arrayReturn['indent'] = (int)round($data/6);
3222 1
        }
3223 3
        if ($masksData['defaultTabSize'] == 1) {
3224
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3225
            $arrayReturn['length'] += 2;
3226
        }
3227 3
        if ($masksData['tabStops'] == 1) {
3228
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3229
        }
3230 3
        if ($masksData['fontAlign'] == 1) {
3231
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3232
            $arrayReturn['length'] += 2;
3233
        }
3234 3
        if ($masksData['charWrap'] == 1 || $masksData['wordWrap'] == 1 || $masksData['overflow'] == 1) {
3235
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3236 3
            $arrayReturn['length'] += 2;
3237 3
        }
3238 3
        if ($masksData['textDirection'] == 1) {
3239
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3240
        }
3241
3242 3
        return $arrayReturn;
3243
    }
3244
3245
    /**
3246
     * A structure that specifies language and spelling information for a run of text.
3247
     * @param string $stream
3248
     * @param integer $pos
3249
     * @param string $strLenRT
3250
     * @return array
3251
     * @link https://msdn.microsoft.com/en-us/library/dd909603(v=office.12).aspx
3252
     */
3253 3
    private function readStructureTextSIRun($stream, $pos, $strLenRT)
3254
    {
3255
        $arrayReturn = array(
3256 3
            'length' => 0,
3257 3
            'strLenRT' => $strLenRT,
3258 3
        );
3259
3260 3
        $arrayReturn['strLenRT'] -= self::getInt4d($stream, $pos + $arrayReturn['length']);
3261 3
        $arrayReturn['length'] += 4;
3262
3263 3
        $data = self::getInt4d($stream, $pos + $arrayReturn['length']);
3264 3
        $arrayReturn['length'] += 4;
3265 3
        $masksData = array();
3266 3
        $masksData['spell'] = ($data >> 0) & bindec('1');
3267 3
        $masksData['lang'] = ($data >> 1) & bindec('1');
3268 3
        $masksData['altLang'] = ($data >> 2) & bindec('1');
3269 3
        $masksData['unused1'] = ($data >> 3) & bindec('1');
3270 3
        $masksData['unused2'] = ($data >> 4) & bindec('1');
3271 3
        $masksData['fPp10ext'] = ($data >> 5) & bindec('1');
3272 3
        $masksData['fBidi'] = ($data >> 6) & bindec('1');
3273 3
        $masksData['unused3'] = ($data >> 7) & bindec('1');
3274 3
        $masksData['reserved1'] = ($data >> 8) & bindec('1');
3275 3
        $masksData['smartTag'] = ($data >> 9) & bindec('1');
3276
3277 3
        if ($masksData['spell'] == 1) {
3278 3
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3279 3
            $arrayReturn['length'] += 2;
3280 3
            $masksSpell = array();
3281 3
            $masksSpell['error'] = ($data >> 0) & bindec('1');
3282 3
            $masksSpell['clean'] = ($data >> 1) & bindec('1');
3283 3
            $masksSpell['grammar'] = ($data >> 2) & bindec('1');
3284 3
        }
3285 3
        if ($masksData['lang'] == 1) {
3286
            // $data = self::getInt2d($stream, $pos);
3287 3
            $arrayReturn['length'] += 2;
3288 3
        }
3289 3
        if ($masksData['altLang'] == 1) {
3290
            // $data = self::getInt2d($stream, $pos);
3291 3
            $arrayReturn['length'] += 2;
3292 3
        }
3293 3
        if ($masksData['fBidi'] == 1) {
3294
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3295
        }
3296 3
        if ($masksData['fPp10ext'] == 1) {
3297
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3298
        }
3299 3
        if ($masksData['smartTag'] == 1) {
3300
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3301
        }
3302
3303 3
        return $arrayReturn;
3304
    }
3305
3306
    /**
3307
     * A structure that specifies tabbing, margins, and indentation for text.
3308
     * @param string $stream
3309
     * @param integer $pos
3310
     * @link https://msdn.microsoft.com/en-us/library/dd922749(v=office.12).aspx
3311
     */
3312 3
    private function readStructureTextRuler($stream, $pos)
3313
    {
3314
        $arrayReturn = array(
3315 3
            'length' => 0,
3316 3
        );
3317
3318 3
        $data = self::getInt4d($stream, $pos + $arrayReturn['length']);
3319 3
        $arrayReturn['length'] += 4;
3320
3321 3
        $masksData = array();
3322 3
        $masksData['fDefaultTabSize'] = ($data >> 0) & bindec('1');
3323 3
        $masksData['fCLevels'] = ($data >> 1) & bindec('1');
3324 3
        $masksData['fTabStops'] = ($data >> 2) & bindec('1');
3325 3
        $masksData['fLeftMargin1'] = ($data >> 3) & bindec('1');
3326 3
        $masksData['fLeftMargin2'] = ($data >> 4) & bindec('1');
3327 3
        $masksData['fLeftMargin3'] = ($data >> 5) & bindec('1');
3328 3
        $masksData['fLeftMargin4'] = ($data >> 6) & bindec('1');
3329 3
        $masksData['fLeftMargin5'] = ($data >> 7) & bindec('1');
3330 3
        $masksData['fIndent1'] = ($data >> 8) & bindec('1');
3331 3
        $masksData['fIndent2'] = ($data >> 9) & bindec('1');
3332 3
        $masksData['fIndent3'] = ($data >> 10) & bindec('1');
3333 3
        $masksData['fIndent4'] = ($data >> 11) & bindec('1');
3334 3
        $masksData['fIndent5'] = ($data >> 12) & bindec('1');
3335
3336 3
        if ($masksData['fCLevels'] == 1) {
3337
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3338
        }
3339 3
        if ($masksData['fDefaultTabSize'] == 1) {
3340
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
3341
        }
3342 3
        if ($masksData['fTabStops'] == 1) {
3343 3
            $count = self::getInt2d($stream, $pos + $arrayReturn['length']);
3344 3
            $arrayReturn['length'] += 2;
3345 3
            $arrayTabStops = array();
3346 3
            for ($inc = 0; $inc < $count; $inc++) {
3347 3
                $position = self::getInt2d($stream, $pos + $arrayReturn['length']);
3348 3
                $arrayReturn['length'] += 2;
3349 3
                $type = self::getInt2d($stream, $pos + $arrayReturn['length']);
3350 3
                $arrayReturn['length'] += 2;
3351 3
                $arrayTabStops[] = array(
3352 3
                    'position' => $position,
3353 3
                    'type' => $type,
3354
                );
3355 3
            }
3356 3
        }
3357 3
        if ($masksData['fLeftMargin1'] == 1) {
3358
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3359 1
            $arrayReturn['length'] += 2;
3360 1
        }
3361 3
        if ($masksData['fIndent1'] == 1) {
3362
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3363 1
            $arrayReturn['length'] += 2;
3364 1
        }
3365 3
        if ($masksData['fLeftMargin2'] == 1) {
3366
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3367 1
            $arrayReturn['length'] += 2;
3368 1
        }
3369 3
        if ($masksData['fIndent2'] == 1) {
3370
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3371 1
            $arrayReturn['length'] += 2;
3372 1
        }
3373 3
        if ($masksData['fLeftMargin3'] == 1) {
3374
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3375
            $arrayReturn['length'] += 2;
3376
        }
3377 3
        if ($masksData['fIndent3'] == 1) {
3378
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3379
            $arrayReturn['length'] += 2;
3380
        }
3381 3
        if ($masksData['fLeftMargin4'] == 1) {
3382
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3383
            $arrayReturn['length'] += 2;
3384
        }
3385 3
        if ($masksData['fIndent4'] == 1) {
3386
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3387
            $arrayReturn['length'] += 2;
3388
        }
3389 3
        if ($masksData['fLeftMargin5'] == 1) {
3390
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3391
            $arrayReturn['length'] += 2;
3392
        }
3393 3
        if ($masksData['fIndent5'] == 1) {
3394
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
3395
            $arrayReturn['length'] += 2;
3396
        }
3397
3398 3
        return $arrayReturn;
3399
    }
3400
3401
    /**
3402
     * @param $stream
3403
     * @param int $pos
3404
     * @throws \Exception
3405
     */
3406 4
    private function readRecordNotesContainer($stream, $pos)
3407
    {
3408
        // notesAtom
3409 4
        $notesAtom = $this->readRecordNotesAtom($stream, $pos);
3410 4
        $pos += $notesAtom['length'];
3411
3412
        // drawing
3413 4
        $drawing = $this->readRecordDrawingContainer($stream, $pos);
3414 3
        $pos += $drawing['length'];
3415
3416
        // slideSchemeColorSchemeAtom
3417
        // slideNameAtom
3418
        // slideProgTagsContainer
3419
        // rgNotesRoundTripAtom
3420 3
    }
3421
3422
    /**
3423
     * @param $stream
3424
     * @param int $pos
3425
     * @return array
3426
     * @throws \Exception
3427
     */
3428 4
    private function readRecordNotesAtom($stream, $pos)
3429
    {
3430
        $arrayReturn = array(
3431 4
            'length' => 0,
3432 4
        );
3433
3434 4
        $data = $this->loadRecordHeader($stream, $pos);
3435 4
        if ($data['recVer'] != 0x1 || $data['recInstance'] != 0x000 || $data['recType'] != self::RT_NOTESATOM || $data['recLen'] != 0x00000008) {
3436
            throw new \Exception('File PowerPoint 97 in error (Location : NotesAtom > RecordHeader)');
3437
        }
3438
        // Record Header
3439 4
        $arrayReturn['length'] += 8;
3440
        // NotesAtom > slideIdRef
3441 4
        $notesIdRef = self::getInt4d($stream, $pos + $arrayReturn['length']);
3442 4
        if ($notesIdRef == -2147483648) {
3443
            $notesIdRef = 0;
3444
        }
3445 4
        $this->currentNote = $notesIdRef;
3446 4
        $arrayReturn['length'] += 4;
3447
3448
        // NotesAtom > slideFlags
3449 4
        $arrayReturn['length'] += 2;
3450
        // NotesAtom > unused
3451 4
        $arrayReturn['length'] += 2;
3452
3453 4
        return $arrayReturn;
3454
    }
3455
}
3456