PowerPoint97::readRecordNotesContainer()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

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