Completed
Pull Request — develop (#342)
by Franck
07:35
created

PowerPoint97::readRecordOfficeArtSpgrContainer()   D

Complexity

Conditions 19
Paths 34

Size

Total Lines 63

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 27.1043

Importance

Changes 0
Metric Value
dl 0
loc 63
ccs 28
cts 39
cp 0.7179
rs 4.5166
c 0
b 0
f 0
cc 19
nc 34
nop 3
crap 27.1043

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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