Completed
Pull Request — develop (#322)
by Thomas
08:13 queued 03:00
created

readRecordRoundTripSlideSyncInfo12Container()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4.5923

Importance

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