Completed
Push — develop ( 006a34...f4e000 )
by Franck
15:50
created

PowerPoint97::getInt1d()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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