Completed
Pull Request — develop (#565)
by
unknown
07:14
created

PowerPoint97::readRecordPersistDirectoryAtom()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6.0073

Importance

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