<?php
/**
 * This file is part of PHPPresentation - A pure PHP library for reading and writing
 * presentations documents.
 *
 * PHPPresentation is free software distributed under the terms of the GNU Lesser
 * General Public License version 3 as published by the Free Software Foundation.
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code. For the full list of
 * contributors, visit https://github.com/PHPOffice/PHPPresentation/contributors.
 *
 * @link        https://github.com/PHPOffice/PHPPresentation
 * @copyright   2009-2015 PHPPresentation contributors
 * @license     http://www.gnu.org/licenses/lgpl.txt LGPL version 3
 */

namespace PhpOffice\PhpPresentation\Reader;

use PhpOffice\Common\Microsoft\OLERead;
use PhpOffice\Common\Text;
use PhpOffice\PhpPresentation\PhpPresentation;
use PhpOffice\PhpPresentation\AbstractShape;
use PhpOffice\PhpPresentation\Shape;
use PhpOffice\PhpPresentation\Shape\Drawing;
use PhpOffice\PhpPresentation\Shape\Group;
use PhpOffice\PhpPresentation\Shape\Hyperlink;
use PhpOffice\PhpPresentation\Shape\Line;
use PhpOffice\PhpPresentation\Shape\RichText;
use PhpOffice\PhpPresentation\Style\Alignment;
use PhpOffice\PhpPresentation\Style\Color;
use PhpOffice\PhpPresentation\Style\Bullet;

/**
 * Serialized format reader
 */
class PowerPoint97 implements ReaderInterface
{
    const OFFICEARTBLIPEMF = 0xF01A;
    const OFFICEARTBLIPWMF = 0xF01B;
    const OFFICEARTBLIPPICT = 0xF01C;
    const OFFICEARTBLIPJPG = 0xF01D;
    const OFFICEARTBLIPPNG = 0xF01E;
    const OFFICEARTBLIPDIB = 0xF01F;
    const OFFICEARTBLIPTIFF = 0xF029;
    const OFFICEARTBLIPJPEG = 0xF02A;

    /**
     * @link http://msdn.microsoft.com/en-us/library/dd945336(v=office.12).aspx
     */
    const RT_ANIMATIONINFO = 0x1014;
    const RT_ANIMATIONINFOATOM = 0x0FF1;
    const RT_BINARYTAGDATABLOB = 0x138B;
    const RT_BLIPCOLLECTION9 = 0x07F8;
    const RT_BLIPENTITY9ATOM = 0x07F9;
    const RT_BOOKMARKCOLLECTION = 0x07E3;
    const RT_BOOKMARKENTITYATOM = 0x0FD0;
    const RT_BOOKMARKSEEDATOM = 0x07E9;
    const RT_BROADCASTDOCINFO9 = 0x177E;
    const RT_BROADCASTDOCINFO9ATOM = 0x177F;
    const RT_BUILDATOM = 0x2B03;
    const RT_BUILDLIST = 0x2B02;
    const RT_CHARTBUILD = 0x2B04;
    const RT_CHARTBUILDATOM = 0x2B05;
    const RT_COLORSCHEMEATOM = 0x07F0;
    const RT_COMMENT10 = 0x2EE0;
    const RT_COMMENT10ATOM = 0x2EE1;
    const RT_COMMENTINDEX10 = 0x2EE4;
    const RT_COMMENTINDEX10ATOM = 0x2EE5;
    const RT_CRYPTSESSION10CONTAINER = 0x2F14;
    const RT_CURRENTUSERATOM = 0x0FF6;
    const RT_CSTRING = 0x0FBA;
    const RT_DATETIMEMETACHARATOM = 0x0FF7;
    const RT_DEFAULTRULERATOM = 0x0FAB;
    const RT_DOCROUTINGSLIPATOM = 0x0406;
    const RT_DIAGRAMBUILD = 0x2B06;
    const RT_DIAGRAMBUILDATOM = 0x2B07;
    const RT_DIFF10 = 0x2EED;
    const RT_DIFF10ATOM = 0x2EEE;
    const RT_DIFFTREE10 = 0x2EEC;
    const RT_DOCTOOLBARSTATES10ATOM = 0x36B1;
    const RT_DOCUMENT = 0x03E8;
    const RT_DOCUMENTATOM = 0x03E9;
    const RT_DRAWING = 0x040C;
    const RT_DRAWINGGROUP = 0x040B;
    const RT_ENDDOCUMENTATOM = 0x03EA;
    const RT_EXTERNALAVIMOVIE = 0x1006;
    const RT_EXTERNALCDAUDIO = 0x100E;
    const RT_EXTERNALCDAUDIOATOM = 0x1012;
    const RT_EXTERNALHYPERLINK = 0x0FD7;
    const RT_EXTERNALHYPERLINK9 = 0x0FE4;
    const RT_EXTERNALHYPERLINKATOM = 0x0FD3;
    const RT_EXTERNALHYPERLINKFLAGSATOM = 0x1018;
    const RT_EXTERNALMCIMOVIE = 0x1007;
    const RT_EXTERNALMEDIAATOM = 0x1004;
    const RT_EXTERNALMIDIAUDIO = 0x100D;
    const RT_EXTERNALOBJECTLIST = 0x0409;
    const RT_EXTERNALOBJECTLISTATOM = 0x040A;
    const RT_EXTERNALOBJECTREFATOM = 0x0BC1;
    const RT_EXTERNALOLECONTROL = 0x0FEE;
    const RT_EXTERNALOLECONTROLATOM = 0x0FFB;
    const RT_EXTERNALOLEEMBED = 0x0FCC;
    const RT_EXTERNALOLEEMBEDATOM = 0x0FCD;
    const RT_EXTERNALOLELINK = 0x0FCE;
    const RT_EXTERNALOLELINKATOM = 0x0FD1;
    const RT_EXTERNALOLEOBJECTATOM = 0x0FC3;
    const RT_EXTERNALOLEOBJECTSTG = 0x1011;
    const RT_EXTERNALVIDEO = 0x1005;
    const RT_EXTERNALWAVAUDIOEMBEDDED = 0x100F;
    const RT_EXTERNALWAVAUDIOEMBEDDEDATOM = 0x1013;
    const RT_EXTERNALWAVAUDIOLINK = 0x1010;
    const RT_ENVELOPEDATA9ATOM = 0x1785;
    const RT_ENVELOPEFLAGS9ATOM = 0x1784;
    const RT_ENVIRONMENT = 0x03F2;
    const RT_FONTCOLLECTION = 0x07D5;
    const RT_FONTCOLLECTION10 = 0x07D6;
    const RT_FONTEMBEDDATABLOB = 0x0FB8;
    const RT_FONTEMBEDFLAGS10ATOM = 0x32C8;
    const RT_FILTERPRIVACYFLAGS10ATOM = 0x36B0;
    const RT_FONTENTITYATOM = 0x0FB7;
    const RT_FOOTERMETACHARATOM = 0x0FFA;
    const RT_GENERICDATEMETACHARATOM = 0x0FF8;
    const RT_GRIDSPACING10ATOM = 0x040D;
    const RT_GUIDEATOM = 0x03FB;
    const RT_HANDOUT = 0x0FC9;
    const RT_HASHCODEATOM = 0x2B00;
    const RT_HEADERSFOOTERS = 0x0FD9;
    const RT_HEADERSFOOTERSATOM = 0x0FDA;
    const RT_HEADERMETACHARATOM = 0x0FF9;
    const RT_HTMLDOCINFO9ATOM = 0x177B;
    const RT_HTMLPUBLISHINFOATOM = 0x177C;
    const RT_HTMLPUBLISHINFO9 = 0x177D;
    const RT_INTERACTIVEINFO = 0x0FF2;
    const RT_INTERACTIVEINFOATOM = 0x0FF3;
    const RT_KINSOKU = 0x0FC8;
    const RT_KINSOKUATOM = 0x0FD2;
    const RT_LEVELINFOATOM = 0x2B0A;
    const RT_LINKEDSHAPE10ATOM = 0x2EE6;
    const RT_LINKEDSLIDE10ATOM = 0x2EE7;
    const RT_LIST = 0x07D0;
    const RT_MAINMASTER = 0x03F8;
    const RT_MASTERTEXTPROPATOM = 0x0FA2;
    const RT_METAFILE = 0x0FC1;
    const RT_NAMEDSHOW = 0x0411;
    const RT_NAMEDSHOWS = 0x0410;
    const RT_NAMEDSHOWSLIDESATOM = 0x0412;
    const RT_NORMALVIEWSETINFO9 = 0x0414;
    const RT_NORMALVIEWSETINFO9ATOM = 0x0415;
    const RT_NOTES= 0x03F0;
    const RT_NOTESATOM = 0x03F1;
    const RT_NOTESTEXTVIEWINFO9 = 0x0413;
    const RT_OUTLINETEXTPROPS9 = 0x0FAE;
    const RT_OUTLINETEXTPROPS10 = 0x0FB3;
    const RT_OUTLINETEXTPROPS11 = 0x0FB5;
    const RT_OUTLINETEXTPROPSHEADER9ATOM = 0x0FAF;
    const RT_OUTLINETEXTREFATOM = 0x0F9E;
    const RT_OUTLINEVIEWINFO = 0x0407;
    const RT_PERSISTDIRECTORYATOM = 0x1772;
    const RT_PARABUILD = 0x2B08;
    const RT_PARABUILDATOM = 0x2B09;
    const RT_PHOTOALBUMINFO10ATOM = 0x36B2;
    const RT_PLACEHOLDERATOM = 0x0BC3;
    const RT_PRESENTATIONADVISORFLAGS9ATOM = 0x177A;
    const RT_PRINTOPTIONSATOM = 0x1770;
    const RT_PROGBINARYTAG = 0x138A;
    const RT_PROGSTRINGTAG = 0x1389;
    const RT_PROGTAGS = 0x1388;
    const RT_RECOLORINFOATOM = 0x0FE7;
    const RT_RTFDATETIMEMETACHARATOM = 0x1015;
    const RT_ROUNDTRIPANIMATIONATOM12ATOM = 0x2B0B;
    const RT_ROUNDTRIPANIMATIONHASHATOM12ATOM = 0x2B0D;
    const RT_ROUNDTRIPCOLORMAPPING12ATOM = 0x040F;
    const RT_ROUNDTRIPCOMPOSITEMASTERID12ATOM = 0x041D;
    const RT_ROUNDTRIPCONTENTMASTERID12ATOM = 0x0422;
    const RT_ROUNDTRIPCONTENTMASTERINFO12ATOM = 0x041E;
    const RT_ROUNDTRIPCUSTOMTABLESTYLES12ATOM = 0x0428;
    const RT_ROUNDTRIPDOCFLAGS12ATOM = 0x0425;
    const RT_ROUNDTRIPHEADERFOOTERDEFAULTS12ATOM = 0x0424;
    const RT_ROUNDTRIPHFPLACEHOLDER12ATOM = 0x0420;
    const RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM = 0x0BDD;
    const RT_ROUNDTRIPNOTESMASTERTEXTSTYLES12ATOM = 0x0427;
    const RT_ROUNDTRIPOARTTEXTSTYLES12ATOM = 0x0423;
    const RT_ROUNDTRIPORIGINALMAINMASTERID12ATOM = 0x041C;
    const RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM = 0x0426;
    const RT_ROUNDTRIPSHAPEID12ATOM = 0x041F;
    const RT_ROUNDTRIPSLIDESYNCINFO12 = 0x3714;
    const RT_ROUNDTRIPSLIDESYNCINFOATOM12 = 0x3715;
    const RT_ROUNDTRIPTHEME12ATOM = 0x040E;
    const RT_SHAPEATOM = 0x0BDB;
    const RT_SHAPEFLAGS10ATOM = 0x0BDC;
    const RT_SLIDE = 0x03EE;
    const RT_SLIDEATOM = 0x03EF;
    const RT_SLIDEFLAGS10ATOM = 0x2EEA;
    const RT_SLIDELISTENTRY10ATOM = 0x2EF0;
    const RT_SLIDELISTTABLE10 = 0x2EF1;
    const RT_SLIDELISTWITHTEXT = 0x0FF0;
    const RT_SLIDELISTTABLESIZE10ATOM = 0x2EEF;
    const RT_SLIDENUMBERMETACHARATOM = 0x0FD8;
    const RT_SLIDEPERSISTATOM = 0x03F3;
    const RT_SLIDESHOWDOCINFOATOM = 0x0401;
    const RT_SLIDESHOWSLIDEINFOATOM = 0x03F9;
    const RT_SLIDETIME10ATOM = 0x2EEB;
    const RT_SLIDEVIEWINFO = 0x03FA;
    const RT_SLIDEVIEWINFOATOM = 0x03FE;
    const RT_SMARTTAGSTORE11CONTAINER = 0x36B3;
    const RT_SOUND = 0x07E6;
    const RT_SOUNDCOLLECTION = 0x07E4;
    const RT_SOUNDCOLLECTIONATOM = 0x07E5;
    const RT_SOUNDDATABLOB = 0x07E7;
    const RT_SORTERVIEWINFO = 0x0408;
    const RT_STYLETEXTPROPATOM = 0x0FA1;
    const RT_STYLETEXTPROP10ATOM = 0x0FB1;
    const RT_STYLETEXTPROP11ATOM = 0x0FB6;
    const RT_STYLETEXTPROP9ATOM = 0x0FAC;
    const RT_SUMMARY = 0x0402;
    const RT_TEXTBOOKMARKATOM = 0x0FA7;
    const RT_TEXTBYTESATOM = 0x0FA8;
    const RT_TEXTCHARFORMATEXCEPTIONATOM = 0x0FA4;
    const RT_TEXTCHARSATOM = 0x0FA0;
    const RT_TEXTDEFAULTS10ATOM = 0x0FB4;
    const RT_TEXTDEFAULTS9ATOM = 0x0FB0;
    const RT_TEXTHEADERATOM = 0x0F9F;
    const RT_TEXTINTERACTIVEINFOATOM = 0x0FDF;
    const RT_TEXTMASTERSTYLEATOM = 0x0FA3;
    const RT_TEXTMASTERSTYLE10ATOM = 0x0FB2;
    const RT_TEXTMASTERSTYLE9ATOM = 0x0FAD;
    const RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM = 0x0FA5;
    const RT_TEXTRULERATOM = 0x0FA6;
    const RT_TEXTSPECIALINFOATOM = 0x0FAA;
    const RT_TEXTSPECIALINFODEFAULTATOM = 0x0FA9;
    const RT_TIMEANIMATEBEHAVIOR = 0xF134;
    const RT_TIMEANIMATEBEHAVIORCONTAINER = 0xF12B;
    const RT_TIMEANIMATIONVALUE = 0xF143;
    const RT_TIMEANIMATIONVALUELIST = 0xF13F;
    const RT_TIMEBEHAVIOR = 0xF133;
    const RT_TIMEBEHAVIORCONTAINER = 0xF12A;
    const RT_TIMECOLORBEHAVIOR = 0xF135;
    const RT_TIMECOLORBEHAVIORCONTAINER = 0xF12C;
    const RT_TIMECLIENTVISUALELEMENT = 0xF13C;
    const RT_TIMECOMMANDBEHAVIOR = 0xF13B;
    const RT_TIMECOMMANDBEHAVIORCONTAINER = 0xF132;
    const RT_TIMECONDITION = 0xF128;
    const RT_TIMECONDITIONCONTAINER = 0xF125;
    const RT_TIMEEFFECTBEHAVIOR = 0xF136;
    const RT_TIMEEFFECTBEHAVIORCONTAINER = 0xF12D;
    const RT_TIMEEXTTIMENODECONTAINER = 0xF144;
    const RT_TIMEITERATEDATA = 0xF140;
    const RT_TIMEMODIFIER = 0xF129;
    const RT_TIMEMOTIONBEHAVIOR = 0xF137;
    const RT_TIMEMOTIONBEHAVIORCONTAINER = 0xF12E;
    const RT_TIMENODE = 0xF127;
    const RT_TIMEPROPERTYLIST = 0xF13D;
    const RT_TIMEROTATIONBEHAVIOR = 0xF138;
    const RT_TIMEROTATIONBEHAVIORCONTAINER = 0xF12F;
    const RT_TIMESCALEBEHAVIOR = 0xF139;
    const RT_TIMESCALEBEHAVIORCONTAINER = 0xF130;
    const RT_TIMESEQUENCEDATA = 0xF141;
    const RT_TIMESETBEHAVIOR = 0xF13A;
    const RT_TIMESETBEHAVIORCONTAINER = 0xF131;
    const RT_TIMESUBEFFECTCONTAINER = 0xF145;
    const RT_TIMEVARIANT = 0xF142;
    const RT_TIMEVARIANTLIST = 0xF13E;
    const RT_USEREDITATOM = 0x0FF5;
    const RT_VBAINFO = 0x03FF;
    const RT_VBAINFOATOM = 0x0400;
    const RT_VIEWINFOATOM = 0x03FD;
    const RT_VISUALPAGEATOM = 0x2B01;
    const RT_VISUALSHAPEATOM = 0x2AFB;

    /**
     * @var http://msdn.microsoft.com/en-us/library/dd926394(v=office.12).aspx
     */
    const SL_BIGOBJECT = 0x0000000F;
    const SL_BLANK = 0x00000010;
    const SL_COLUMNTWOROWS = 0x0000000A;
    const SL_FOUROBJECTS = 0x0000000E;
    const SL_MASTERTITLE = 0x00000002;
    const SL_TITLEBODY = 0x00000001;
    const SL_TITLEONLY = 0x00000007;
    const SL_TITLESLIDE = 0x00000000;
    const SL_TWOCOLUMNS = 0x00000008;
    const SL_TWOCOLUMNSROW = 0x0000000D;
    const SL_TWOROWS = 0x00000009;
    const SL_TWOROWSCOLUMN = 0x0000000B;
    const SL_VERTICALTITLEBODY = 0x00000011;
    const SL_VERTICALTWOROWS = 0x00000012;

    /**
     * Array with Fonts
     */
    private $arrayFonts = array();
    /**
     * Array with Hyperlinks
     */
    private $arrayHyperlinks = array();
    /**
     * Array with Notes
     */
    private $arrayNotes = array();
    /**
     * Array with Pictures
     */
    private $arrayPictures = array();
    /**
     * Offset (in bytes) from the beginning of the PowerPoint Document Stream to the UserEditAtom record for the most recent user edit.
     * @var int
     */
    private $offsetToCurrentEdit;
    /**
     * A structure that specifies a compressed table of sequential persist object identifiers and stream offsets to associated persist objects.
     * @var int[]
     */
    private $rgPersistDirEntry;
    /**
     * Offset (in bytes) from the beginning of the PowerPoint Document Stream to the PersistDirectoryAtom record for this user edit
     * @var int
     */
    private $offsetPersistDirectory;
    /**
     * Output Object
     * @var PhpPresentation
     */
    private $oPhpPresentation;
    /**
     * Group Object
     * @var Group
     */
    private $oCurrentGroup;
    /**
     * @var boolean
     */
    private $bFirstShapeGroup = false;
    /**
     * Stream "Powerpoint Document"
     * @var string
     */
    private $streamPowerpointDocument;
    /**
     * Stream "Current User"
     * @var string
     */
    private $streamCurrentUser;
    /**
     * Stream "Summary Information"
     * @var string
     */
    private $streamSummaryInformation;
    /**
     * Stream "Document Summary Information"
     * @var string
     */
    private $streamDocumentSummaryInformation;
    /**
     * Stream "Pictures"
     * @var string
     */
    private $streamPictures;
    /**
     * @var integer
     */
    private $inMainType;
    /**
     * @var integer
     */
    private $currentNote;

    /**
     * Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
     *
     * @param  string $pFilename
     * @throws \Exception
     * @return boolean
     */
    public function canRead($pFilename)
    {
        return $this->fileSupportsUnserializePhpPresentation($pFilename);
    }

    /**
     * Does a file support UnserializePhpPresentation ?
     *
     * @param  string    $pFilename
     * @throws \Exception
     * @return boolean
     */
    public function fileSupportsUnserializePhpPresentation($pFilename = '')
    {
        // Check if file exists
        if (!file_exists($pFilename)) {
            throw new \Exception("Could not open " . $pFilename . " for reading! File does not exist.");
        }

        try {
            // Use ParseXL for the hard work.
            $ole = new OLERead();
            // get excel data
            $ole->read($pFilename);
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * Loads PhpPresentation Serialized file
     *
     * @param  string        $pFilename
     * @return \PhpOffice\PhpPresentation\PhpPresentation
     * @throws \Exception
     */
    public function load($pFilename)
    {
        // Unserialize... First make sure the file supports it!
        if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
            throw new \Exception("Invalid file format for PhpOffice\PhpPresentation\Reader\PowerPoint97: " . $pFilename . ".");
        }

        return $this->loadFile($pFilename);
    }

    /**
     * Load PhpPresentation Serialized file
     *
     * @param  string $pFilename
     * @return \PhpOffice\PhpPresentation\PhpPresentation
     * @throws \Exception
     */
    private function loadFile($pFilename)
    {
        $this->oPhpPresentation = new PhpPresentation();
        $this->oPhpPresentation->removeSlideByIndex();

        // Read OLE Blocks
        $this->loadOLE($pFilename);
        // Read pictures in the Pictures Stream
        $this->loadPicturesStream();
        // Read information in the Current User Stream
        $this->loadCurrentUserStream();
        // Read information in the PowerPoint Document Stream
        $this->loadPowerpointDocumentStream();

        return $this->oPhpPresentation;
    }

    /**
     * Read OLE Part
     * @param string $pFilename
     * @throws \Exception
     */
    private function loadOLE($pFilename)
    {
        // OLE reader
        $oOLE = new OLERead();
        $oOLE->read($pFilename);

        // PowerPoint Document Stream
        $this->streamPowerpointDocument = $oOLE->getStream($oOLE->powerpointDocument);

        // Current User Stream
        $this->streamCurrentUser = $oOLE->getStream($oOLE->currentUser);

        // Get summary information data
        $this->streamSummaryInformation = $oOLE->getStream($oOLE->summaryInformation);

        // Get additional document summary information data
        $this->streamDocumentSummaryInformation = $oOLE->getStream($oOLE->docSummaryInfos);

        // Get pictures data
        $this->streamPictures = $oOLE->getStream($oOLE->pictures);
    }

    /**
     * Stream Pictures
     * @link http://msdn.microsoft.com/en-us/library/dd920746(v=office.12).aspx
     */
    private function loadPicturesStream()
    {
        $stream = $this->streamPictures;

        $pos = 0;
        do {
            $arrayRH = $this->loadRecordHeader($stream, $pos);
            $pos += 8;
            $readSuccess = false;
            if ($arrayRH['recVer'] == 0x00 && ($arrayRH['recType'] == 0xF007 || ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117))) {
                //@link : http://msdn.microsoft.com/en-us/library/dd950560(v=office.12).aspx
                if ($arrayRH['recType'] == 0xF007) {
                    // OfficeArtFBSE
                    throw new \Exception('Feature not implemented (l.'.__LINE__.')');
                }
                if ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117) {
                    $arrayRecord = $this->readRecordOfficeArtBlip($stream, $pos - 8);
                    if ($arrayRecord['length'] > 0) {
                        $pos += $arrayRecord['length'];
                        $this->arrayPictures[] = $arrayRecord['picture'];
                    }
                }
                $readSuccess = true;
            }
        } while ($readSuccess === true);
    }

    /**
     * Stream Current User
     * @link http://msdn.microsoft.com/en-us/library/dd908567(v=office.12).aspx
     */
    private function loadCurrentUserStream()
    {
        $pos = 0;

        /**
         * CurrentUserAtom : http://msdn.microsoft.com/en-us/library/dd948895(v=office.12).aspx
         */
        // RecordHeader : http://msdn.microsoft.com/en-us/library/dd926377(v=office.12).aspx
        $rHeader = $this->loadRecordHeader($this->streamCurrentUser, $pos);
        $pos += 8;
        if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_CURRENTUSERATOM) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > RecordHeader).');
        }

        // Size
        $size = self::getInt4d($this->streamCurrentUser, $pos);
        $pos += 4;
        if ($size !=  0x00000014) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > Size).');
        }

        // headerToken
        $headerToken = self::getInt4d($this->streamCurrentUser, $pos);
        $pos += 4;
        if ($headerToken == 0xF3D1C4DF && $headerToken != 0xE391C05F) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.') : Encrypted file');
        }

        // offsetToCurrentEdit
        $this->offsetToCurrentEdit = self::getInt4d($this->streamCurrentUser, $pos);
        $pos += 4;

        // lenUserName
        $lenUserName = self::getInt2d($this->streamCurrentUser, $pos);
        $pos += 2;
        if ($lenUserName > 255) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > lenUserName).');
        }

        // docFileVersion
        $docFileVersion = self::getInt2d($this->streamCurrentUser, $pos);
        $pos += 2;
        if ($docFileVersion != 0x03F4) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > docFileVersion).');
        }

        // majorVersion
        $majorVersion = self::getInt1d($this->streamCurrentUser, $pos);
        $pos += 1;
        if ($majorVersion != 0x03) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > majorVersion).');
        }

        // minorVersion
        $minorVersion = self::getInt1d($this->streamCurrentUser, $pos);
        $pos += 1;
        if ($minorVersion != 0x00) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > minorVersion).');
        }

        // unused
        $pos += 2;

        // ansiUserName
        $ansiUserName = '';
        do {
            $char = self::getInt1d($this->streamCurrentUser, $pos);
            if (($char >= 0x00 && $char <= 0x1F) || ($char >= 0x7F && $char <= 0x9F)) {
                $char = false;
            } else {
                $ansiUserName .= chr($char);
                $pos += 1;
            }
        } while ($char !== false);

        // relVersion
        $relVersion = self::getInt4d($this->streamCurrentUser, $pos);
        $pos += 4;
        if ($relVersion != 0x00000008 && $relVersion != 0x00000009) {
            throw new \Exception('File PowerPoint 97 in error (Location : CurrentUserAtom > relVersion).');
        }

        // unicodeUserName
        $unicodeUserName = '';
        for ($inc = 0; $inc < $lenUserName; $inc++) {
            $char = self::getInt2d($this->streamCurrentUser, $pos);
            if (($char >= 0x00 && $char <= 0x1F) || ($char >= 0x7F && $char <= 0x9F)) {
                break;
            }
            $unicodeUserName .= chr($char);
            $pos += 2;
        }
    }

    /**
     * Stream Powerpoint Document
     * @link http://msdn.microsoft.com/en-us/library/dd921564(v=office.12).aspx
     */
    private function loadPowerpointDocumentStream()
    {
        $this->readRecordUserEditAtom($this->streamPowerpointDocument, $this->offsetToCurrentEdit);

        $this->readRecordPersistDirectoryAtom($this->streamPowerpointDocument, $this->offsetPersistDirectory);

        foreach ($this->rgPersistDirEntry as $offsetDir) {
            $pos = $offsetDir;

            $rh = $this->loadRecordHeader($this->streamPowerpointDocument, $pos);
            $pos += 8;
            $this->inMainType = $rh['recType'];
            $this->currentNote = null;
            switch ($rh['recType']) {
                case self::RT_DOCUMENT:
                    $this->readRecordDocumentContainer($this->streamPowerpointDocument, $pos);
                    break;
                case self::RT_NOTES:
                    $this->readRecordNotesContainer($this->streamPowerpointDocument, $pos);
                    break;
                case self::RT_SLIDE:
                    $this->readRecordSlideContainer($this->streamPowerpointDocument, $pos);
                    break;
                default:
                    // throw new \Exception('Feature not implemented : l.'.__LINE__.'('.dechex($rh['recType']).')');
                    break;
            }
        }
    }

    /**
     * Read a record header
     * @param string $stream
     * @param integer $pos
     * @return array
     */
    private function loadRecordHeader($stream, $pos)
    {
        $rec = self::getInt2d($stream, $pos);
        $recType = self::getInt2d($stream, $pos + 2);
        $recLen = self::getInt4d($stream, $pos + 4);
        return array(
            'recVer' => ($rec >> 0) & bindec('1111'),
            'recInstance' => ($rec >> 4) & bindec('111111111111'),
            'recType' => $recType,
            'recLen' => $recLen,
        );
    }

    /**
     * Read 8-bit unsigned integer
     *
     * @param string $data
     * @param int $pos
     * @return int
     */
    public static function getInt1d($data, $pos)
    {
        return ord($data[$pos]);
    }

    /**
     * Read 16-bit unsigned integer
     *
     * @param string $data
     * @param int $pos
     * @return int
     */
    public static function getInt2d($data, $pos)
    {
        return ord($data[$pos]) | (ord($data[$pos+1]) << 8);
    }

    /**
     * Read 32-bit signed integer
     *
     * @param string $data
     * @param int $pos
     * @return int
     */
    public static function getInt4d($data, $pos)
    {
        // FIX: represent numbers correctly on 64-bit system
        // http://sourceforge.net/tracker/index.php?func=detail&aid=1487372&group_id=99160&atid=623334
        // Hacked by Andreas Rehm 2006 to ensure correct result of the <<24 block on 32 and 64bit systems
        $or24 = ord($data[$pos + 3]);

        $ord24 = ($or24 & 127) << 24;
        if ($or24 >= 128) {
            // negative number
            $ord24 = -abs((256 - $or24) << 24);
        }
        return ord($data[$pos]) | (ord($data[$pos+1]) << 8) | (ord($data[$pos+2]) << 16) | $ord24;
    }

    /**
     * A container record that specifies the animation and sound information for a shape.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd772900(v=office.12).aspx
     */
    private function readRecordAnimationInfoContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ANIMATIONINFO) {
            // Record Header
            $arrayReturn['length'] += 8;
            // animationAtom
            // animationSound
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies information about the document.
     * @param string $stream
     * @param integer $pos
     * @throws \Exception
     * @link http://msdn.microsoft.com/en-us/library/dd947357(v=office.12).aspx
     */
    private function readRecordDocumentContainer($stream, $pos)
    {
        $documentAtom = $this->loadRecordHeader($stream, $pos);
        $pos += 8;
        if ($documentAtom['recVer'] != 0x1 || $documentAtom['recInstance'] != 0x000 || $documentAtom['recType'] != self::RT_DOCUMENTATOM) {
            throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom).');
        }
        $pos += $documentAtom['recLen'];

        $exObjList = $this->loadRecordHeader($stream, $pos);
        if ($exObjList['recVer'] == 0xF && $exObjList['recInstance'] == 0x000 && $exObjList['recType'] == self::RT_EXTERNALOBJECTLIST) {
            $pos += 8;
            // exObjListAtom > rh
            $exObjListAtom = $this->loadRecordHeader($stream, $pos);
            if ($exObjListAtom['recVer'] != 0x0 || $exObjListAtom['recInstance'] != 0x000 || $exObjListAtom['recType'] != self::RT_EXTERNALOBJECTLISTATOM || $exObjListAtom['recLen'] != 0x00000004) {
                throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom > exObjList > exObjListAtom).');
            }
            $pos += 8;
            // exObjListAtom > exObjIdSeed
            $pos += 4;
            // rgChildRec
            $exObjList['recLen'] -= 12;
            do {
                $childRec = $this->loadRecordHeader($stream, $pos);
                $pos += 8;
                $exObjList['recLen'] -= 8;
                switch ($childRec['recType']) {
                    case self::RT_EXTERNALHYPERLINK:
                        //@link : http://msdn.microsoft.com/en-us/library/dd944995(v=office.12).aspx
                        // exHyperlinkAtom > rh
                        $exHyperlinkAtom = $this->loadRecordHeader($stream, $pos);
                        if ($exHyperlinkAtom['recVer'] != 0x0 || $exHyperlinkAtom['recInstance'] != 0x000 || $exHyperlinkAtom['recType'] != self::RT_EXTERNALHYPERLINKATOM || $exObjListAtom['recLen'] != 0x00000004) {
                            throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > DocumentAtom > exObjList > rgChildRec > RT_ExternalHyperlink).');
                        }
                        $pos += 8;
                        $exObjList['recLen'] -= 8;
                        // exHyperlinkAtom > exHyperlinkId
                        $exHyperlinkId = self::getInt4d($stream, $pos);
                        $pos += 4;
                        $exObjList['recLen'] -= 4;

                        $this->arrayHyperlinks[$exHyperlinkId] = array();
                        // friendlyNameAtom
                        $friendlyNameAtom  = $this->loadRecordHeader($stream, $pos);
                        if ($friendlyNameAtom['recVer'] == 0x0 && $friendlyNameAtom['recInstance'] == 0x000 && $friendlyNameAtom['recType'] == self::RT_CSTRING && $friendlyNameAtom['recLen'] % 2 == 0) {
                            $pos += 8;
                            $exObjList['recLen'] -= 8;
                            $this->arrayHyperlinks[$exHyperlinkId]['text'] = '';
                            for ($inc = 0; $inc < ($friendlyNameAtom['recLen'] / 2); $inc++) {
                                $char = self::getInt2d($stream, $pos);
                                $pos += 2;
                                $exObjList['recLen'] -= 2;
                                $this->arrayHyperlinks[$exHyperlinkId]['text'] .= chr($char);
                            }
                        }
                        // targetAtom
                        $targetAtom  = $this->loadRecordHeader($stream, $pos);
                        if ($targetAtom['recVer'] == 0x0 && $targetAtom['recInstance'] == 0x001 && $targetAtom['recType'] == self::RT_CSTRING && $targetAtom['recLen'] % 2 == 0) {
                            $pos += 8;
                            $exObjList['recLen'] -= 8;
                            $this->arrayHyperlinks[$exHyperlinkId]['url'] = '';
                            for ($inc = 0; $inc < ($targetAtom['recLen'] / 2); $inc++) {
                                $char = self::getInt2d($stream, $pos);
                                $pos += 2;
                                $exObjList['recLen'] -= 2;
                                $this->arrayHyperlinks[$exHyperlinkId]['url'] .= chr($char);
                            }
                        }
                        // locationAtom
                        $locationAtom  = $this->loadRecordHeader($stream, $pos);
                        if ($locationAtom['recVer'] == 0x0 && $locationAtom['recInstance'] == 0x003 && $locationAtom['recType'] == self::RT_CSTRING && $locationAtom['recLen'] % 2 == 0) {
                            $pos += 8;
                            $exObjList['recLen'] -= 8;
                            $string = '';
                            for ($inc = 0; $inc < ($locationAtom['recLen'] / 2); $inc++) {
                                $char = self::getInt2d($stream, $pos);
                                $pos += 2;
                                $exObjList['recLen'] -= 2;
                                $string .= chr($char);
                            }
                        }
                        break;
                    default:
                        throw new \Exception('Feature not implemented (l.'.__LINE__.' : '.dechex($childRec['recType'].')'));
                }
            } while ($exObjList['recLen'] > 0);
        }

        //@link : http://msdn.microsoft.com/en-us/library/dd907813(v=office.12).aspx
        $documentTextInfo = $this->loadRecordHeader($stream, $pos);
        if ($documentTextInfo['recVer'] == 0xF && $documentTextInfo['recInstance'] == 0x000 && $documentTextInfo['recType'] == self::RT_ENVIRONMENT) {
            $pos += 8;
            //@link : http://msdn.microsoft.com/en-us/library/dd952717(v=office.12).aspx
            $kinsoku = $this->loadRecordHeader($stream, $pos);
            if ($kinsoku['recVer'] == 0xF && $kinsoku['recInstance'] == 0x002 && $kinsoku['recType'] == self::RT_KINSOKU) {
                $pos += 8;
                $pos += $kinsoku['recLen'];
            }

            //@link : http://msdn.microsoft.com/en-us/library/dd948152(v=office.12).aspx
            $fontCollection = $this->loadRecordHeader($stream, $pos);
            if ($fontCollection['recVer'] == 0xF && $fontCollection['recInstance'] == 0x000 && $fontCollection['recType'] == self::RT_FONTCOLLECTION) {
                $pos += 8;
                do {
                    $fontEntityAtom = $this->loadRecordHeader($stream, $pos);
                    $pos += 8;
                    $fontCollection['recLen'] -= 8;
                    if ($fontEntityAtom['recVer'] != 0x0 || $fontEntityAtom['recInstance'] > 128 || $fontEntityAtom['recType'] != self::RT_FONTENTITYATOM) {
                        throw new \Exception('File PowerPoint 97 in error (Location : RTDocument > RT_Environment > RT_FontCollection > RT_FontEntityAtom).');
                    }
                    $string = '';
                    for ($inc = 0; $inc < 32; $inc++) {
                        $char = self::getInt2d($stream, $pos);
                        $pos += 2;
                        $fontCollection['recLen'] -= 2;
                        $string .= chr($char);
                    }
                    $this->arrayFonts[] = $string;

                    // lfCharSet (1 byte)
                    $pos += 1;
                    $fontCollection['recLen'] -= 1;

                    // fEmbedSubsetted (1 bit)
                    // unused (7 bits)
                    $pos += 1;
                    $fontCollection['recLen'] -= 1;

                    // rasterFontType (1 bit)
                    // deviceFontType (1 bit)
                    // truetypeFontType (1 bit)
                    // fNoFontSubstitution (1 bit)
                    // reserved (4 bits)
                    $pos += 1;
                    $fontCollection['recLen'] -= 1;

                    // lfPitchAndFamily (1 byte)
                    $pos += 1;
                    $fontCollection['recLen'] -= 1;

                    $fontEmbedData1 = $this->loadRecordHeader($stream, $pos);
                    if ($fontEmbedData1['recVer'] == 0x0 && $fontEmbedData1['recInstance'] >= 0x000 && $fontEmbedData1['recInstance'] <= 0x003 && $fontEmbedData1['recType'] == self::RT_FONTEMBEDDATABLOB) {
                        $pos += 8;
                        $fontCollection['recLen'] -= 8;
                        $pos += $fontEmbedData1['recLen'];
                        $fontCollection['recLen'] -= $fontEmbedData1['recLen'];
                    }

                    $fontEmbedData2 = $this->loadRecordHeader($stream, $pos);
                    if ($fontEmbedData2['recVer'] == 0x0 && $fontEmbedData2['recInstance'] >= 0x000 && $fontEmbedData2['recInstance'] <= 0x003 && $fontEmbedData2['recType'] == self::RT_FONTEMBEDDATABLOB) {
                        $pos += 8;
                        $fontCollection['recLen'] -= 8;
                        $pos += $fontEmbedData2['recLen'];
                        $fontCollection['recLen'] -= $fontEmbedData2['recLen'];
                    }

                    $fontEmbedData3 = $this->loadRecordHeader($stream, $pos);
                    if ($fontEmbedData3['recVer'] == 0x0 && $fontEmbedData3['recInstance'] >= 0x000 && $fontEmbedData3['recInstance'] <= 0x003 && $fontEmbedData3['recType'] == self::RT_FONTEMBEDDATABLOB) {
                        $pos += 8;
                        $fontCollection['recLen'] -= 8;
                        $pos += $fontEmbedData3['recLen'];
                        $fontCollection['recLen'] -= $fontEmbedData3['recLen'];
                    }

                    $fontEmbedData4 = $this->loadRecordHeader($stream, $pos);
                    if ($fontEmbedData4['recVer'] == 0x0 && $fontEmbedData4['recInstance'] >= 0x000 && $fontEmbedData4['recInstance'] <= 0x003 && $fontEmbedData4['recType'] == self::RT_FONTEMBEDDATABLOB) {
                        $pos += 8;
                        $fontCollection['recLen'] -= 8;
                        $pos += $fontEmbedData4['recLen'];
                        $fontCollection['recLen'] -= $fontEmbedData4['recLen'];
                    }
                } while ($fontCollection['recLen'] > 0);
            }

            $textCFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
            if ($textCFDefaultsAtom['recVer'] == 0x0 && $textCFDefaultsAtom['recInstance'] == 0x000 && $textCFDefaultsAtom['recType'] == self::RT_TEXTCHARFORMATEXCEPTIONATOM) {
                $pos += 8;
                $pos += $textCFDefaultsAtom['recLen'];
            }

            $textPFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
            if ($textPFDefaultsAtom['recVer'] == 0x0 && $textPFDefaultsAtom['recInstance'] == 0x000 && $textPFDefaultsAtom['recType'] == self::RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM) {
                $pos += 8;
                $pos += $textPFDefaultsAtom['recLen'];
            }

            $defaultRulerAtom = $this->loadRecordHeader($stream, $pos);
            if ($defaultRulerAtom['recVer'] == 0x0 && $defaultRulerAtom['recInstance'] == 0x000 && $defaultRulerAtom['recType'] == self::RT_DEFAULTRULERATOM) {
                $pos += 8;
                $pos += $defaultRulerAtom['recLen'];
            }

            $textSIDefaultsAtom = $this->loadRecordHeader($stream, $pos);
            if ($textSIDefaultsAtom['recVer'] == 0x0 && $textSIDefaultsAtom['recInstance'] == 0x000 && $textSIDefaultsAtom['recType'] == self::RT_TEXTSPECIALINFODEFAULTATOM) {
                $pos += 8;
                $pos += $textSIDefaultsAtom['recLen'];
            }

            $textMasterStyleAtom = $this->loadRecordHeader($stream, $pos);
            if ($textMasterStyleAtom['recVer'] == 0x0 && $textMasterStyleAtom['recType'] == self::RT_TEXTMASTERSTYLEATOM) {
                $pos += 8;
                $pos += $textMasterStyleAtom['recLen'];
            }
        }

        $soundCollection = $this->loadRecordHeader($stream, $pos);
        if ($soundCollection['recVer'] == 0xF && $soundCollection['recInstance'] == 0x005 && $soundCollection['recType'] == self::RT_SOUNDCOLLECTION) {
            $pos += 8;
            $pos += $soundCollection['recLen'];
        }

        $drawingGroup = $this->loadRecordHeader($stream, $pos);
        if ($drawingGroup['recVer'] == 0xF && $drawingGroup['recInstance'] == 0x000 && $drawingGroup['recType'] == self::RT_DRAWINGGROUP) {
            $drawing = $this->readRecordDrawingGroupContainer($stream, $pos);
            $pos += 8;
            $pos += $drawing['length'];
        }

        $masterList = $this->loadRecordHeader($stream, $pos);
        if ($masterList['recVer'] == 0xF && $masterList['recInstance'] == 0x001 && $masterList['recType'] == self::RT_SLIDELISTWITHTEXT) {
            $pos += 8;
            $pos += $masterList['recLen'];
        }

        $docInfoList = $this->loadRecordHeader($stream, $pos);
        if ($docInfoList['recVer'] == 0xF && $docInfoList['recInstance'] == 0x000 && $docInfoList['recType'] == self::RT_LIST) {
            $pos += 8;
            $pos += $docInfoList['recLen'];
        }

        $slideHF = $this->loadRecordHeader($stream, $pos);
        if ($slideHF['recVer'] == 0xF && $slideHF['recInstance'] == 0x003 && $slideHF['recType'] == self::RT_HEADERSFOOTERS) {
            $pos += 8;
            $pos += $slideHF['recLen'];
        }

        $notesHF = $this->loadRecordHeader($stream, $pos);
        if ($notesHF['recVer'] == 0xF && $notesHF['recInstance'] == 0x004 && $notesHF['recType'] == self::RT_HEADERSFOOTERS) {
            $pos += 8;
            $pos += $notesHF['recLen'];
        }

        // SlideListWithTextContainer
        $slideList = $this->loadRecordHeader($stream, $pos);
        if ($slideList['recVer'] == 0xF && $slideList['recInstance'] == 0x000 && $slideList['recType'] == self::RT_SLIDELISTWITHTEXT) {
            $pos += 8;
            do {
                // SlideListWithTextSubContainerOrAtom
                $rhSlideList = $this->loadRecordHeader($stream, $pos);
                if ($rhSlideList['recVer'] == 0x0 && $rhSlideList['recInstance'] == 0x000 && $rhSlideList['recType'] == self::RT_SLIDEPERSISTATOM && $rhSlideList['recLen'] == 0x00000014) {
                    $pos += 8;
                    $slideList['recLen'] -= 8;
                    // persistIdRef
                    $pos += 4;
                    $slideList['recLen'] -= 4;
                    // reserved1 - fShouldCollapse - fNonOutlineData - reserved2
                    $pos += 4;
                    $slideList['recLen'] -= 4;
                    // cTexts
                    $pos += 4;
                    $slideList['recLen'] -= 4;
                    // slideId
                    $slideId = self::getInt4d($stream, $pos);
                    if ($slideId == -2147483648) {
                        $slideId = 0;
                    }
                    if ($slideId > 0) {
                        $this->arrayNotes[$this->oPhpPresentation->getActiveSlideIndex()] = $slideId;
                    }
                    $pos += 4;
                    $slideList['recLen'] -= 4;
                    // reserved3
                    $pos += 4;
                    $slideList['recLen'] -= 4;
                }
            } while ($slideList['recLen'] > 0);
        }
    }

    /**
     * An atom record that specifies information about a slide.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
     */
    private function readRecordDrawingContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_DRAWING) {
            // Record Header
            $arrayReturn['length'] += 8;

            $officeArtDg = $this->readRecordOfficeArtDgContainer($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $officeArtDg['length'];
        }
        return $arrayReturn;
    }

    private function readRecordDrawingGroupContainer($stream, $pos)
    {

        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_DRAWINGGROUP) {
            // Record Header
            $arrayReturn['length'] += 8;
            $arrayReturn['length'] += $data['recLen'];
        }
        return $arrayReturn;
    }

    /**
     * An atom record that specifies a reference to an external object.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd910388(v=office.12).aspx
     */
    private function readRecordExObjRefAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_EXTERNALOBJECTREFATOM && $data['recLen'] == 0x00000004) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies a type of action to be performed.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd953300(v=office.12).aspx
     */
    private function readRecordInteractiveInfoAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_INTERACTIVEINFOATOM && $data['recLen'] == 0x00000010) {
            // Record Header
            $arrayReturn['length'] += 8;
            // soundIdRef
            $arrayReturn['length'] += 4;
            // exHyperlinkIdRef
            $arrayReturn['exHyperlinkIdRef'] = self::getInt4d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 4;
            // action
            $arrayReturn['length'] += 1;
            // oleVerb
            $arrayReturn['length'] += 1;
            // jump
            $arrayReturn['length'] += 1;
            // fAnimated (1 bit)
            // fStopSound (1 bit)
            // fCustomShowReturn (1 bit)
            // fVisited (1 bit)
            // reserved (4 bits)
            $arrayReturn['length'] += 1;
            // hyperlinkType
            $arrayReturn['length'] += 1;
            // unused
            $arrayReturn['length'] += 3;
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies the name of a macro, a file name, or a named show.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd925121(v=office.12).aspx
     */
    private function readRecordMacroNameAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x002 && $data['recType'] == self::RT_CSTRING && $data['recLen'] % 2 == 0) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies what actions to perform when interacting with an object by means of a mouse click.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd952348(v=office.12).aspx
     */
    private function readRecordMouseClickInteractiveInfoContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_INTERACTIVEINFO) {
            // Record Header
            $arrayReturn['length'] += 8;
            // interactiveInfoAtom
            $interactiveInfoAtom = $this->readRecordInteractiveInfoAtom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $interactiveInfoAtom['length'];
            if ($interactiveInfoAtom['length'] > 0) {
                $arrayReturn['exHyperlinkIdRef'] = $interactiveInfoAtom['exHyperlinkIdRef'];
            }
            // macroNameAtom
            $macroNameAtom = $this->readRecordMacroNameAtom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $macroNameAtom['length'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies what actions to perform when interacting with an object by moving the mouse cursor over it.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd925811(v=office.12).aspx
     */
    private function readRecordMouseOverInteractiveInfoContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x001 && $data['recType'] == self::RT_INTERACTIVEINFO) {
            // Record Header
            $arrayReturn['length'] += 8;
            // interactiveInfoAtom
            // macroNameAtom
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtBlip record specifies BLIP file data.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd910081(v=office.12).aspx
     */
    private function readRecordOfficeArtBlip($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
            'picture' => null
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && ($data['recType'] >= 0xF018 && $data['recType'] <= 0xF117)) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            switch ($data['recType']) {
                case self::OFFICEARTBLIPJPG:
                case self::OFFICEARTBLIPPNG:
                    // rgbUid1
                    $arrayReturn['length'] += 16;
                    $data['recLen'] -= 16;
                    if ($data['recInstance'] == 0x6E1) {
                        // rgbUid2
                        $arrayReturn['length'] += 16;
                        $data['recLen'] -= 16;
                    }
                    // tag
                    $arrayReturn['length'] += 1;
                    $data['recLen'] -= 1;
                    // BLIPFileData
                    $arrayReturn['picture'] = substr($this->streamPictures, $pos + $arrayReturn['length'], $data['recLen']);
                    $arrayReturn['length'] += $data['recLen'];
                    break;
                default:
                    throw new \Exception('Feature not implemented (l.'.__LINE__.' : '.dechex($data['recType'].')'));
            }
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtChildAnchor record specifies four signed integers that specify the anchor for the shape that contains this record.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd922720(v=office.12).aspx
     */
    private function readRecordOfficeArtChildAnchor($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF00F && $data['recLen'] == 0x00000010) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['left'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 4;
            $arrayReturn['top'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 4;
            $arrayReturn['width'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']) - $arrayReturn['left'];
            $arrayReturn['length'] += 4;
            $arrayReturn['height'] = (int) self::getInt4d($stream, $pos + $arrayReturn['length']) - $arrayReturn['top'];
            $arrayReturn['length'] += 4;
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies the location of a shape.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd922797(v=office.12).aspx
     */
    private function readRecordOfficeArtClientAnchor($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF010 && ($data['recLen'] == 0x00000008 || $data['recLen'] == 0x00000010)) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            switch ($data['recLen']) {
                case 0x00000008:
                    $arrayReturn['top'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6);
                    $arrayReturn['length'] += 2;
                    $arrayReturn['left'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6);
                    $arrayReturn['length'] += 2;
                    $arrayReturn['width'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6) - $arrayReturn['left'];
                    $arrayReturn['length'] += 2;
                    $arrayReturn['height'] = (int) (self::getInt2d($stream, $pos + $arrayReturn['length']) / 6) - $arrayReturn['left'];
                    $arrayReturn['length'] += 2;
                    $pos += 8;
                    break;
                case 0x00000010:
                    throw new \Exception('PowerPoint97 Reader : record OfficeArtClientAnchor (0x00000010)');
            }
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies text related data for a shape.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
     */
    private function readRecordOfficeArtClientTextbox($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
            'text' => '',
            'numParts' => 0,
            'numTexts' => 0,
            'hyperlink' => array(),
        );

        $data = $this->loadRecordHeader($stream, $pos);
        // recVer 0xF
        // Doc : 0x0    https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
        // Sample : 0xF https://msdn.microsoft.com/en-us/library/dd953497(v=office.12).aspx
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF00D) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $strLen = 0;
            do {
                $rhChild = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
                /**
                 * @link : https://msdn.microsoft.com/en-us/library/dd947039(v=office.12).aspx
                 */
                // echo dechex($rhChild['recType']).'-'.$rhChild['recType'].EOL;
                switch ($rhChild['recType']) {
                    case self::RT_INTERACTIVEINFO:
                        //@link : http://msdn.microsoft.com/en-us/library/dd948623(v=office.12).aspx
                        if ($rhChild['recInstance'] == 0x0000) {
                            $mouseClickInfo = $this->readRecordMouseClickInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
                            $arrayReturn['length'] += $mouseClickInfo['length'];
                            $arrayReturn['hyperlink'][]['id'] = $mouseClickInfo['exHyperlinkIdRef'];
                        }
                        if ($rhChild['recInstance'] == 0x0001) {
                            $mouseOverInfo = $this->readRecordMouseOverInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
                            $arrayReturn['length'] += $mouseOverInfo['length'];
                        }
                        break;
                    case self::RT_STYLETEXTPROPATOM:
                        $arrayReturn['length'] += 8;
                        // @link : http://msdn.microsoft.com/en-us/library/dd950647(v=office.12).aspx
                        // rgTextPFRun
                        $strLenRT = $strLen + 1;
                        do {
                            $strucTextPFRun = $this->readStructureTextPFRun($stream, $pos + $arrayReturn['length'], $strLenRT);
                            $arrayReturn['numTexts']++;
                            $arrayReturn['text'.$arrayReturn['numTexts']] = $strucTextPFRun;
                            if (isset($strucTextPFRun['alignH'])) {
                                $arrayReturn['alignH'] = $strucTextPFRun['alignH'];
                            }
                            $strLenRT = $strucTextPFRun['strLenRT'];
                            $arrayReturn['length'] += $strucTextPFRun['length'];
                        } while ($strLenRT > 0);
                        // rgTextCFRun
                        $strLenRT = $strLen + 1;
                        do {
                            $strucTextCFRun = $this->readStructureTextCFRun($stream, $pos + $arrayReturn['length'], $strLenRT);
                            $arrayReturn['numParts']++;
                            $arrayReturn['part'.$arrayReturn['numParts']] = $strucTextCFRun;
                            $strLenRT = $strucTextCFRun['strLenRT'];
                            $arrayReturn['length'] += $strucTextCFRun['length'];
                        } while ($strLenRT > 0);
                        break;
                    case self::RT_TEXTBYTESATOM:
                        $arrayReturn['length'] += 8;
                        // @link : https://msdn.microsoft.com/en-us/library/dd947905(v=office.12).aspx
                        $strLen = (int)$rhChild['recLen'];
                        for ($inc = 0; $inc < $strLen; $inc++) {
                            $char = self::getInt1d($stream, $pos + $arrayReturn['length']);
                            if ($char == 0x0B) {
                                $char = 0x20;
                            }
                            $arrayReturn['text'] .= Text::chr($char);
                            $arrayReturn['length'] += 1;
                        }
                        break;
                    case self::RT_TEXTCHARSATOM:
                        $arrayReturn['length'] += 8;
                        // @link : http://msdn.microsoft.com/en-us/library/dd772921(v=office.12).aspx
                        $strLen = (int)($rhChild['recLen']/2);
                        for ($inc = 0; $inc < $strLen; $inc++) {
                            $char = self::getInt2d($stream, $pos + $arrayReturn['length']);
                            if ($char == 0x0B) {
                                $char = 0x20;
                            }
                            $arrayReturn['text'] .= Text::chr($char);
                            $arrayReturn['length'] += 2;
                        }
                        break;
                    case self::RT_TEXTHEADERATOM:
                        $arrayReturn['length'] += 8;
                        // @link : http://msdn.microsoft.com/en-us/library/dd905272(v=office.12).aspx
                        // textType
                        $arrayReturn['length'] += 4;
                        break;
                    case self::RT_TEXTINTERACTIVEINFOATOM:
                        $arrayReturn['length'] += 8;
                        //@link : http://msdn.microsoft.com/en-us/library/dd947973(v=office.12).aspx
                        if ($rhChild['recInstance'] == 0x0000) {
                            //@todo : MouseClickTextInteractiveInfoAtom
                            $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['start'] = self::getInt4d($stream, $pos +  + $arrayReturn['length']);
                            $arrayReturn['length'] += 4;

                            $arrayReturn['hyperlink'][count($arrayReturn['hyperlink']) - 1]['end'] = self::getInt4d($stream, $pos +  + $arrayReturn['length']);
                            $arrayReturn['length'] += 4;
                        }
                        if ($rhChild['recInstance'] == 0x0001) {
                            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
                        }
                        break;
                    case self::RT_TEXTSPECIALINFOATOM:
                        $arrayReturn['length'] += 8;
                        // @link : http://msdn.microsoft.com/en-us/library/dd945296(v=office.12).aspx
                        $strLenRT = $strLen + 1;
                        do {
                            $structTextSIRun = $this->readStructureTextSIRun($stream, $pos + $arrayReturn['length'], $strLenRT);
                            $strLenRT = $structTextSIRun['strLenRT'];
                            $arrayReturn['length'] += $structTextSIRun['length'];
                        } while ($strLenRT > 0);
                        break;
                    case self::RT_TEXTRULERATOM:
                        $arrayReturn['length'] += 8;
                        // @link : http://msdn.microsoft.com/en-us/library/dd953212(v=office.12).aspx
                        $structRuler = $this->readStructureTextRuler($stream, $pos + $arrayReturn['length']);
                        $arrayReturn['length'] += $structRuler['length'];
                        break;
                    case self::RT_SLIDENUMBERMETACHARATOM:
                        $datasRecord = $this->readRecordSlideNumberMCAtom($stream, $pos + $arrayReturn['length']);
                        $arrayReturn['length'] += $datasRecord['length'];
                        break;
                    default:
                        $arrayReturn['length'] += 8;
                        $arrayReturn['length'] += $rhChild['recLen'];
                    // throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($rhChild['recType']).')');
                }
            } while (($data['recLen'] - $arrayReturn['length']) > 0);
        }
        return $arrayReturn;
    }

    /**
     * The OfficeArtSpContainer record specifies a shape container.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd943794(v=office.12).aspx
     */
    private function readRecordOfficeArtSpContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
            'shape' => null,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF004) {
            // Record Header
            $arrayReturn['length'] += 8;
            // shapeGroup
            $shapeGroup = $this->readRecordOfficeArtFSPGR($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $shapeGroup['length'];

            // shapeProp
            $shapeProp = $this->readRecordOfficeArtFSP($stream, $pos + $arrayReturn['length']);
            if ($shapeProp['length'] == 0) {
                throw new \Exception('PowerPoint97 Reader : record OfficeArtFSP');
            }
            $arrayReturn['length'] += $shapeProp['length'];

            if ($shapeProp['fDeleted'] == 0x1 && $shapeProp['fChild'] == 0x0) {
                // deletedShape
                $deletedShape = $this->readRecordOfficeArtFPSPL($stream, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += $deletedShape['length'];
            }

            // shapePrimaryOptions
            $shpPrimaryOptions = $this->readRecordOfficeArtFOPT($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $shpPrimaryOptions['length'];

            // shapeSecondaryOptions1
            $shpSecondaryOptions1 = $this->readRecordOfficeArtSecondaryFOPT($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $shpSecondaryOptions1['length'];

            // shapeTertiaryOptions1
            $shpTertiaryOptions1 = $this->readRecordOfficeArtTertiaryFOPT($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $shpTertiaryOptions1['length'];

            // childAnchor
            $childAnchor = $this->readRecordOfficeArtChildAnchor($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $childAnchor['length'];

            // clientAnchor
            $clientAnchor = $this->readRecordOfficeArtClientAnchor($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $clientAnchor['length'];

            // clientData
            $clientData = $this->readRecordOfficeArtClientData($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $clientData['length'];

            // clientTextbox
            $clientTextbox = $this->readRecordOfficeArtClientTextbox($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $clientTextbox['length'];

            // shapeSecondaryOptions2
            if ($shpSecondaryOptions1['length'] == 0) {
                $shpSecondaryOptions2 = $this->readRecordOfficeArtSecondaryFOPT($stream, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += $shpSecondaryOptions2['length'];
            }

            // shapeTertiaryOptions2
            if ($shpTertiaryOptions1['length'] == 0) {
                $shpTertiaryOptions2 = $this->readRecordOfficeArtTertiaryFOPT($stream, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += $shpTertiaryOptions2['length'];
            }

            // Core : Shape
            // Informations about group are not defined
            $arrayDimensions = array();
            $bIsGroup = false;
            if (is_object($this->oCurrentGroup)) {
                if (!$this->bFirstShapeGroup) {
                    if ($clientAnchor['length'] > 0) {
                        // $this->oCurrentGroup->setOffsetX($clientAnchor['left']);
                        // $this->oCurrentGroup->setOffsetY($clientAnchor['top']);
                        // $this->oCurrentGroup->setHeight($clientAnchor['height']);
                        // $this->oCurrentGroup->setWidth($clientAnchor['width']);
                    }
                    $bIsGroup = true;
                    $this->bFirstShapeGroup = true;
                } else {
                    if ($childAnchor['length'] > 0) {
                        $arrayDimensions = $childAnchor;
                    }
                }
            } else {
                if ($clientAnchor['length'] > 0) {
                    $arrayDimensions = $clientAnchor;
                }
            }
            if (!$bIsGroup) {
                // *** Shape ***
                if (isset($shpPrimaryOptions['pib'])) {
                    // isDrawing
                    $drawingPib = $shpPrimaryOptions['pib'];
                    if (isset($this->arrayPictures[$drawingPib - 1])) {
                        $gdImage = imagecreatefromstring($this->arrayPictures[$drawingPib - 1]);
                        $arrayReturn['shape'] = new Drawing\Gd();
                        $arrayReturn['shape']->setImageResource($gdImage);
                    }
                } elseif (isset($shpPrimaryOptions['line']) && $shpPrimaryOptions['line']) {
                    // isLine
                    $arrayReturn['shape'] = new Line(0, 0, 0, 0);
                } elseif ($clientTextbox['length'] > 0) {
                    $arrayReturn['shape'] = new RichText();
                    if (isset($clientTextbox['alignH'])) {
                        $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setHorizontal($clientTextbox['alignH']);
                    }

                    $start = 0;
                    $lastLevel = -1;
                    $lastMarginLeft = 0;
                    for ($inc = 1; $inc <= $clientTextbox['numParts']; $inc++) {
                        if ($clientTextbox['numParts'] == $clientTextbox['numTexts'] && isset($clientTextbox['text'.$inc])) {
                            if (isset($clientTextbox['text'.$inc]['bulletChar'])) {
                                $arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletType(Bullet::TYPE_BULLET);
                                $arrayReturn['shape']->getActiveParagraph()->getBulletStyle()->setBulletChar($clientTextbox['text'.$inc]['bulletChar']);
                            }
                            // Indent
                            $indent = 0;
                            if (isset($clientTextbox['text'.$inc]['indent'])) {
                                $indent = $clientTextbox['text'.$inc]['indent'];
                            }
                            if (isset($clientTextbox['text'.$inc]['leftMargin'])) {
                                if ($lastMarginLeft > $clientTextbox['text'.$inc]['leftMargin']) {
                                    $lastLevel--;
                                }
                                if ($lastMarginLeft < $clientTextbox['text'.$inc]['leftMargin']) {
                                    $lastLevel++;
                                }
                                $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setLevel($lastLevel);
                                $lastMarginLeft = $clientTextbox['text'.$inc]['leftMargin'];

                                $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setMarginLeft($clientTextbox['text'.$inc]['leftMargin']);
                                $arrayReturn['shape']->getActiveParagraph()->getAlignment()->setIndent($indent - $clientTextbox['text'.$inc]['leftMargin']);
                            }
                        }
                        // Texte
                        $sText = substr(isset($clientTextbox['text']) ? $clientTextbox['text'] : '', $start, $clientTextbox['part'.$inc]['partLength']);
                        $sHyperlinkURL = '';
                        if (empty($sText)) {
                            // Is there a hyperlink ?
                            if (isset($clientTextbox['hyperlink']) && is_array($clientTextbox['hyperlink']) && !empty($clientTextbox['hyperlink'])) {
                                foreach ($clientTextbox['hyperlink'] as $itmHyperlink) {
                                    if ($itmHyperlink['start'] == $start && ($itmHyperlink['end'] - $itmHyperlink['start']) == $clientTextbox['part'.$inc]['partLength']) {
                                        $sText = $this->arrayHyperlinks[$itmHyperlink['id']]['text'];
                                        $sHyperlinkURL = $this->arrayHyperlinks[$itmHyperlink['id']]['url'];
                                        break;
                                    }
                                }
                            }
                        }
                        // New paragraph
                        $bCreateParagraph = false;
                        if (strpos($sText, "\r") !== false) {
                            $bCreateParagraph = true;
                            $sText = str_replace("\r", '', $sText);
                        }
                        // TextRun
                        $txtRun = $arrayReturn['shape']->createTextRun($sText);
                        if (isset($clientTextbox['part'.$inc]['bold'])) {
                            $txtRun->getFont()->setBold($clientTextbox['part'.$inc]['bold']);
                        }
                        if (isset($clientTextbox['part'.$inc]['italic'])) {
                            $txtRun->getFont()->setItalic($clientTextbox['part'.$inc]['italic']);
                        }
                        if (isset($clientTextbox['part'.$inc]['underline'])) {
                            $txtRun->getFont()->setUnderline($clientTextbox['part'.$inc]['underline']);
                        }
                        if (isset($clientTextbox['part'.$inc]['fontName'])) {
                            $txtRun->getFont()->setName($clientTextbox['part'.$inc]['fontName']);
                        }
                        if (isset($clientTextbox['part'.$inc]['fontSize'])) {
                            $txtRun->getFont()->setSize($clientTextbox['part'.$inc]['fontSize']);
                        }
                        if (isset($clientTextbox['part'.$inc]['color'])) {
                            $txtRun->getFont()->setColor($clientTextbox['part'.$inc]['color']);
                        }
                        // Hyperlink
                        if (!empty($sHyperlinkURL)) {
                            $txtRun->setHyperlink(new Hyperlink($sHyperlinkURL));
                        }

                        $start += $clientTextbox['part'.$inc]['partLength'];
                        if ($bCreateParagraph) {
                            $arrayReturn['shape']->createParagraph();
                        }
                    }
                }

                // *** Properties ***
                // Dimensions
                if ($arrayReturn['shape'] instanceof AbstractShape) {
                    if (!empty($arrayDimensions)) {
                        $arrayReturn['shape']->setOffsetX($arrayDimensions['left']);
                        $arrayReturn['shape']->setOffsetY($arrayDimensions['top']);
                        $arrayReturn['shape']->setHeight($arrayDimensions['height']);
                        $arrayReturn['shape']->setWidth($arrayDimensions['width']);
                    }
                    // Rotation
                    if (isset($shpPrimaryOptions['rotation'])) {
                        $rotation = $shpPrimaryOptions['rotation'];
                        $arrayReturn['shape']->setRotation($rotation);
                    }
                    // Shadow
                    if (isset($shpPrimaryOptions['shadowOffsetX']) && isset($shpPrimaryOptions['shadowOffsetY'])) {
                        $shadowOffsetX = $shpPrimaryOptions['shadowOffsetX'];
                        $shadowOffsetY = $shpPrimaryOptions['shadowOffsetY'];
                        if ($shadowOffsetX != 0 && $shadowOffsetX != 0) {
                            $arrayReturn['shape']->getShadow()->setVisible(true);
                            if ($shadowOffsetX > 0 && $shadowOffsetX == $shadowOffsetY) {
                                $arrayReturn['shape']->getShadow()->setDistance($shadowOffsetX)->setDirection(45);
                            }
                        }
                    }
                    // Specific Line
                    if ($arrayReturn['shape'] instanceof Line) {
                        if (isset($shpPrimaryOptions['lineColor'])) {
                            $arrayReturn['shape']->getBorder()->getColor()->setARGB('FF'.$shpPrimaryOptions['lineColor']);
                        }
                        if (isset($shpPrimaryOptions['lineWidth'])) {
                            $arrayReturn['shape']->setHeight($shpPrimaryOptions['lineWidth']);
                        }
                    }
                    // Specific RichText
                    if ($arrayReturn['shape'] instanceof RichText) {
                        if (isset($shpPrimaryOptions['insetBottom'])) {
                            $arrayReturn['shape']->setInsetBottom($shpPrimaryOptions['insetBottom']);
                        }
                        if (isset($shpPrimaryOptions['insetLeft'])) {
                            $arrayReturn['shape']->setInsetLeft($shpPrimaryOptions['insetLeft']);
                        }
                        if (isset($shpPrimaryOptions['insetRight'])) {
                            $arrayReturn['shape']->setInsetRight($shpPrimaryOptions['insetRight']);
                        }
                        if (isset($shpPrimaryOptions['insetTop'])) {
                            $arrayReturn['shape']->setInsetTop($shpPrimaryOptions['insetTop']);
                        }
                    }
                }
            } else {
                // Rotation
                if (isset($shpPrimaryOptions['rotation'])) {
                    $rotation = $shpPrimaryOptions['rotation'];
                    $this->oCurrentGroup->setRotation($rotation);
                }
            }
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtSpgrContainer record specifies a container for groups of shapes.
     * @param string $stream
     * @param integer $pos
     * @param boolean $bInGroup
     * @return array
     * @throws \Exception
     * @link : https://msdn.microsoft.com/en-us/library/dd910416(v=office.12).aspx
     */
    private function readRecordOfficeArtSpgrContainer($stream, $pos, $bInGroup = false)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF003) {
            $arrayReturn['length'] += 8;

            do {
                $rhFileBlock = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
                if (!($rhFileBlock['recVer'] == 0xF && $rhFileBlock['recInstance'] == 0x0000 && ($rhFileBlock['recType'] == 0xF003 || $rhFileBlock['recType'] == 0xF004))) {
                    throw new \Exception('PowerPoint97 Reader : readRecordOfficeArtSpgrContainer.');
                }

                switch ($rhFileBlock['recType']) {
                    case 0xF003:
                        // Core
                        $this->oCurrentGroup = $this->oPhpPresentation->getActiveSlide()->createGroup();
                        $this->bFirstShapeGroup = false;
                        // OfficeArtSpgrContainer
                        $fileBlock = $this->readRecordOfficeArtSpgrContainer($stream, $pos + $arrayReturn['length'], true);
                        $arrayReturn['length'] += $fileBlock['length'];
                        $data['recLen'] -= $fileBlock['length'];
                        break;
                    case 0xF004:
                        // Core
                        if (!$bInGroup) {
                            $this->oCurrentGroup = null;
                        }
                        // OfficeArtSpContainer
                        $fileBlock = $this->readRecordOfficeArtSpContainer($stream, $pos + $arrayReturn['length']);
                        $arrayReturn['length'] += $fileBlock['length'];
                        $data['recLen'] -= $fileBlock['length'];
                        // Core
                        //@todo
                        if (!is_null($fileBlock['shape'])) {
                            switch ($this->inMainType) {
                                case self::RT_NOTES:
                                    $arrayIdxSlide = array_flip($this->arrayNotes);
                                    if ($this->currentNote > 0 && isset($arrayIdxSlide[$this->currentNote])) {
                                        $oSlide = $this->oPhpPresentation->getSlide($arrayIdxSlide[$this->currentNote]);
                                        if ($oSlide->getNote()->getShapeCollection()->count() == 0) {
                                            $oSlide->getNote()->addShape($fileBlock['shape']);
                                        }
                                    }
                                    break;
                                case self::RT_SLIDE:
                                    if ($bInGroup) {
                                        $this->oCurrentGroup->addShape($fileBlock['shape']);
                                    } else {
                                        $this->oPhpPresentation->getActiveSlide()->addShape($fileBlock['shape']);
                                    }
                                    break;
                            }
                        }

                        break;
                }
            } while ($data['recLen'] > 0);
        }
        return $arrayReturn;
    }

    /**
     * The OfficeArtTertiaryFOPT record specifies a table of OfficeArtRGFOPTE records,.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd950206(v=office.12).aspx
     */
    private function readRecordOfficeArtTertiaryFOPT($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x3 && $data['recType'] == 0xF122) {
            // Record Header
            $arrayReturn['length'] += 8;

            $officeArtFOPTE = array();
            for ($inc = 0; $inc < $data['recInstance']; $inc++) {
                $opid = self::getInt2d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 2;
                $optOp = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 4;
                $officeArtFOPTE[] = array(
                    'opid' => ($opid >> 0) & bindec('11111111111111'),
                    'fBid' => ($opid >> 14) & bindec('1'),
                    'fComplex' => ($opid >> 15) & bindec('1'),
                    'op' => $optOp,
                );
            }
            //@link : http://code.metager.de/source/xref/kde/calligra/filters/libmso/OPID
            foreach ($officeArtFOPTE as $opt) {
                switch ($opt['opid']) {
                    case 0x039F:
                        // Table properties
                        //@link : https://msdn.microsoft.com/en-us/library/dd922773(v=office.12).aspx
                        break;
                    case 0x03A0:
                        // Table Row Properties
                        //@link : https://msdn.microsoft.com/en-us/library/dd923419(v=office.12).aspx
                        if ($opt['fComplex'] == 0x1) {
                            $arrayReturn['length'] += $opt['op'];
                        }
                        break;
                    case 0x03A9:
                        // GroupShape : metroBlob
                        //@link : https://msdn.microsoft.com/en-us/library/dd943388(v=office.12).aspx
                        if ($opt['fComplex'] == 0x1) {
                            $arrayReturn['length'] += $opt['op'];
                        }
                        break;
                    case 0x01FF:
                        // Line Style Boolean
                        //@link : https://msdn.microsoft.com/en-us/library/dd951605(v=office.12).aspx
                        break;
                    default:
                        throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($opt['opid']).')');
                }
            }
        }
        return $arrayReturn;
    }

    /**
     * The OfficeArtDgContainer record specifies the container for all the file records for the objects in a drawing.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link : https://msdn.microsoft.com/en-us/library/dd924455(v=office.12).aspx
     */
    private function readRecordOfficeArtDgContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF002) {
            // Record Header
            $arrayReturn['length'] += 8;
            // drawingData
            $drawingData  = $this->readRecordOfficeArtFDG($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $drawingData['length'];
            // regroupItems
            //@todo
            // groupShape
            $groupShape = $this->readRecordOfficeArtSpgrContainer($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $groupShape['length'];
            // shape
            $shape = $this->readRecordOfficeArtSpContainer($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $shape['length'];
            // solvers1
            //@todo
            // deletedShapes
            //@todo
            // solvers1
            //@todo
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtFDG record specifies the number of shapes, the drawing identifier, and the shape identifier of the last shape in a drawing.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link : https://msdn.microsoft.com/en-us/library/dd946757(v=office.12).aspx
     */
    private function readRecordOfficeArtFDG($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] <= 0xFFE && $data['recType'] == 0xF008 && $data['recLen'] == 0x00000008) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd943404(v=office.12).aspx
     */
    private function readRecordOfficeArtFOPT($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x3 && $data['recType'] == 0xF00B) {
            // Record Header
            $arrayReturn['length'] += 8;

            //@link : http://msdn.microsoft.com/en-us/library/dd906086(v=office.12).aspx
            $officeArtFOPTE = array();
            for ($inc = 0; $inc < $data['recInstance']; $inc++) {
                $opid = self::getInt2d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 2;
                $data['recLen'] -= 2;
                $optOp = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 4;
                $data['recLen'] -= 4;
                $officeArtFOPTE[] = array(
                    'opid' => ($opid >> 0) & bindec('11111111111111'),
                    'fBid' => ($opid >> 14) & bindec('1'),
                    'fComplex' => ($opid >> 15) & bindec('1'),
                    'op' => $optOp,
                );
            }
            //@link : http://code.metager.de/source/xref/kde/calligra/filters/libmso/OPID
            foreach ($officeArtFOPTE as $opt) {
                // echo $opt['opid'].'-0x'.dechex($opt['opid']).EOL;
                switch ($opt['opid']) {
                    case 0x0004:
                        // Transform : rotation
                        //@link : https://msdn.microsoft.com/en-us/library/dd949750(v=office.12).aspx
                        $arrayReturn['rotation'] = $opt['op'];
                        break;
                    case 0x007F:
                        // Transform : Protection Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd909131(v=office.12).aspx
                        break;
                    case 0x0080:
                        // Text : ltxid
                        //@link : http://msdn.microsoft.com/en-us/library/dd947446(v=office.12).aspx
                        break;
                    case 0x0081:
                        // Text : dxTextLeft
                        //@link : http://msdn.microsoft.com/en-us/library/dd953234(v=office.12).aspx
                        $arrayReturn['insetLeft'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x0082:
                        // Text : dyTextTop
                        //@link : http://msdn.microsoft.com/en-us/library/dd925068(v=office.12).aspx
                        $arrayReturn['insetTop'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x0083:
                        // Text : dxTextRight
                        //@link : http://msdn.microsoft.com/en-us/library/dd906782(v=office.12).aspx
                        $arrayReturn['insetRight'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x0084:
                        // Text : dyTextBottom
                        //@link : http://msdn.microsoft.com/en-us/library/dd772858(v=office.12).aspx
                        $arrayReturn['insetBottom'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x0085:
                        // Text : WrapText
                        //@link : http://msdn.microsoft.com/en-us/library/dd924770(v=office.12).aspx
                        break;
                    case 0x0087:
                        // Text : anchorText
                        //@link : http://msdn.microsoft.com/en-us/library/dd948575(v=office.12).aspx
                        break;
                    case 0x00BF:
                        // Text : Text Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd950905(v=office.12).aspx
                        break;
                    case 0x0104:
                        // Blip : pib
                        //@link : http://msdn.microsoft.com/en-us/library/dd772837(v=office.12).aspx
                        if ($opt['fComplex'] == 0) {
                            $arrayReturn['pib'] = $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        } else {
                            // pib Complex
                        }
                        break;
                    case 0x13F:
                        // Blip Boolean Properties
                        //@link : https://msdn.microsoft.com/en-us/library/dd944215(v=office.12).aspx
                        break;
                    case 0x140:
                        // Geometry : geoLeft
                        //@link : http://msdn.microsoft.com/en-us/library/dd947489(v=office.12).aspx
                        // print_r('geoLeft : '.$opt['op'].EOL);
                        break;
                    case 0x141:
                        // Geometry : geoTop
                        //@link : http://msdn.microsoft.com/en-us/library/dd949459(v=office.12).aspx
                        // print_r('geoTop : '.$opt['op'].EOL);
                        break;
                    case 0x142:
                        // Geometry : geoRight
                        //@link : http://msdn.microsoft.com/en-us/library/dd947117(v=office.12).aspx
                        // print_r('geoRight : '.$opt['op'].EOL);
                        break;
                    case 0x143:
                        // Geometry : geoBottom
                        //@link : http://msdn.microsoft.com/en-us/library/dd948602(v=office.12).aspx
                        // print_r('geoBottom : '.$opt['op'].EOL);
                        break;
                    case 0x144:
                        // Geometry : shapePath
                        //@link : http://msdn.microsoft.com/en-us/library/dd945249(v=office.12).aspx
                        $arrayReturn['line'] = true;
                        break;
                    case 0x145:
                        // Geometry : pVertices
                        //@link : http://msdn.microsoft.com/en-us/library/dd949814(v=office.12).aspx
                        if ($opt['fComplex'] == 1) {
                            $arrayReturn['length'] += $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        }
                        break;
                    case 0x146:
                        // Geometry : pSegmentInfo
                        //@link : http://msdn.microsoft.com/en-us/library/dd905742(v=office.12).aspx
                        if ($opt['fComplex'] == 1) {
                            $arrayReturn['length'] += $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        }
                        break;
                    case 0x155:
                        // Geometry : pAdjustHandles
                        //@link : http://msdn.microsoft.com/en-us/library/dd905890(v=office.12).aspx
                        if ($opt['fComplex'] == 1) {
                            $arrayReturn['length'] += $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        }
                        break;
                    case 0x156:
                        // Geometry : pGuides
                        //@link : http://msdn.microsoft.com/en-us/library/dd910801(v=office.12).aspx
                        if ($opt['fComplex'] == 1) {
                            $arrayReturn['length'] += $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        }
                        break;
                    case 0x157:
                        // Geometry : pInscribe
                        //@link : http://msdn.microsoft.com/en-us/library/dd904889(v=office.12).aspx
                        if ($opt['fComplex'] == 1) {
                            $arrayReturn['length'] += $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        }
                        break;
                    case 0x17F:
                        // Geometry Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd944968(v=office.12).aspx
                        break;
                    case 0x0180:
                        // Fill : fillType
                        //@link : http://msdn.microsoft.com/en-us/library/dd947909(v=office.12).aspx
                        break;
                    case 0x0181:
                        // Fill : fillColor
                        //@link : http://msdn.microsoft.com/en-us/library/dd921332(v=office.12).aspx
                        $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        // echo 'fillColor  : '.$strColor.EOL;
                        break;
                    case 0x0183:
                        // Fill : fillBackColor
                        //@link : http://msdn.microsoft.com/en-us/library/dd950634(v=office.12).aspx
                        $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        // echo 'fillBackColor  : '.$strColor.EOL;
                        break;
                    case 0x0193:
                        // Fill : fillRectRight
                        //@link : http://msdn.microsoft.com/en-us/library/dd951294(v=office.12).aspx
                        // echo 'fillRectRight  : '.\PhpOffice\Common\Drawing::emuToPixels($opt['op']).EOL;
                        break;
                    case 0x0194:
                        // Fill : fillRectBottom
                        //@link : http://msdn.microsoft.com/en-us/library/dd910194(v=office.12).aspx
                        // echo 'fillRectBottom   : '.\PhpOffice\Common\Drawing::emuToPixels($opt['op']).EOL;
                        break;
                    case 0x01BF:
                        // Fill : Fill Style Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd909380(v=office.12).aspx
                        break;
                    case 0x01C0:
                        // Line Style : lineColor
                        //@link : http://msdn.microsoft.com/en-us/library/dd920397(v=office.12).aspx
                        $strColor = str_pad(dechex(($opt['op'] >> 0) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $strColor .= str_pad(dechex(($opt['op'] >> 8) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $strColor .= str_pad(dechex(($opt['op'] >> 16) & bindec('11111111')), 2, STR_PAD_LEFT, '0');
                        $arrayReturn['lineColor'] = $strColor;
                        break;
                    case 0x01C1:
                        // Line Style : lineOpacity
                        //@link : http://msdn.microsoft.com/en-us/library/dd923433(v=office.12).aspx
                        // echo 'lineOpacity : '.dechex($opt['op']).EOL;
                        break;
                    case 0x01C2:
                        // Line Style : lineBackColor
                        //@link : http://msdn.microsoft.com/en-us/library/dd947669(v=office.12).aspx
                        break;
                    case 0x01CB:
                        // Line Style : lineWidth
                        //@link : http://msdn.microsoft.com/en-us/library/dd926964(v=office.12).aspx
                        $arrayReturn['lineWidth'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x01D6:
                        // Line Style : lineJoinStyle
                        //@link : http://msdn.microsoft.com/en-us/library/dd909643(v=office.12).aspx
                        break;
                    case 0x01D7:
                        // Line Style : lineEndCapStyle
                        //@link : http://msdn.microsoft.com/en-us/library/dd925071(v=office.12).aspx
                        break;
                    case 0x01FF:
                        // Line Style : Line Style Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd951605(v=office.12).aspx
                        break;
                    case 0x0201:
                        // Shadow Style : shadowColor
                        //@link : http://msdn.microsoft.com/en-us/library/dd923454(v=office.12).aspx
                        break;
                    case 0x0204:
                        // Shadow Style : shadowOpacity
                        //@link : http://msdn.microsoft.com/en-us/library/dd920720(v=office.12).aspx
                        break;
                    case 0x0205:
                        // Shadow Style : shadowOffsetX
                        //@link : http://msdn.microsoft.com/en-us/library/dd945280(v=office.12).aspx
                        $arrayReturn['shadowOffsetX'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x0206:
                        // Shadow Style : shadowOffsetY
                        //@link : http://msdn.microsoft.com/en-us/library/dd907855(v=office.12).aspx
                        $arrayReturn['shadowOffsetY'] = \PhpOffice\Common\Drawing::emuToPixels($opt['op']);
                        break;
                    case 0x023F:
                        // Shadow Style : Shadow Style Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd947887(v=office.12).aspx
                        break;
                    case 0x0304:
                        // Shape : bWMode
                        //@link : http://msdn.microsoft.com/en-us/library/dd947659(v=office.12).aspx
                        break;
                    case 0x033F:
                        // Shape Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd951345(v=office.12).aspx
                        break;
                    case 0x0380:
                        // Group Shape Property Set : wzName
                        //@link : http://msdn.microsoft.com/en-us/library/dd950681(v=office.12).aspx
                        if ($opt['fComplex'] == 1) {
                            $arrayReturn['length'] += $opt['op'];
                            $data['recLen'] -= $opt['op'];
                        }
                        break;
                    case 0x03BF:
                        // Group Shape Property Set : Group Shape Boolean Properties
                        //@link : http://msdn.microsoft.com/en-us/library/dd949807(v=office.12).aspx
                        break;
                    default:
                        // throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($opt['opid']).')');
                }
            }
            if ($data['recLen'] > 0) {
                $arrayReturn['length'] += $data['recLen'];
            }
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtFPSPL record specifies the former hierarchical position of the containing object that is either a shape or a group of shapes.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd947479(v=office.12).aspx
     */
    private function readRecordOfficeArtFPSPL($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF11D && $data['recLen'] == 0x00000004) {
            $arrayReturn['length'] += 8;
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtFSP record specifies an instance of a shape.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd925898(v=office.12).aspx
     */
    private function readRecordOfficeArtFSP($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x2 && $data['recType'] == 0xF00A && $data['recLen'] == 0x00000008) {
            $arrayReturn['length'] += 8;
            // spid
            $arrayReturn['length'] += 4;
            // data
            $data = self::getInt4d($this->streamPowerpointDocument, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 4;
            $arrayReturn['fGroup'] = ($data >> 0) & bindec('1');
            $arrayReturn['fChild'] = ($data >> 1) & bindec('1');
            $arrayReturn['fPatriarch'] = ($data >> 2) & bindec('1');
            $arrayReturn['fDeleted'] = ($data >> 3) & bindec('1');
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtFSPGR record specifies the coordinate system of the group shape that the anchors of the child shape are expressed in.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd925381(v=office.12).aspx
     */
    private function readRecordOfficeArtFSPGR($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x1 && $data['recInstance'] == 0x000 && $data['recType'] == 0xF009 && $data['recLen'] == 0x00000010) {
            $arrayReturn['length'] += 8;
            //$arrShapeGroup['xLeft'] = self::getInt4d($this->streamPowerpointDocument, $pos);
            $arrayReturn['length'] += 4;
            //$arrShapeGroup['yTop'] = self::getInt4d($this->streamPowerpointDocument, $pos);
            $arrayReturn['length'] += 4;
            //$arrShapeGroup['xRight'] = self::getInt4d($this->streamPowerpointDocument, $pos);
            $arrayReturn['length'] += 4;
            //$arrShapeGroup['yBottom'] = self::getInt4d($this->streamPowerpointDocument, $pos);
            $arrayReturn['length'] += 4;
        }

        return $arrayReturn;
    }

    /**
     * The OfficeArtSecondaryFOPT record specifies a table of OfficeArtRGFOPTE records.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd950259(v=office.12).aspx
     */
    private function readRecordOfficeArtSecondaryFOPT($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x3 && $data['recType'] == 0xF121) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $arrayReturn['length'] += $data['recLen'];
        }
        return $arrayReturn;
    }

    /**
     * A container record that specifies information about a shape.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link : https://msdn.microsoft.com/en-us/library/dd950927(v=office.12).aspx
     */
    private function readRecordOfficeArtClientData($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == 0xF011) {
            $arrayReturn['length'] += 8;
            // shapeFlagsAtom (9 bytes)
            $dataShapeFlagsAtom = $this->readRecordShapeFlagsAtom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $dataShapeFlagsAtom['length'];

            // shapeFlags10Atom (9 bytes)
            $dataShapeFlags10Atom = $this->readRecordShapeFlags10Atom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $dataShapeFlags10Atom['length'];

            // exObjRefAtom (12 bytes)
            $dataExObjRefAtom = $this->readRecordExObjRefAtom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $dataExObjRefAtom['length'];

            // animationInfo (variable)
            $dataAnimationInfo = $this->readRecordAnimationInfoContainer($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $dataAnimationInfo['length'];

            // mouseClickInteractiveInfo (variable)
            $mouseClickInfo = $this->readRecordMouseClickInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $mouseClickInfo['length'];

            // mouseOverInteractiveInfo (variable)
            $mouseOverInfo = $this->readRecordMouseOverInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $mouseOverInfo['length'];

            // placeholderAtom (16 bytes)
            $dataPlaceholderAtom = $this->readRecordPlaceholderAtom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $dataPlaceholderAtom['length'];

            // recolorInfoAtom (variable)
            $dataRecolorInfo = $this->readRecordRecolorInfoAtom($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += $dataRecolorInfo['length'];

            // rgShapeClientRoundtripData (variable)
            $array = array(
                self::RT_PROGTAGS,
                self::RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM,
                self::RT_ROUNDTRIPSHAPEID12ATOM,
                self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM,
                self::RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM,
            );
            do {
                $dataHeaderRG = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
                if (in_array($dataHeaderRG['recType'], $array)) {
                    switch ($dataHeaderRG['recType']) {
                        case self::RT_PROGTAGS:
                            $dataRG = $this->readRecordShapeProgTagsContainer($stream, $pos + $arrayReturn['length']);
                            $arrayReturn['length'] += $dataRG['length'];
                            break;
                        case self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM:
                            $dataRG = $this->readRecordRoundTripHFPlaceholder12Atom($stream, $pos + $arrayReturn['length']);
                            $arrayReturn['length'] += $dataRG['length'];
                            break;
                        case self::RT_ROUNDTRIPSHAPEID12ATOM:
                            $dataRG = $this->readRecordRoundTripShapeId12Atom($stream, $pos + $arrayReturn['length']);
                            $arrayReturn['length'] += $dataRG['length'];
                            break;
                        default:
                            throw new \Exception('Feature not implemented (l.'.__LINE__.' : 0x'.dechex($dataHeaderRG['recType']).')');
                    }
                }
            } while (in_array($dataHeaderRG['recType'], $array));
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies a persist object directory. Each persist object identifier specified MUST be unique in that persist object directory.
     * @link http://msdn.microsoft.com/en-us/library/dd952680(v=office.12).aspx
     * @param string $stream
     * @param integer $pos
     * @throws \Exception
     */
    private function readRecordPersistDirectoryAtom($stream, $pos)
    {
        $rHeader = $this->loadRecordHeader($stream, $pos);
        $pos += 8;
        if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_PERSISTDIRECTORYATOM) {
            throw new \Exception('File PowerPoint 97 in error (Location : PersistDirectoryAtom > RecordHeader).');
        }
        // rgPersistDirEntry
        // @link : http://msdn.microsoft.com/en-us/library/dd947347(v=office.12).aspx
        do {
            $data = self::getInt4d($stream, $pos);
            $pos += 4;
            $rHeader['recLen'] -= 4;
            //$persistId  = ($data >> 0) & bindec('11111111111111111111');
            $cPersist  = ($data >> 20) & bindec('111111111111');

            $rgPersistOffset = array();
            for ($inc = 0; $inc < $cPersist; $inc++) {
                $rgPersistOffset[] = self::getInt4d($stream, $pos);
                $pos += 4;
                $rHeader['recLen'] -= 4;
            }
        } while ($rHeader['recLen'] > 0);
        $this->rgPersistDirEntry = $rgPersistOffset;
    }

    /**
     * A container record that specifies information about the headers (1) and footers within a slide.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd904856(v=office.12).aspx
     * @return array
     */
    private function readRecordPerSlideHeadersFootersContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_HEADERSFOOTERS) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies whether a shape is a placeholder shape.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd923930(v=office.12).aspx
     * @return array
     */
    private function readRecordPlaceholderAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PLACEHOLDERATOM && $data['recLen'] == 0x00000008) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies a collection of re-color mappings for a metafile ([MS-WMF]).
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd904899(v=office.12).aspx
     * @return array
     */
    private function readRecordRecolorInfoAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_RECOLORINFOATOM) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies that a shape is a header or footerplaceholder shape.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd910800(v=office.12).aspx
     * @return array
     */
    private function readRecordRoundTripHFPlaceholder12Atom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM && $data['recLen'] == 0x00000001) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies a shape identifier.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd772926(v=office.12).aspx
     * @return array
     */
    private function readRecordRoundTripShapeId12Atom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPSHAPEID12ATOM && $data['recLen'] == 0x00000004) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies information about a slide that synchronizes to a slide in a slide library.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
     * @return array
     */
    private function readRecordRoundTripSlideSyncInfo12Container($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_ROUNDTRIPSLIDESYNCINFO12) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies shape-level Boolean flags.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd908949(v=office.12).aspx
     * @return array
     */
    private function readRecordShapeFlags10Atom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SHAPEFLAGS10ATOM && $data['recLen'] == 0x00000001) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies shape-level Boolean flags.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd925824(v=office.12).aspx
     * @return array
     */
    private function readRecordShapeFlagsAtom($stream, $pos)
    {
        $arrayReturn = array(
                'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SHAPEATOM && $data['recLen'] == 0x00000001) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies programmable tags with additional binary shape data.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd911033(v=office.12).aspx
     * @return array
     */
    private function readRecordShapeProgBinaryTagContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGBINARYTAG) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies programmable tags with additional shape data.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd911266(v=office.12).aspx
     */
    private function readRecordShapeProgTagsContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGTAGS) {
            // Record Header
            $arrayReturn['length'] += 8;

            $length = 0;
            do {
                $dataHeaderRG = $this->loadRecordHeader($stream, $pos + $arrayReturn['length'] + $length);
                switch ($dataHeaderRG['recType']) {
                    case self::RT_PROGBINARYTAG:
                        $dataRG = $this->readRecordShapeProgBinaryTagContainer($stream, $pos + $arrayReturn['length'] + $length);
                        $length += $dataRG['length'];
                        break;
                    //case self::RT_PROGSTRINGTAG:
                    default:
                        throw new \Exception('Feature not implemented (l.'.__LINE__.')');
                }
            } while ($length < $data['recLen']);
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies information about a slide.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @link https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
     */
    private function readRecordSlideAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x2 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDEATOM) {
            // Record Header
            $arrayReturn['length'] += 8;
            // slideAtom > geom
            $arrayReturn['length'] += 4;
            // slideAtom > rgPlaceholderTypes
            $rgPlaceholderTypes = array();
            for ($inc = 0; $inc < 8; $inc++) {
                $rgPlaceholderTypes[] = self::getInt1d($this->streamPowerpointDocument, $pos);
                $arrayReturn['length'] += 1;
            }

            // slideAtom > masterIdRef
            $arrayReturn['length'] += 4;
            // slideAtom > notesIdRef
            $arrayReturn['length'] += 4;
            // slideAtom > slideFlags
            $arrayReturn['length'] += 2;
            // slideAtom > unused;
            $arrayReturn['length'] += 2;
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies a presentation slide or title master slide.
     * @param string $stream
     * @param int $pos
     * @throws \Exception
     * @link http://msdn.microsoft.com/en-us/library/dd946323(v=office.12).aspx
     */
    private function readRecordSlideContainer($stream, $pos)
    {
        // Core
        $this->oPhpPresentation->createSlide();
        $this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);

        // *** slideAtom (32 bytes)
        $slideAtom = $this->readRecordSlideAtom($stream, $pos);
        if ($slideAtom['length'] == 0) {
            throw new \Exception('PowerPoint97 Reader : record SlideAtom');
        }
        $pos += $slideAtom['length'];

        // *** slideShowSlideInfoAtom (24 bytes)
        $slideShowInfoAtom = $this->readRecordSlideShowSlideInfoAtom($stream, $pos);
        $pos += $slideShowInfoAtom['length'];

        // *** perSlideHFContainer (variable) : optional
        $perSlideHFContainer = $this->readRecordPerSlideHeadersFootersContainer($stream, $pos);
        $pos += $perSlideHFContainer['length'];

        // *** rtSlideSyncInfo12 (variable) : optional
        $rtSlideSyncInfo12 = $this->readRecordRoundTripSlideSyncInfo12Container($stream, $pos);
        $pos += $rtSlideSyncInfo12['length'];

        // *** drawing (variable)
        $drawing = $this->readRecordDrawingContainer($stream, $pos);
        $pos += $drawing['length'];

        // *** slideSchemeColorSchemeAtom (40 bytes)
        $slideSchemeColorAtom = $this->readRecordSlideSchemeColorSchemeAtom($stream, $pos);
        if ($slideSchemeColorAtom['length'] == 0) {
            throw new \Exception('PowerPoint97 Reader : record SlideSchemeColorSchemeAtom');
        }
        $pos += $slideSchemeColorAtom['length'];

        // *** slideNameAtom (variable)
        $slideNameAtom = $this->readRecordSlideNameAtom($stream, $pos);
        $pos += $slideNameAtom['length'];

        // *** slideProgTagsContainer (variable).
        $slideProgTags = $this->readRecordSlideProgTagsContainer($stream, $pos);
        $pos += $slideProgTags['length'];

        // *** rgRoundTripSlide (variable)
    }

    /**
     * An atom record that specifies the name of a slide.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd906297(v=office.12).aspx
     * @return array
     */
    private function readRecordSlideNameAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
            'slideName' => '',
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x003 && $data['recType'] == self::RT_CSTRING && $data['recLen'] % 2 == 0) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $strLen = ($data['recLen'] / 2);
            for ($inc = 0; $inc < $strLen; $inc++) {
                $char = self::getInt2d($stream, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 2;
                $arrayReturn['slideName'] .= Text::chr($char);
            }
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies a slide number metacharacter.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd945703(v=office.12).aspx
     * @return array
     */
    private function readRecordSlideNumberMCAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDENUMBERMETACHARATOM && $data['recLen'] == 0x00000004) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Datas
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies programmable tags with additional slide data.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd951946(v=office.12).aspx
     * @return array
     */
    private function readRecordSlideProgTagsContainer($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0xF && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_PROGTAGS) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * A container record that specifies the color scheme used by a slide.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd949420(v=office.12).aspx
     * @return array
     */
    private function readRecordSlideSchemeColorSchemeAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x001 && $data['recType'] == self::RT_COLORSCHEMEATOM && $data['recLen'] == 0x00000020) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length
            $rgSchemeColor = array();
            for ($inc = 0; $inc <= 7; $inc++) {
                $rgSchemeColor[] = array(
                    'red' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4),
                    'green' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4 + 1),
                    'blue' => self::getInt1d($stream, $pos + $arrayReturn['length'] + $inc * 4 + 2),
                );
            }
            $arrayReturn['length'] += (8 * 4);
        }

        return $arrayReturn;
    }

    /**
     * An atom record that specifies what transition effect to perform during a slide show, and how to advance to the next presentation slide.
     * @param string $stream
     * @param integer $pos
     * @link https://msdn.microsoft.com/en-us/library/dd943408(v=office.12).aspx
     * @return array
     */
    private function readRecordSlideShowSlideInfoAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] == 0x0 && $data['recInstance'] == 0x000 && $data['recType'] == self::RT_SLIDESHOWSLIDEINFOATOM && $data['recLen'] == 0x00000010) {
            // Record Header
            $arrayReturn['length'] += 8;
            // Length;
            $arrayReturn['length'] += $data['recLen'];
        }

        return $arrayReturn;
    }

    /**
     * UserEditAtom
     * @link http://msdn.microsoft.com/en-us/library/dd945746(v=office.12).aspx
     * @param string $stream
     * @param integer $pos
     * @throws \Exception
     */
    private function readRecordUserEditAtom($stream, $pos)
    {
        $rHeader = $this->loadRecordHeader($stream, $pos);
        $pos += 8;
        if ($rHeader['recVer'] != 0x0 || $rHeader['recInstance'] != 0x000 || $rHeader['recType'] != self::RT_USEREDITATOM || ($rHeader['recLen'] != 0x0000001C && $rHeader['recLen'] != 0x00000020)) {
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > RecordHeader).');
        }

        // lastSlideIdRef
        $pos += 4;
        // version
        $pos += 2;

        // minorVersion
        $minorVersion = self::getInt1d($stream, $pos);
        $pos += 1;
        if ($minorVersion != 0x00) {
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > minorVersion).');
        }

        // majorVersion
        $majorVersion = self::getInt1d($stream, $pos);
        $pos += 1;
        if ($majorVersion != 0x03) {
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > majorVersion).');
        }

        // offsetLastEdit
        $pos += 4;
        // offsetPersistDirectory
        $this->offsetPersistDirectory = self::getInt4d($stream, $pos);
        $pos += 4;

        // docPersistIdRef
        $docPersistIdRef  = self::getInt4d($stream, $pos);
        $pos += 4;
        if ($docPersistIdRef != 0x00000001) {
            throw new \Exception('File PowerPoint 97 in error (Location : UserEditAtom > docPersistIdRef).');
        }

        // persistIdSeed
        $pos += 4;
        // lastView
        $pos += 2;
        // unused
        $pos += 2;
    }

    /**
     * A structure that specifies the character-level formatting of a run of text.
     * @param string $stream
     * @param int $pos
     * @param int $strLenRT
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd945870(v=office.12).aspx
     */
    private function readStructureTextCFRun($stream, $pos, $strLenRT)
    {
        $arrayReturn = array(
            'length' => 0,
            'strLenRT' => $strLenRT,
        );

        // rgTextCFRun
        $countRgTextCFRun = self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['strLenRT'] -= $countRgTextCFRun;
        $arrayReturn['length'] += 4;
        $arrayReturn['partLength'] = $countRgTextCFRun;

        $masks = self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['length'] += 4;

        $masksData = array();
        $masksData['bold'] = ($masks >> 0) & bindec('1');
        $masksData['italic'] = ($masks >> 1) & bindec('1');
        $masksData['underline'] = ($masks >> 2) & bindec('1');
        $masksData['unused1'] = ($masks >> 3) & bindec('1');
        $masksData['shadow'] = ($masks >> 4) & bindec('1');
        $masksData['fehint'] = ($masks >> 5) & bindec('1');
        $masksData['unused2'] = ($masks >> 6) & bindec('1');
        $masksData['kumi'] = ($masks >> 7) & bindec('1');
        $masksData['unused3'] = ($masks >> 8) & bindec('1');
        $masksData['emboss'] = ($masks >> 9) & bindec('1');
        $masksData['fHasStyle'] = ($masks >> 10) & bindec('1111');
        $masksData['unused4'] = ($masks >> 14) & bindec('11');
        $masksData['typeface'] = ($masks >> 16) & bindec('1');
        $masksData['size'] = ($masks >> 17) & bindec('1');
        $masksData['color'] = ($masks >> 18) & bindec('1');
        $masksData['position'] = ($masks >> 19) & bindec('1');
        $masksData['pp10ext'] = ($masks >> 20) & bindec('1');
        $masksData['oldEATypeface'] = ($masks >> 21) & bindec('1');
        $masksData['ansiTypeface'] = ($masks >> 22) & bindec('1');
        $masksData['symbolTypeface'] = ($masks >> 23) & bindec('1');
        $masksData['newEATypeface'] = ($masks >> 24) & bindec('1');
        $masksData['csTypeface'] = ($masks >> 25) & bindec('1');
        $masksData['pp11ext'] = ($masks >> 26) & bindec('1');
        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) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;

            $fontStyleFlags = array();
            $fontStyleFlags['bold'] = ($data >> 0) & bindec('1');
            $fontStyleFlags['italic'] = ($data >> 1) & bindec('1');
            $fontStyleFlags['underline'] = ($data >> 2) & bindec('1');
            $fontStyleFlags['unused1'] = ($data >> 3) & bindec('1');
            $fontStyleFlags['shadow'] = ($data >> 4) & bindec('1');
            $fontStyleFlags['fehint'] = ($data >> 5) & bindec('1');
            $fontStyleFlags['unused2'] = ($data >> 6) & bindec('1');
            $fontStyleFlags['kumi'] = ($data >> 7) & bindec('1');
            $fontStyleFlags['unused3'] = ($data >> 8) & bindec('1');
            $fontStyleFlags['emboss'] = ($data >> 9) & bindec('1');
            $fontStyleFlags['pp9rt'] = ($data >> 10) & bindec('1111');
            $fontStyleFlags['unused4'] = ($data >> 14) & bindec('11');

            $arrayReturn['bold'] = ($fontStyleFlags['bold'] == 1) ? true : false;
            $arrayReturn['italic'] = ($fontStyleFlags['italic'] == 1) ? true : false;
            $arrayReturn['underline'] = ($fontStyleFlags['underline'] == 1) ? true : false;
        }
        if ($masksData['typeface'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            $arrayReturn['fontName'] = isset($this->arrayFonts[$data]) ? $this->arrayFonts[$data] : '';
        }
        if ($masksData['oldEATypeface'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['ansiTypeface'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['symbolTypeface'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['size'] == 1) {
            $arrayReturn['fontSize'] = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['color'] == 1) {
            $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;
            $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;
            $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;
            $index = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;

            if ($index == 0xFE) {
                $strColor = str_pad(dechex($red), 2, STR_PAD_LEFT, '0');
                $strColor .= str_pad(dechex($green), 2, STR_PAD_LEFT, '0');
                $strColor .= str_pad(dechex($blue), 2, STR_PAD_LEFT, '0');

                $arrayReturn['color'] = new Color('FF'.$strColor);
            }
        }
        if ($masksData['position'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }

        return $arrayReturn;
    }

    /**
     * A structure that specifies the paragraph-level formatting of a run of text.
     * @param string $stream
     * @param integer $pos
     * @param integer $strLenRT
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd923535(v=office.12).aspx
     */
    private function readStructureTextPFRun($stream, $pos, $strLenRT)
    {
        $arrayReturn = array(
            'length' => 0,
            'strLenRT' => $strLenRT,
        );

        // rgTextPFRun
        $countRgTextPFRun = self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['strLenRT'] -= $countRgTextPFRun;
        $arrayReturn['length'] += 4;

        // indent
        $arrayReturn['length'] += 2;

        $masks = self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['length'] += 4;

        $masksData = array();
        $masksData['hasBullet'] = ($masks >> 0) & bindec('1');
        $masksData['bulletHasFont'] = ($masks >> 1) & bindec('1');
        $masksData['bulletHasColor'] = ($masks >> 2) & bindec('1');
        $masksData['bulletHasSize'] = ($masks >> 3) & bindec('1');
        $masksData['bulletFont'] = ($masks >> 4) & bindec('1');
        $masksData['bulletColor'] = ($masks >> 5) & bindec('1');
        $masksData['bulletSize'] = ($masks >> 6) & bindec('1');
        $masksData['bulletChar'] = ($masks >> 7) & bindec('1');
        $masksData['leftMargin'] = ($masks >> 8) & bindec('1');
        $masksData['unused'] = ($masks >> 9) & bindec('1');
        $masksData['indent'] = ($masks >> 10) & bindec('1');
        $masksData['align'] = ($masks >> 11) & bindec('1');
        $masksData['lineSpacing'] = ($masks >> 12) & bindec('1');
        $masksData['spaceBefore'] = ($masks >> 13) & bindec('1');
        $masksData['spaceAfter'] = ($masks >> 14) & bindec('1');
        $masksData['defaultTabSize'] = ($masks >> 15) & bindec('1');
        $masksData['fontAlign'] = ($masks >> 16) & bindec('1');
        $masksData['charWrap'] = ($masks >> 17) & bindec('1');
        $masksData['wordWrap'] = ($masks >> 18) & bindec('1');
        $masksData['overflow'] = ($masks >> 19) & bindec('1');
        $masksData['tabStops'] = ($masks >> 20) & bindec('1');
        $masksData['textDirection'] = ($masks >> 21) & bindec('1');
        $masksData['reserved1'] = ($masks >> 22) & bindec('1');
        $masksData['bulletBlip'] = ($masks >> 23) & bindec('1');
        $masksData['bulletScheme'] = ($masks >> 24) & bindec('1');
        $masksData['bulletHasScheme'] = ($masks >> 25) & bindec('1');

        $bulletFlags = array();
        if ($masksData['hasBullet'] == 1 || $masksData['bulletHasFont'] == 1  || $masksData['bulletHasColor'] == 1  || $masksData['bulletHasSize'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;

            $bulletFlags['fHasBullet'] = ($data >> 0) & bindec('1');
            $bulletFlags['fBulletHasFont'] = ($data >> 1) & bindec('1');
            $bulletFlags['fBulletHasColor'] = ($data >> 2) & bindec('1');
            $bulletFlags['fBulletHasSize'] = ($data >> 3) & bindec('1');
        }
        if ($masksData['bulletChar'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            $arrayReturn['bulletChar'] = chr($data);
        }
        if ($masksData['bulletFont'] == 1) {
            // $data = self::getInt2d($stream, $pos);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['bulletSize'] == 1) {
            // $data = self::getInt2d($stream, $pos);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['bulletColor'] == 1) {
            $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;
            $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;
            $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;
            $index = self::getInt1d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 1;

            if ($index == 0xFE) {
                $strColor = str_pad(dechex($red), 2, STR_PAD_LEFT, '0');
                $strColor .= str_pad(dechex($green), 2, STR_PAD_LEFT, '0');
                $strColor .= str_pad(dechex($blue), 2, STR_PAD_LEFT, '0');
            }
        }
        if ($masksData['align'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            switch ($data) {
                case 0x0000:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_LEFT;
                    break;
                case 0x0001:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_CENTER;
                    break;
                case 0x0002:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_RIGHT;
                    break;
                case 0x0003:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_JUSTIFY;
                    break;
                case 0x0004:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_DISTRIBUTED;
                    break;
                case 0x0005:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_DISTRIBUTED;
                    break;
                case 0x0006:
                    $arrayReturn['alignH'] = Alignment::HORIZONTAL_JUSTIFY;
                    break;
                default:
                    break;
            }
        }
        if ($masksData['lineSpacing'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['spaceBefore'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['spaceAfter'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['leftMargin'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            $arrayReturn['leftMargin'] = (int)round($data/6);
        }
        if ($masksData['indent'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            $arrayReturn['indent'] = (int)round($data/6);
        }
        if ($masksData['defaultTabSize'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['tabStops'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }
        if ($masksData['fontAlign'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['charWrap'] == 1 || $masksData['wordWrap'] == 1 || $masksData['overflow'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['textDirection'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }

        return $arrayReturn;
    }

    /**
     * A structure that specifies language and spelling information for a run of text.
     * @param string $stream
     * @param integer $pos
     * @param string $strLenRT
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd909603(v=office.12).aspx
     */
    private function readStructureTextSIRun($stream, $pos, $strLenRT)
    {
        $arrayReturn = array(
            'length' => 0,
            'strLenRT' => $strLenRT,
        );

        $arrayReturn['strLenRT'] -= self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['length'] += 4;

        $data = self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['length'] += 4;
        $masksData = array();
        $masksData['spell'] = ($data >> 0) & bindec('1');
        $masksData['lang'] = ($data >> 1) & bindec('1');
        $masksData['altLang'] = ($data >> 2) & bindec('1');
        $masksData['unused1'] = ($data >> 3) & bindec('1');
        $masksData['unused2'] = ($data >> 4) & bindec('1');
        $masksData['fPp10ext'] = ($data >> 5) & bindec('1');
        $masksData['fBidi'] = ($data >> 6) & bindec('1');
        $masksData['unused3'] = ($data >> 7) & bindec('1');
        $masksData['reserved1'] = ($data >> 8) & bindec('1');
        $masksData['smartTag'] = ($data >> 9) & bindec('1');

        if ($masksData['spell'] == 1) {
            $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            $masksSpell = array();
            $masksSpell['error'] = ($data >> 0) & bindec('1');
            $masksSpell['clean'] = ($data >> 1) & bindec('1');
            $masksSpell['grammar'] = ($data >> 2) & bindec('1');
        }
        if ($masksData['lang'] == 1) {
            // $data = self::getInt2d($stream, $pos);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['altLang'] == 1) {
            // $data = self::getInt2d($stream, $pos);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fBidi'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }
        if ($masksData['fPp10ext'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }
        if ($masksData['smartTag'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }

        return $arrayReturn;
    }

    /**
     * A structure that specifies tabbing, margins, and indentation for text.
     * @param string $stream
     * @param integer $pos
     * @return array
     * @throws \Exception
     * @link https://msdn.microsoft.com/en-us/library/dd922749(v=office.12).aspx
     */
    private function readStructureTextRuler($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = self::getInt4d($stream, $pos + $arrayReturn['length']);
        $arrayReturn['length'] += 4;

        $masksData = array();
        $masksData['fDefaultTabSize'] = ($data >> 0) & bindec('1');
        $masksData['fCLevels'] = ($data >> 1) & bindec('1');
        $masksData['fTabStops'] = ($data >> 2) & bindec('1');
        $masksData['fLeftMargin1'] = ($data >> 3) & bindec('1');
        $masksData['fLeftMargin2'] = ($data >> 4) & bindec('1');
        $masksData['fLeftMargin3'] = ($data >> 5) & bindec('1');
        $masksData['fLeftMargin4'] = ($data >> 6) & bindec('1');
        $masksData['fLeftMargin5'] = ($data >> 7) & bindec('1');
        $masksData['fIndent1'] = ($data >> 8) & bindec('1');
        $masksData['fIndent2'] = ($data >> 9) & bindec('1');
        $masksData['fIndent3'] = ($data >> 10) & bindec('1');
        $masksData['fIndent4'] = ($data >> 11) & bindec('1');
        $masksData['fIndent5'] = ($data >> 12) & bindec('1');

        if ($masksData['fCLevels'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }
        if ($masksData['fDefaultTabSize'] == 1) {
            throw new \Exception('Feature not implemented (l.'.__LINE__.')');
        }
        if ($masksData['fTabStops'] == 1) {
            $count = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
            $arrayTabStops = array();
            for ($inc = 0; $inc < $count; $inc++) {
                $position = self::getInt2d($stream, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 2;
                $type = self::getInt2d($stream, $pos + $arrayReturn['length']);
                $arrayReturn['length'] += 2;
                $arrayTabStops[] = array(
                    'position' => $position,
                    'type' => $type,
                );
            }
        }
        if ($masksData['fLeftMargin1'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fIndent1'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fLeftMargin2'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fIndent2'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fLeftMargin3'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fIndent3'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fLeftMargin4'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fIndent4'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fLeftMargin5'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }
        if ($masksData['fIndent5'] == 1) {
            // $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
            $arrayReturn['length'] += 2;
        }

        return $arrayReturn;
    }

    /**
     * @param $stream
     * @param int $pos
     * @throws \Exception
     */
    private function readRecordNotesContainer($stream, $pos)
    {
        // notesAtom
        $notesAtom = $this->readRecordNotesAtom($stream, $pos);
        $pos += $notesAtom['length'];

        // drawing
        $drawing = $this->readRecordDrawingContainer($stream, $pos);
        $pos += $drawing['length'];

        // slideSchemeColorSchemeAtom
        // slideNameAtom
        // slideProgTagsContainer
        // rgNotesRoundTripAtom
    }

    /**
     * @param $stream
     * @param int $pos
     * @return array
     * @throws \Exception
     */
    private function readRecordNotesAtom($stream, $pos)
    {
        $arrayReturn = array(
            'length' => 0,
        );

        $data = $this->loadRecordHeader($stream, $pos);
        if ($data['recVer'] != 0x1 || $data['recInstance'] != 0x000 || $data['recType'] != self::RT_NOTESATOM || $data['recLen'] != 0x00000008) {
            throw new \Exception('File PowerPoint 97 in error (Location : NotesAtom > RecordHeader)');
        }
        // Record Header
        $arrayReturn['length'] += 8;
        // NotesAtom > slideIdRef
        $notesIdRef = self::getInt4d($stream, $pos + $arrayReturn['length']);
        if ($notesIdRef == -2147483648) {
            $notesIdRef = 0;
        }
        $this->currentNote = $notesIdRef;
        $arrayReturn['length'] += 4;

        // NotesAtom > slideFlags
        $arrayReturn['length'] += 2;
        // NotesAtom > unused
        $arrayReturn['length'] += 2;

        return $arrayReturn;
    }
}
