Completed
Pull Request — develop (#178)
by
unknown
09:34
created

PowerPoint97::readRecordPersistDirectoryAtom()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 25
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 6.0061

Importance

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