mirror of
https://github.com/Karaka-Management/Resources.git
synced 2026-01-10 21:08:41 +00:00
3499 lines
148 KiB
PHP
Executable File
3499 lines
148 KiB
PHP
Executable File
<?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.
|
|
*
|
|
* @see https://github.com/PHPOffice/PHPPresentation
|
|
*
|
|
* @copyright 2009-2015 PHPPresentation contributors
|
|
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace PhpOffice\PhpPresentation\Reader;
|
|
|
|
use Exception;
|
|
use PhpOffice\Common\Microsoft\OLERead;
|
|
use PhpOffice\Common\Text;
|
|
use PhpOffice\PhpPresentation\AbstractShape;
|
|
use PhpOffice\PhpPresentation\Exception\FeatureNotImplementedException;
|
|
use PhpOffice\PhpPresentation\Exception\FileNotFoundException;
|
|
use PhpOffice\PhpPresentation\Exception\InvalidFileFormatException;
|
|
use PhpOffice\PhpPresentation\PhpPresentation;
|
|
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\Bullet;
|
|
use PhpOffice\PhpPresentation\Style\Color;
|
|
use PhpOffice\PhpPresentation\Style\Font;
|
|
|
|
/**
|
|
* Serialized format reader.
|
|
*/
|
|
class PowerPoint97 implements ReaderInterface
|
|
{
|
|
public const OFFICEARTBLIPEMF = 0xF01A;
|
|
public const OFFICEARTBLIPWMF = 0xF01B;
|
|
public const OFFICEARTBLIPPICT = 0xF01C;
|
|
public const OFFICEARTBLIPJPG = 0xF01D;
|
|
public const OFFICEARTBLIPPNG = 0xF01E;
|
|
public const OFFICEARTBLIPDIB = 0xF01F;
|
|
public const OFFICEARTBLIPTIFF = 0xF029;
|
|
public const OFFICEARTBLIPJPEG = 0xF02A;
|
|
|
|
/**
|
|
* @see http://msdn.microsoft.com/en-us/library/dd945336(v=office.12).aspx
|
|
*/
|
|
public const RT_ANIMATIONINFO = 0x1014;
|
|
public const RT_ANIMATIONINFOATOM = 0x0FF1;
|
|
public const RT_BINARYTAGDATABLOB = 0x138B;
|
|
public const RT_BLIPCOLLECTION9 = 0x07F8;
|
|
public const RT_BLIPENTITY9ATOM = 0x07F9;
|
|
public const RT_BOOKMARKCOLLECTION = 0x07E3;
|
|
public const RT_BOOKMARKENTITYATOM = 0x0FD0;
|
|
public const RT_BOOKMARKSEEDATOM = 0x07E9;
|
|
public const RT_BROADCASTDOCINFO9 = 0x177E;
|
|
public const RT_BROADCASTDOCINFO9ATOM = 0x177F;
|
|
public const RT_BUILDATOM = 0x2B03;
|
|
public const RT_BUILDLIST = 0x2B02;
|
|
public const RT_CHARTBUILD = 0x2B04;
|
|
public const RT_CHARTBUILDATOM = 0x2B05;
|
|
public const RT_COLORSCHEMEATOM = 0x07F0;
|
|
public const RT_COMMENT10 = 0x2EE0;
|
|
public const RT_COMMENT10ATOM = 0x2EE1;
|
|
public const RT_COMMENTINDEX10 = 0x2EE4;
|
|
public const RT_COMMENTINDEX10ATOM = 0x2EE5;
|
|
public const RT_CRYPTSESSION10CONTAINER = 0x2F14;
|
|
public const RT_CURRENTUSERATOM = 0x0FF6;
|
|
public const RT_CSTRING = 0x0FBA;
|
|
public const RT_DATETIMEMETACHARATOM = 0x0FF7;
|
|
public const RT_DEFAULTRULERATOM = 0x0FAB;
|
|
public const RT_DOCROUTINGSLIPATOM = 0x0406;
|
|
public const RT_DIAGRAMBUILD = 0x2B06;
|
|
public const RT_DIAGRAMBUILDATOM = 0x2B07;
|
|
public const RT_DIFF10 = 0x2EED;
|
|
public const RT_DIFF10ATOM = 0x2EEE;
|
|
public const RT_DIFFTREE10 = 0x2EEC;
|
|
public const RT_DOCTOOLBARSTATES10ATOM = 0x36B1;
|
|
public const RT_DOCUMENT = 0x03E8;
|
|
public const RT_DOCUMENTATOM = 0x03E9;
|
|
public const RT_DRAWING = 0x040C;
|
|
public const RT_DRAWINGGROUP = 0x040B;
|
|
public const RT_ENDDOCUMENTATOM = 0x03EA;
|
|
public const RT_EXTERNALAVIMOVIE = 0x1006;
|
|
public const RT_EXTERNALCDAUDIO = 0x100E;
|
|
public const RT_EXTERNALCDAUDIOATOM = 0x1012;
|
|
public const RT_EXTERNALHYPERLINK = 0x0FD7;
|
|
public const RT_EXTERNALHYPERLINK9 = 0x0FE4;
|
|
public const RT_EXTERNALHYPERLINKATOM = 0x0FD3;
|
|
public const RT_EXTERNALHYPERLINKFLAGSATOM = 0x1018;
|
|
public const RT_EXTERNALMCIMOVIE = 0x1007;
|
|
public const RT_EXTERNALMEDIAATOM = 0x1004;
|
|
public const RT_EXTERNALMIDIAUDIO = 0x100D;
|
|
public const RT_EXTERNALOBJECTLIST = 0x0409;
|
|
public const RT_EXTERNALOBJECTLISTATOM = 0x040A;
|
|
public const RT_EXTERNALOBJECTREFATOM = 0x0BC1;
|
|
public const RT_EXTERNALOLECONTROL = 0x0FEE;
|
|
public const RT_EXTERNALOLECONTROLATOM = 0x0FFB;
|
|
public const RT_EXTERNALOLEEMBED = 0x0FCC;
|
|
public const RT_EXTERNALOLEEMBEDATOM = 0x0FCD;
|
|
public const RT_EXTERNALOLELINK = 0x0FCE;
|
|
public const RT_EXTERNALOLELINKATOM = 0x0FD1;
|
|
public const RT_EXTERNALOLEOBJECTATOM = 0x0FC3;
|
|
public const RT_EXTERNALOLEOBJECTSTG = 0x1011;
|
|
public const RT_EXTERNALVIDEO = 0x1005;
|
|
public const RT_EXTERNALWAVAUDIOEMBEDDED = 0x100F;
|
|
public const RT_EXTERNALWAVAUDIOEMBEDDEDATOM = 0x1013;
|
|
public const RT_EXTERNALWAVAUDIOLINK = 0x1010;
|
|
public const RT_ENVELOPEDATA9ATOM = 0x1785;
|
|
public const RT_ENVELOPEFLAGS9ATOM = 0x1784;
|
|
public const RT_ENVIRONMENT = 0x03F2;
|
|
public const RT_FONTCOLLECTION = 0x07D5;
|
|
public const RT_FONTCOLLECTION10 = 0x07D6;
|
|
public const RT_FONTEMBEDDATABLOB = 0x0FB8;
|
|
public const RT_FONTEMBEDFLAGS10ATOM = 0x32C8;
|
|
public const RT_FILTERPRIVACYFLAGS10ATOM = 0x36B0;
|
|
public const RT_FONTENTITYATOM = 0x0FB7;
|
|
public const RT_FOOTERMETACHARATOM = 0x0FFA;
|
|
public const RT_GENERICDATEMETACHARATOM = 0x0FF8;
|
|
public const RT_GRIDSPACING10ATOM = 0x040D;
|
|
public const RT_GUIDEATOM = 0x03FB;
|
|
public const RT_HANDOUT = 0x0FC9;
|
|
public const RT_HASHCODEATOM = 0x2B00;
|
|
public const RT_HEADERSFOOTERS = 0x0FD9;
|
|
public const RT_HEADERSFOOTERSATOM = 0x0FDA;
|
|
public const RT_HEADERMETACHARATOM = 0x0FF9;
|
|
public const RT_HTMLDOCINFO9ATOM = 0x177B;
|
|
public const RT_HTMLPUBLISHINFOATOM = 0x177C;
|
|
public const RT_HTMLPUBLISHINFO9 = 0x177D;
|
|
public const RT_INTERACTIVEINFO = 0x0FF2;
|
|
public const RT_INTERACTIVEINFOATOM = 0x0FF3;
|
|
public const RT_KINSOKU = 0x0FC8;
|
|
public const RT_KINSOKUATOM = 0x0FD2;
|
|
public const RT_LEVELINFOATOM = 0x2B0A;
|
|
public const RT_LINKEDSHAPE10ATOM = 0x2EE6;
|
|
public const RT_LINKEDSLIDE10ATOM = 0x2EE7;
|
|
public const RT_LIST = 0x07D0;
|
|
public const RT_MAINMASTER = 0x03F8;
|
|
public const RT_MASTERTEXTPROPATOM = 0x0FA2;
|
|
public const RT_METAFILE = 0x0FC1;
|
|
public const RT_NAMEDSHOW = 0x0411;
|
|
public const RT_NAMEDSHOWS = 0x0410;
|
|
public const RT_NAMEDSHOWSLIDESATOM = 0x0412;
|
|
public const RT_NORMALVIEWSETINFO9 = 0x0414;
|
|
public const RT_NORMALVIEWSETINFO9ATOM = 0x0415;
|
|
public const RT_NOTES = 0x03F0;
|
|
public const RT_NOTESATOM = 0x03F1;
|
|
public const RT_NOTESTEXTVIEWINFO9 = 0x0413;
|
|
public const RT_OUTLINETEXTPROPS9 = 0x0FAE;
|
|
public const RT_OUTLINETEXTPROPS10 = 0x0FB3;
|
|
public const RT_OUTLINETEXTPROPS11 = 0x0FB5;
|
|
public const RT_OUTLINETEXTPROPSHEADER9ATOM = 0x0FAF;
|
|
public const RT_OUTLINETEXTREFATOM = 0x0F9E;
|
|
public const RT_OUTLINEVIEWINFO = 0x0407;
|
|
public const RT_PERSISTDIRECTORYATOM = 0x1772;
|
|
public const RT_PARABUILD = 0x2B08;
|
|
public const RT_PARABUILDATOM = 0x2B09;
|
|
public const RT_PHOTOALBUMINFO10ATOM = 0x36B2;
|
|
public const RT_PLACEHOLDERATOM = 0x0BC3;
|
|
public const RT_PRESENTATIONADVISORFLAGS9ATOM = 0x177A;
|
|
public const RT_PRINTOPTIONSATOM = 0x1770;
|
|
public const RT_PROGBINARYTAG = 0x138A;
|
|
public const RT_PROGSTRINGTAG = 0x1389;
|
|
public const RT_PROGTAGS = 0x1388;
|
|
public const RT_RECOLORINFOATOM = 0x0FE7;
|
|
public const RT_RTFDATETIMEMETACHARATOM = 0x1015;
|
|
public const RT_ROUNDTRIPANIMATIONATOM12ATOM = 0x2B0B;
|
|
public const RT_ROUNDTRIPANIMATIONHASHATOM12ATOM = 0x2B0D;
|
|
public const RT_ROUNDTRIPCOLORMAPPING12ATOM = 0x040F;
|
|
public const RT_ROUNDTRIPCOMPOSITEMASTERID12ATOM = 0x041D;
|
|
public const RT_ROUNDTRIPCONTENTMASTERID12ATOM = 0x0422;
|
|
public const RT_ROUNDTRIPCONTENTMASTERINFO12ATOM = 0x041E;
|
|
public const RT_ROUNDTRIPCUSTOMTABLESTYLES12ATOM = 0x0428;
|
|
public const RT_ROUNDTRIPDOCFLAGS12ATOM = 0x0425;
|
|
public const RT_ROUNDTRIPHEADERFOOTERDEFAULTS12ATOM = 0x0424;
|
|
public const RT_ROUNDTRIPHFPLACEHOLDER12ATOM = 0x0420;
|
|
public const RT_ROUNDTRIPNEWPLACEHOLDERID12ATOM = 0x0BDD;
|
|
public const RT_ROUNDTRIPNOTESMASTERTEXTSTYLES12ATOM = 0x0427;
|
|
public const RT_ROUNDTRIPOARTTEXTSTYLES12ATOM = 0x0423;
|
|
public const RT_ROUNDTRIPORIGINALMAINMASTERID12ATOM = 0x041C;
|
|
public const RT_ROUNDTRIPSHAPECHECKSUMFORCL12ATOM = 0x0426;
|
|
public const RT_ROUNDTRIPSHAPEID12ATOM = 0x041F;
|
|
public const RT_ROUNDTRIPSLIDESYNCINFO12 = 0x3714;
|
|
public const RT_ROUNDTRIPSLIDESYNCINFOATOM12 = 0x3715;
|
|
public const RT_ROUNDTRIPTHEME12ATOM = 0x040E;
|
|
public const RT_SHAPEATOM = 0x0BDB;
|
|
public const RT_SHAPEFLAGS10ATOM = 0x0BDC;
|
|
public const RT_SLIDE = 0x03EE;
|
|
public const RT_SLIDEATOM = 0x03EF;
|
|
public const RT_SLIDEFLAGS10ATOM = 0x2EEA;
|
|
public const RT_SLIDELISTENTRY10ATOM = 0x2EF0;
|
|
public const RT_SLIDELISTTABLE10 = 0x2EF1;
|
|
public const RT_SLIDELISTWITHTEXT = 0x0FF0;
|
|
public const RT_SLIDELISTTABLESIZE10ATOM = 0x2EEF;
|
|
public const RT_SLIDENUMBERMETACHARATOM = 0x0FD8;
|
|
public const RT_SLIDEPERSISTATOM = 0x03F3;
|
|
public const RT_SLIDESHOWDOCINFOATOM = 0x0401;
|
|
public const RT_SLIDESHOWSLIDEINFOATOM = 0x03F9;
|
|
public const RT_SLIDETIME10ATOM = 0x2EEB;
|
|
public const RT_SLIDEVIEWINFO = 0x03FA;
|
|
public const RT_SLIDEVIEWINFOATOM = 0x03FE;
|
|
public const RT_SMARTTAGSTORE11CONTAINER = 0x36B3;
|
|
public const RT_SOUND = 0x07E6;
|
|
public const RT_SOUNDCOLLECTION = 0x07E4;
|
|
public const RT_SOUNDCOLLECTIONATOM = 0x07E5;
|
|
public const RT_SOUNDDATABLOB = 0x07E7;
|
|
public const RT_SORTERVIEWINFO = 0x0408;
|
|
public const RT_STYLETEXTPROPATOM = 0x0FA1;
|
|
public const RT_STYLETEXTPROP10ATOM = 0x0FB1;
|
|
public const RT_STYLETEXTPROP11ATOM = 0x0FB6;
|
|
public const RT_STYLETEXTPROP9ATOM = 0x0FAC;
|
|
public const RT_SUMMARY = 0x0402;
|
|
public const RT_TEXTBOOKMARKATOM = 0x0FA7;
|
|
public const RT_TEXTBYTESATOM = 0x0FA8;
|
|
public const RT_TEXTCHARFORMATEXCEPTIONATOM = 0x0FA4;
|
|
public const RT_TEXTCHARSATOM = 0x0FA0;
|
|
public const RT_TEXTDEFAULTS10ATOM = 0x0FB4;
|
|
public const RT_TEXTDEFAULTS9ATOM = 0x0FB0;
|
|
public const RT_TEXTHEADERATOM = 0x0F9F;
|
|
public const RT_TEXTINTERACTIVEINFOATOM = 0x0FDF;
|
|
public const RT_TEXTMASTERSTYLEATOM = 0x0FA3;
|
|
public const RT_TEXTMASTERSTYLE10ATOM = 0x0FB2;
|
|
public const RT_TEXTMASTERSTYLE9ATOM = 0x0FAD;
|
|
public const RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM = 0x0FA5;
|
|
public const RT_TEXTRULERATOM = 0x0FA6;
|
|
public const RT_TEXTSPECIALINFOATOM = 0x0FAA;
|
|
public const RT_TEXTSPECIALINFODEFAULTATOM = 0x0FA9;
|
|
public const RT_TIMEANIMATEBEHAVIOR = 0xF134;
|
|
public const RT_TIMEANIMATEBEHAVIORCONTAINER = 0xF12B;
|
|
public const RT_TIMEANIMATIONVALUE = 0xF143;
|
|
public const RT_TIMEANIMATIONVALUELIST = 0xF13F;
|
|
public const RT_TIMEBEHAVIOR = 0xF133;
|
|
public const RT_TIMEBEHAVIORCONTAINER = 0xF12A;
|
|
public const RT_TIMECOLORBEHAVIOR = 0xF135;
|
|
public const RT_TIMECOLORBEHAVIORCONTAINER = 0xF12C;
|
|
public const RT_TIMECLIENTVISUALELEMENT = 0xF13C;
|
|
public const RT_TIMECOMMANDBEHAVIOR = 0xF13B;
|
|
public const RT_TIMECOMMANDBEHAVIORCONTAINER = 0xF132;
|
|
public const RT_TIMECONDITION = 0xF128;
|
|
public const RT_TIMECONDITIONCONTAINER = 0xF125;
|
|
public const RT_TIMEEFFECTBEHAVIOR = 0xF136;
|
|
public const RT_TIMEEFFECTBEHAVIORCONTAINER = 0xF12D;
|
|
public const RT_TIMEEXTTIMENODECONTAINER = 0xF144;
|
|
public const RT_TIMEITERATEDATA = 0xF140;
|
|
public const RT_TIMEMODIFIER = 0xF129;
|
|
public const RT_TIMEMOTIONBEHAVIOR = 0xF137;
|
|
public const RT_TIMEMOTIONBEHAVIORCONTAINER = 0xF12E;
|
|
public const RT_TIMENODE = 0xF127;
|
|
public const RT_TIMEPROPERTYLIST = 0xF13D;
|
|
public const RT_TIMEROTATIONBEHAVIOR = 0xF138;
|
|
public const RT_TIMEROTATIONBEHAVIORCONTAINER = 0xF12F;
|
|
public const RT_TIMESCALEBEHAVIOR = 0xF139;
|
|
public const RT_TIMESCALEBEHAVIORCONTAINER = 0xF130;
|
|
public const RT_TIMESEQUENCEDATA = 0xF141;
|
|
public const RT_TIMESETBEHAVIOR = 0xF13A;
|
|
public const RT_TIMESETBEHAVIORCONTAINER = 0xF131;
|
|
public const RT_TIMESUBEFFECTCONTAINER = 0xF145;
|
|
public const RT_TIMEVARIANT = 0xF142;
|
|
public const RT_TIMEVARIANTLIST = 0xF13E;
|
|
public const RT_USEREDITATOM = 0x0FF5;
|
|
public const RT_VBAINFO = 0x03FF;
|
|
public const RT_VBAINFOATOM = 0x0400;
|
|
public const RT_VIEWINFOATOM = 0x03FD;
|
|
public const RT_VISUALPAGEATOM = 0x2B01;
|
|
public const RT_VISUALSHAPEATOM = 0x2AFB;
|
|
|
|
/**
|
|
* @see http://msdn.microsoft.com/en-us/library/dd926394(v=office.12).aspx
|
|
*/
|
|
public const SL_BIGOBJECT = 0x0000000F;
|
|
public const SL_BLANK = 0x00000010;
|
|
public const SL_COLUMNTWOROWS = 0x0000000A;
|
|
public const SL_FOUROBJECTS = 0x0000000E;
|
|
public const SL_MASTERTITLE = 0x00000002;
|
|
public const SL_TITLEBODY = 0x00000001;
|
|
public const SL_TITLEONLY = 0x00000007;
|
|
public const SL_TITLESLIDE = 0x00000000;
|
|
public const SL_TWOCOLUMNS = 0x00000008;
|
|
public const SL_TWOCOLUMNSROW = 0x0000000D;
|
|
public const SL_TWOROWS = 0x00000009;
|
|
public const SL_TWOROWSCOLUMN = 0x0000000B;
|
|
public const SL_VERTICALTITLEBODY = 0x00000011;
|
|
public const SL_VERTICALTWOROWS = 0x00000012;
|
|
|
|
/**
|
|
* Array with Fonts.
|
|
*
|
|
* @var array<int, string>
|
|
*/
|
|
private $arrayFonts = [];
|
|
/**
|
|
* Array with Hyperlinks.
|
|
*
|
|
* @var array<int, array<string, string>>
|
|
*/
|
|
private $arrayHyperlinks = [];
|
|
/**
|
|
* Array with Notes.
|
|
*
|
|
* @var array<int, int>
|
|
*/
|
|
private $arrayNotes = [];
|
|
/**
|
|
* Array with Pictures.
|
|
*
|
|
* @var array<int, string>
|
|
*/
|
|
private $arrayPictures = [];
|
|
/**
|
|
* 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 array<int, 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;
|
|
/**
|
|
* @var Group|null
|
|
*/
|
|
private $oCurrentGroup;
|
|
/**
|
|
* @var bool
|
|
*/
|
|
private $bFirstShapeGroup = false;
|
|
/**
|
|
* Stream "Powerpoint Document".
|
|
*
|
|
* @var string
|
|
*/
|
|
private $streamPowerpointDocument;
|
|
/**
|
|
* Stream "Current User".
|
|
*
|
|
* @var string
|
|
*/
|
|
private $streamCurrentUser;
|
|
/**
|
|
* Stream "Pictures".
|
|
*
|
|
* @var string
|
|
*/
|
|
private $streamPictures;
|
|
/**
|
|
* @var int
|
|
*/
|
|
private $inMainType;
|
|
/**
|
|
* @var int|null
|
|
*/
|
|
private $currentNote;
|
|
|
|
/**
|
|
* @var string|null
|
|
*/
|
|
private $filename;
|
|
|
|
/**
|
|
* Can the current \PhpOffice\PhpPresentation\Reader\ReaderInterface read the file?
|
|
*/
|
|
public function canRead(string $pFilename): bool
|
|
{
|
|
return $this->fileSupportsUnserializePhpPresentation($pFilename);
|
|
}
|
|
|
|
/**
|
|
* Does a file support UnserializePhpPresentation ?
|
|
*
|
|
* @throws FileNotFoundException
|
|
*/
|
|
public function fileSupportsUnserializePhpPresentation(string $pFilename = ''): bool
|
|
{
|
|
// Check if file exists
|
|
if (!\file_exists($pFilename)) {
|
|
throw new FileNotFoundException($pFilename);
|
|
}
|
|
|
|
/*
|
|
try {
|
|
// Use ParseXL for the hard work.
|
|
$ole = new OLERead();
|
|
// get excel data
|
|
$ole->read($pFilename);
|
|
|
|
return true;
|
|
} catch (\Exception $e) {
|
|
return false;
|
|
}
|
|
*/
|
|
|
|
$ole = new OLERead();
|
|
|
|
return $ole->read($pFilename);
|
|
}
|
|
|
|
/**
|
|
* Loads PhpPresentation Serialized file.
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*/
|
|
public function load(string $pFilename): PhpPresentation
|
|
{
|
|
// Unserialize... First make sure the file supports it!
|
|
if (!$this->fileSupportsUnserializePhpPresentation($pFilename)) {
|
|
throw new InvalidFileFormatException($pFilename, PowerPoint97::class);
|
|
}
|
|
|
|
$this->filename = $pFilename;
|
|
|
|
return $this->loadFile();
|
|
}
|
|
|
|
/**
|
|
* Load PhpPresentation Serialized file
|
|
*/
|
|
private function loadFile(): PhpPresentation
|
|
{
|
|
$this->oPhpPresentation = new PhpPresentation();
|
|
$this->oPhpPresentation->removeSlideByIndex();
|
|
|
|
// Read OLE Blocks
|
|
$this->loadOLE();
|
|
// 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
|
|
*/
|
|
private function loadOLE(): void
|
|
{
|
|
// OLE reader
|
|
$oOLE = new OLERead();
|
|
$oOLE->read($this->filename);
|
|
|
|
// PowerPoint Document Stream
|
|
$this->streamPowerpointDocument = $oOLE->getStream($oOLE->powerpointDocument);
|
|
|
|
// Current User Stream
|
|
$this->streamCurrentUser = $oOLE->getStream($oOLE->currentUser);
|
|
|
|
// Get pictures data
|
|
$this->streamPictures = $oOLE->getStream($oOLE->pictures);
|
|
}
|
|
|
|
/**
|
|
* Stream Pictures.
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd920746(v=office.12).aspx
|
|
*/
|
|
private function loadPicturesStream(): void
|
|
{
|
|
$stream = $this->streamPictures;
|
|
|
|
$pos = 0;
|
|
do {
|
|
$arrayRH = $this->loadRecordHeader($stream, $pos);
|
|
$pos += 8;
|
|
$readSuccess = false;
|
|
if (0x00 == $arrayRH['recVer'] && (0xF007 == $arrayRH['recType'] || ($arrayRH['recType'] >= 0xF018 && $arrayRH['recType'] <= 0xF117))) {
|
|
//@link : http://msdn.microsoft.com/en-us/library/dd950560(v=office.12).aspx
|
|
if (0xF007 == $arrayRH['recType']) {
|
|
// OfficeArtFBSE
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
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 (true === $readSuccess);
|
|
}
|
|
|
|
/**
|
|
* Stream Current User.
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
* @throws InvalidFileFormatException
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd908567(v=office.12).aspx
|
|
*/
|
|
private function loadCurrentUserStream(): void
|
|
{
|
|
$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 (0x0 != $rHeader['recVer'] || 0x000 != $rHeader['recInstance'] || self::RT_CURRENTUSERATOM != $rHeader['recType']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > RecordHeader');
|
|
}
|
|
|
|
// Size
|
|
$size = self::getInt4d($this->streamCurrentUser, $pos);
|
|
$pos += 4;
|
|
if (0x00000014 != $size) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > Size');
|
|
}
|
|
|
|
// headerToken
|
|
$headerToken = self::getInt4d($this->streamCurrentUser, $pos);
|
|
$pos += 4;
|
|
if (0xF3D1C4DF == $headerToken && 0xE391C05F != $headerToken) {
|
|
// Encrypted file
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
|
|
// offsetToCurrentEdit
|
|
$this->offsetToCurrentEdit = self::getInt4d($this->streamCurrentUser, $pos);
|
|
$pos += 4;
|
|
|
|
// lenUserName
|
|
$lenUserName = self::getInt2d($this->streamCurrentUser, $pos);
|
|
$pos += 2;
|
|
if ($lenUserName > 255) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > lenUserName');
|
|
}
|
|
|
|
// docFileVersion
|
|
$docFileVersion = self::getInt2d($this->streamCurrentUser, $pos);
|
|
$pos += 2;
|
|
if (0x03F4 != $docFileVersion) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > docFileVersion');
|
|
}
|
|
|
|
// majorVersion
|
|
$majorVersion = self::getInt1d($this->streamCurrentUser, $pos);
|
|
++$pos;
|
|
if (0x03 != $majorVersion) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : CurrentUserAtom > majorVersion');
|
|
}
|
|
|
|
// minorVersion
|
|
$minorVersion = self::getInt1d($this->streamCurrentUser, $pos);
|
|
++$pos;
|
|
if (0x00 != $minorVersion) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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;
|
|
}
|
|
} while (false !== $char);
|
|
|
|
// relVersion
|
|
$relVersion = self::getInt4d($this->streamCurrentUser, $pos);
|
|
$pos += 4;
|
|
if (0x00000008 != $relVersion && 0x00000009 != $relVersion) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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.
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd921564(v=office.12).aspx
|
|
*/
|
|
private function loadPowerpointDocumentStream(): void
|
|
{
|
|
$this->readRecordUserEditAtom($this->streamPowerpointDocument, $this->offsetToCurrentEdit);
|
|
|
|
$this->readRecordPersistDirectoryAtom($this->streamPowerpointDocument, $this->offsetPersistDirectory);
|
|
|
|
foreach ($this->rgPersistDirEntry as $offsetDir) {
|
|
$pos = $offsetDir;
|
|
|
|
$rHeader = $this->loadRecordHeader($this->streamPowerpointDocument, $pos);
|
|
$pos += 8;
|
|
$this->inMainType = $rHeader['recType'];
|
|
$this->currentNote = null;
|
|
switch ($rHeader['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:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read a record header.
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function loadRecordHeader(string $stream, int $pos): array
|
|
{
|
|
$rec = self::getInt2d($stream, $pos);
|
|
$recType = self::getInt2d($stream, $pos + 2);
|
|
$recLen = self::getInt4d($stream, $pos + 4);
|
|
|
|
return [
|
|
'recVer' => ($rec >> 0) & \bindec('1111'),
|
|
'recInstance' => ($rec >> 4) & \bindec('111111111111'),
|
|
'recType' => $recType,
|
|
'recLen' => $recLen,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Read 8-bit unsigned integer.
|
|
*/
|
|
public static function getInt1d(string $data, int $pos): int
|
|
{
|
|
return \ord($data[$pos]);
|
|
}
|
|
|
|
/**
|
|
* Read 16-bit unsigned integer.
|
|
*/
|
|
public static function getInt2d(string $data, int $pos): int
|
|
{
|
|
return \ord($data[$pos]) | (\ord($data[$pos + 1]) << 8);
|
|
}
|
|
|
|
/**
|
|
* Read 32-bit signed integer.
|
|
*/
|
|
public static function getInt4d(string $data, int $pos): int
|
|
{
|
|
// 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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd772900(v=office.12).aspx
|
|
*/
|
|
private function readRecordAnimationInfoContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ANIMATIONINFO == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// animationAtom
|
|
// animationSound
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies information about the document.
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
* @throws InvalidFileFormatException
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd947357(v=office.12).aspx
|
|
*/
|
|
private function readRecordDocumentContainer(string $stream, int $pos): void
|
|
{
|
|
$documentAtom = $this->loadRecordHeader($stream, $pos);
|
|
$pos += 8;
|
|
if (0x1 != $documentAtom['recVer'] || 0x000 != $documentAtom['recInstance'] || self::RT_DOCUMENTATOM != $documentAtom['recType']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : RTDocument > DocumentAtom');
|
|
}
|
|
$pos += $documentAtom['recLen'];
|
|
|
|
$exObjList = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $exObjList['recVer'] && 0x000 == $exObjList['recInstance'] && self::RT_EXTERNALOBJECTLIST == $exObjList['recType']) {
|
|
$pos += 8;
|
|
// exObjListAtom > rh
|
|
$exObjListAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 != $exObjListAtom['recVer'] || 0x000 != $exObjListAtom['recInstance'] || self::RT_EXTERNALOBJECTLISTATOM != $exObjListAtom['recType'] || 0x00000004 != $exObjListAtom['recLen']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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 (0x0 != $exHyperlinkAtom['recVer'] || 0x000 != $exHyperlinkAtom['recInstance'] || self::RT_EXTERNALHYPERLINKATOM != $exHyperlinkAtom['recType'] || 0x00000004 != $exObjListAtom['recLen']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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] = [];
|
|
// friendlyNameAtom
|
|
$friendlyNameAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $friendlyNameAtom['recVer'] && 0x000 == $friendlyNameAtom['recInstance'] && self::RT_CSTRING == $friendlyNameAtom['recType'] && $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 (0x0 == $targetAtom['recVer'] && 0x001 == $targetAtom['recInstance'] && self::RT_CSTRING == $targetAtom['recType'] && $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 (0x0 == $locationAtom['recVer'] && 0x003 == $locationAtom['recInstance'] && self::RT_CSTRING == $locationAtom['recType'] && $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:
|
|
// var_dump(dechex((int) $childRec['recType']));
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
} while ($exObjList['recLen'] > 0);
|
|
}
|
|
|
|
//@link : http://msdn.microsoft.com/en-us/library/dd907813(v=office.12).aspx
|
|
$documentTextInfo = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $documentTextInfo['recVer'] && 0x000 == $documentTextInfo['recInstance'] && self::RT_ENVIRONMENT == $documentTextInfo['recType']) {
|
|
$pos += 8;
|
|
//@link : http://msdn.microsoft.com/en-us/library/dd952717(v=office.12).aspx
|
|
$kinsoku = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $kinsoku['recVer'] && 0x002 == $kinsoku['recInstance'] && self::RT_KINSOKU == $kinsoku['recType']) {
|
|
$pos += 8;
|
|
$pos += $kinsoku['recLen'];
|
|
}
|
|
|
|
//@link : http://msdn.microsoft.com/en-us/library/dd948152(v=office.12).aspx
|
|
$fontCollection = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $fontCollection['recVer'] && 0x000 == $fontCollection['recInstance'] && self::RT_FONTCOLLECTION == $fontCollection['recType']) {
|
|
$pos += 8;
|
|
do {
|
|
$fontEntityAtom = $this->loadRecordHeader($stream, $pos);
|
|
$pos += 8;
|
|
$fontCollection['recLen'] -= 8;
|
|
if (0x0 != $fontEntityAtom['recVer'] || $fontEntityAtom['recInstance'] > 128 || self::RT_FONTENTITYATOM != $fontEntityAtom['recType']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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;
|
|
--$fontCollection['recLen'];
|
|
|
|
// fEmbedSubsetted (1 bit)
|
|
// unused (7 bits)
|
|
++$pos;
|
|
--$fontCollection['recLen'];
|
|
|
|
// rasterFontType (1 bit)
|
|
// deviceFontType (1 bit)
|
|
// truetypeFontType (1 bit)
|
|
// fNoFontSubstitution (1 bit)
|
|
// reserved (4 bits)
|
|
++$pos;
|
|
--$fontCollection['recLen'];
|
|
|
|
// lfPitchAndFamily (1 byte)
|
|
++$pos;
|
|
--$fontCollection['recLen'];
|
|
|
|
$fontEmbedData1 = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $fontEmbedData1['recVer'] && $fontEmbedData1['recInstance'] >= 0x000 && $fontEmbedData1['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData1['recType']) {
|
|
$pos += 8;
|
|
$fontCollection['recLen'] -= 8;
|
|
$pos += $fontEmbedData1['recLen'];
|
|
$fontCollection['recLen'] -= $fontEmbedData1['recLen'];
|
|
}
|
|
|
|
$fontEmbedData2 = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $fontEmbedData2['recVer'] && $fontEmbedData2['recInstance'] >= 0x000 && $fontEmbedData2['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData2['recType']) {
|
|
$pos += 8;
|
|
$fontCollection['recLen'] -= 8;
|
|
$pos += $fontEmbedData2['recLen'];
|
|
$fontCollection['recLen'] -= $fontEmbedData2['recLen'];
|
|
}
|
|
|
|
$fontEmbedData3 = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $fontEmbedData3['recVer'] && $fontEmbedData3['recInstance'] >= 0x000 && $fontEmbedData3['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData3['recType']) {
|
|
$pos += 8;
|
|
$fontCollection['recLen'] -= 8;
|
|
$pos += $fontEmbedData3['recLen'];
|
|
$fontCollection['recLen'] -= $fontEmbedData3['recLen'];
|
|
}
|
|
|
|
$fontEmbedData4 = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $fontEmbedData4['recVer'] && $fontEmbedData4['recInstance'] >= 0x000 && $fontEmbedData4['recInstance'] <= 0x003 && self::RT_FONTEMBEDDATABLOB == $fontEmbedData4['recType']) {
|
|
$pos += 8;
|
|
$fontCollection['recLen'] -= 8;
|
|
$pos += $fontEmbedData4['recLen'];
|
|
$fontCollection['recLen'] -= $fontEmbedData4['recLen'];
|
|
}
|
|
} while ($fontCollection['recLen'] > 0);
|
|
}
|
|
|
|
$textCFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $textCFDefaultsAtom['recVer'] && 0x000 == $textCFDefaultsAtom['recInstance'] && self::RT_TEXTCHARFORMATEXCEPTIONATOM == $textCFDefaultsAtom['recType']) {
|
|
$pos += 8;
|
|
$pos += $textCFDefaultsAtom['recLen'];
|
|
}
|
|
|
|
$textPFDefaultsAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $textPFDefaultsAtom['recVer'] && 0x000 == $textPFDefaultsAtom['recInstance'] && self::RT_TEXTPARAGRAPHFORMATEXCEPTIONATOM == $textPFDefaultsAtom['recType']) {
|
|
$pos += 8;
|
|
$pos += $textPFDefaultsAtom['recLen'];
|
|
}
|
|
|
|
$defaultRulerAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $defaultRulerAtom['recVer'] && 0x000 == $defaultRulerAtom['recInstance'] && self::RT_DEFAULTRULERATOM == $defaultRulerAtom['recType']) {
|
|
$pos += 8;
|
|
$pos += $defaultRulerAtom['recLen'];
|
|
}
|
|
|
|
$textSIDefaultsAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $textSIDefaultsAtom['recVer'] && 0x000 == $textSIDefaultsAtom['recInstance'] && self::RT_TEXTSPECIALINFODEFAULTATOM == $textSIDefaultsAtom['recType']) {
|
|
$pos += 8;
|
|
$pos += $textSIDefaultsAtom['recLen'];
|
|
}
|
|
|
|
$textMasterStyleAtom = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $textMasterStyleAtom['recVer'] && self::RT_TEXTMASTERSTYLEATOM == $textMasterStyleAtom['recType']) {
|
|
$pos += 8;
|
|
$pos += $textMasterStyleAtom['recLen'];
|
|
}
|
|
}
|
|
|
|
$soundCollection = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $soundCollection['recVer'] && 0x005 == $soundCollection['recInstance'] && self::RT_SOUNDCOLLECTION == $soundCollection['recType']) {
|
|
$pos += 8;
|
|
$pos += $soundCollection['recLen'];
|
|
}
|
|
|
|
$drawingGroup = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $drawingGroup['recVer'] && 0x000 == $drawingGroup['recInstance'] && self::RT_DRAWINGGROUP == $drawingGroup['recType']) {
|
|
$drawing = $this->readRecordDrawingGroupContainer($stream, $pos);
|
|
$pos += 8;
|
|
$pos += $drawing['length'];
|
|
}
|
|
|
|
$masterList = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $masterList['recVer'] && 0x001 == $masterList['recInstance'] && self::RT_SLIDELISTWITHTEXT == $masterList['recType']) {
|
|
$pos += 8;
|
|
$pos += $masterList['recLen'];
|
|
}
|
|
|
|
$docInfoList = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $docInfoList['recVer'] && 0x000 == $docInfoList['recInstance'] && self::RT_LIST == $docInfoList['recType']) {
|
|
$pos += 8;
|
|
$pos += $docInfoList['recLen'];
|
|
}
|
|
|
|
$slideHF = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $slideHF['recVer'] && 0x003 == $slideHF['recInstance'] && self::RT_HEADERSFOOTERS == $slideHF['recType']) {
|
|
$pos += 8;
|
|
$pos += $slideHF['recLen'];
|
|
}
|
|
|
|
$notesHF = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $notesHF['recVer'] && 0x004 == $notesHF['recInstance'] && self::RT_HEADERSFOOTERS == $notesHF['recType']) {
|
|
$pos += 8;
|
|
$pos += $notesHF['recLen'];
|
|
}
|
|
|
|
// SlideListWithTextContainer
|
|
$slideList = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $slideList['recVer'] && 0x000 == $slideList['recInstance'] && self::RT_SLIDELISTWITHTEXT == $slideList['recType']) {
|
|
$pos += 8;
|
|
do {
|
|
// SlideListWithTextSubContainerOrAtom
|
|
$rhSlideList = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $rhSlideList['recVer'] && 0x000 == $rhSlideList['recInstance'] && self::RT_SLIDEPERSISTATOM == $rhSlideList['recType'] && 0x00000014 == $rhSlideList['recLen']) {
|
|
$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 (-2147483648 == $slideId) {
|
|
$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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
|
|
*/
|
|
private function readRecordDrawingContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_DRAWING == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
|
|
$officeArtDg = $this->readRecordOfficeArtDgContainer($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += $officeArtDg['length'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordDrawingGroupContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_DRAWINGGROUP == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies a reference to an external object.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd910388(v=office.12).aspx
|
|
*/
|
|
private function readRecordExObjRefAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_EXTERNALOBJECTREFATOM == $data['recType'] && 0x00000004 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies a type of action to be performed.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd953300(v=office.12).aspx
|
|
*/
|
|
private function readRecordInteractiveInfoAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_INTERACTIVEINFOATOM == $data['recType'] && 0x00000010 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// soundIdRef
|
|
$arrayReturn['length'] += 4;
|
|
// exHyperlinkIdRef
|
|
$arrayReturn['exHyperlinkIdRef'] = self::getInt4d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 4;
|
|
// action
|
|
++$arrayReturn['length'];
|
|
// oleVerb
|
|
++$arrayReturn['length'];
|
|
// jump
|
|
++$arrayReturn['length'];
|
|
// fAnimated (1 bit)
|
|
// fStopSound (1 bit)
|
|
// fCustomShowReturn (1 bit)
|
|
// fVisited (1 bit)
|
|
// reserved (4 bits)
|
|
++$arrayReturn['length'];
|
|
// hyperlinkType
|
|
++$arrayReturn['length'];
|
|
// unused
|
|
$arrayReturn['length'] += 3;
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies the name of a macro, a file name, or a named show.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd925121(v=office.12).aspx
|
|
*/
|
|
private function readRecordMacroNameAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x002 == $data['recInstance'] && self::RT_CSTRING == $data['recType'] && $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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd952348(v=office.12).aspx
|
|
*/
|
|
private function readRecordMouseClickInteractiveInfoContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_INTERACTIVEINFO == $data['recType']) {
|
|
// 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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd925811(v=office.12).aspx
|
|
*/
|
|
private function readRecordMouseOverInteractiveInfoContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x001 == $data['recInstance'] && self::RT_INTERACTIVEINFO == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// interactiveInfoAtom
|
|
// macroNameAtom
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* The OfficeArtBlip record specifies BLIP file data.
|
|
*
|
|
* @return array{'length': int, 'picture': null|string}
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd910081(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtBlip(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
'picture' => null,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && ($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 (0x6E1 == $data['recInstance']) {
|
|
// rgbUid2
|
|
$arrayReturn['length'] += 16;
|
|
$data['recLen'] -= 16;
|
|
}
|
|
// tag
|
|
++$arrayReturn['length'];
|
|
--$data['recLen'];
|
|
// BLIPFileData
|
|
$arrayReturn['picture'] = \substr($this->streamPictures, $pos + $arrayReturn['length'], $data['recLen']);
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
break;
|
|
default:
|
|
// var_dump(dechex((int) $data['recType']))
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* The OfficeArtChildAnchor record specifies four signed integers that specify the anchor for the shape that contains this record.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd922720(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtChildAnchor(string $stream, int $pos)
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF00F == $data['recType'] && 0x00000010 == $data['recLen']) {
|
|
// 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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd922797(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtClientAnchor(string $stream, int $pos)
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF010 == $data['recType'] && (0x00000008 == $data['recLen'] || 0x00000010 == $data['recLen'])) {
|
|
// 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:
|
|
// record OfficeArtClientAnchor (0x00000010)
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies text related data for a shape.
|
|
*
|
|
* @return array{'length': int, 'alignH': string|null, 'text': string, 'numParts': int, 'numTexts': int, 'hyperlink': array<int, array<string, int>>, 'part': array{'length': int, 'strLenRT': int, 'partLength': int|float, 'bold': bool, 'italic': bool, 'underline': bool, 'fontName': string, 'fontSize': int, 'color': Color}}
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd910958(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtClientTextbox(string $stream, int $pos)
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
'text' => '',
|
|
'numParts' => 0,
|
|
'numTexts' => 0,
|
|
'hyperlink' => [],
|
|
];
|
|
|
|
$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 (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF00D == $data['recType']) {
|
|
// 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 (0x0000 == $rhChild['recInstance']) {
|
|
$mouseClickInfo = $this->readRecordMouseClickInteractiveInfoContainer($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += $mouseClickInfo['length'];
|
|
$arrayReturn['hyperlink'][]['id'] = $mouseClickInfo['exHyperlinkIdRef'];
|
|
}
|
|
if (0x0001 == $rhChild['recInstance']) {
|
|
$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 (0x0B == $char) {
|
|
$char = 0x20;
|
|
}
|
|
$arrayReturn['text'] .= Text::chr($char);
|
|
++$arrayReturn['length'];
|
|
}
|
|
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 (0x0B == $char) {
|
|
$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 (0x0000 == $rhChild['recInstance']) {
|
|
//@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 (0x0001 == $rhChild['recInstance']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
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'];
|
|
}
|
|
} while (($data['recLen'] - $arrayReturn['length']) > 0);
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* The OfficeArtSpContainer record specifies a shape container.
|
|
*
|
|
* @return array{'length': int, 'shape': null|AbstractShape}
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd943794(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtSpContainer(string $stream, int $pos)
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
'shape' => null,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF004 == $data['recType']) {
|
|
// 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 (0 == $shapeProp['length']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
|
|
}
|
|
$arrayReturn['length'] += $shapeProp['length'];
|
|
|
|
if (0x1 == $shapeProp['fDeleted'] && 0x0 == $shapeProp['fChild']) {
|
|
// 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 (0 == $shpSecondaryOptions1['length']) {
|
|
$shpSecondaryOptions2 = $this->readRecordOfficeArtSecondaryFOPT($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += $shpSecondaryOptions2['length'];
|
|
}
|
|
|
|
// shapeTertiaryOptions2
|
|
if (0 == $shpTertiaryOptions1['length']) {
|
|
$shpTertiaryOptions2 = $this->readRecordOfficeArtTertiaryFOPT($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += $shpTertiaryOptions2['length'];
|
|
}
|
|
|
|
// Core : Shape
|
|
// Informations about group are not defined
|
|
$arrayDimensions = [];
|
|
$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;
|
|
/* @phpstan-ignore-next-line */
|
|
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 (!empty($clientTextbox['hyperlink'])) {
|
|
foreach ($clientTextbox['hyperlink'] as $itmHyperlink) {
|
|
if ($itmHyperlink['start'] == $start && ($itmHyperlink['end'] - $itmHyperlink['start']) == (float) $clientTextbox['part' . $inc]['partLength']) {
|
|
$sText = $this->arrayHyperlinks[$itmHyperlink['id']]['text'];
|
|
$sHyperlinkURL = $this->arrayHyperlinks[$itmHyperlink['id']]['url'];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// New paragraph
|
|
$bCreateParagraph = false;
|
|
if (false !== \strpos($sText, "\r")) {
|
|
$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'] ? Font::UNDERLINE_SINGLE : Font::UNDERLINE_NONE
|
|
);
|
|
}
|
|
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'], $shpPrimaryOptions['shadowOffsetY'])) {
|
|
$shadowOffsetX = $shpPrimaryOptions['shadowOffsetX'];
|
|
$shadowOffsetY = $shpPrimaryOptions['shadowOffsetY'];
|
|
if (0 != $shadowOffsetX && 0 != $shadowOffsetX) {
|
|
$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 int $pos
|
|
* @param bool $bInGroup
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*
|
|
* @see : https://msdn.microsoft.com/en-us/library/dd910416(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtSpgrContainer($stream, $pos, $bInGroup = false)
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF003 == $data['recType']) {
|
|
$arrayReturn['length'] += 8;
|
|
|
|
do {
|
|
$rhFileBlock = $this->loadRecordHeader($stream, $pos + $arrayReturn['length']);
|
|
if (!(0xF == $rhFileBlock['recVer'] && 0x0000 == $rhFileBlock['recInstance'] && (0xF003 == $rhFileBlock['recType'] || 0xF004 == $rhFileBlock['recType']))) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
|
|
}
|
|
|
|
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 (0 == $oSlide->getNote()->getShapeCollection()->count()) {
|
|
$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,.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd950206(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtTertiaryFOPT(string $stream, int $pos)
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x3 == $data['recVer'] && 0xF122 == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
|
|
$officeArtFOPTE = [];
|
|
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[] = [
|
|
'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 (0x1 == $opt['fComplex']) {
|
|
$arrayReturn['length'] += $opt['op'];
|
|
}
|
|
break;
|
|
case 0x03A9:
|
|
// GroupShape : metroBlob
|
|
//@link : https://msdn.microsoft.com/en-us/library/dd943388(v=office.12).aspx
|
|
if (0x1 == $opt['fComplex']) {
|
|
$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:
|
|
// var_dump('0x' . dechex($opt['opid']));
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
}
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* The OfficeArtDgContainer record specifies the container for all the file records for the objects in a drawing.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see : https://msdn.microsoft.com/en-us/library/dd924455(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtDgContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF002 == $data['recType']) {
|
|
// 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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see : https://msdn.microsoft.com/en-us/library/dd946757(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtFDG(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && $data['recInstance'] <= 0xFFE && 0xF008 == $data['recType'] && 0x00000008 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* The OfficeArtFOPT record specifies a table of OfficeArtRGFOPTE records.
|
|
*
|
|
* @return array<string, int|string|bool>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd943404(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtFOPT(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x3 == $data['recVer'] && 0xF00B == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
|
|
//@link : http://msdn.microsoft.com/en-us/library/dd906086(v=office.12).aspx
|
|
$officeArtFOPTE = [];
|
|
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[] = [
|
|
'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((int) $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((int) $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((int) $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((int) $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 (0 == $opt['fComplex']) {
|
|
$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 (1 == $opt['fComplex']) {
|
|
$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 (1 == $opt['fComplex']) {
|
|
$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 (1 == $opt['fComplex']) {
|
|
$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 (1 == $opt['fComplex']) {
|
|
$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 (1 == $opt['fComplex']) {
|
|
$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, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex(($opt['op'] >> 8) & \bindec('11111111')), 2, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex(($opt['op'] >> 16) & \bindec('11111111')), 2, '0', STR_PAD_LEFT);
|
|
// 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, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex(($opt['op'] >> 8) & \bindec('11111111')), 2, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex(($opt['op'] >> 16) & \bindec('11111111')), 2, '0', STR_PAD_LEFT);
|
|
// 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((int) $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((int) $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, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex(($opt['op'] >> 8) & \bindec('11111111')), 2, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex(($opt['op'] >> 16) & \bindec('11111111')), 2, '0', STR_PAD_LEFT);
|
|
$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((int) $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((int) $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((int) $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 (1 == $opt['fComplex']) {
|
|
$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:
|
|
}
|
|
}
|
|
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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd947479(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtFPSPL(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF11D == $data['recType'] && 0x00000004 == $data['recLen']) {
|
|
$arrayReturn['length'] += 8;
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* The OfficeArtFSP record specifies an instance of a shape.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd925898(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtFSP(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x2 == $data['recVer'] && 0xF00A == $data['recType'] && 0x00000008 == $data['recLen']) {
|
|
$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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd925381(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtFSPGR(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x1 == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF009 == $data['recType'] && 0x00000010 == $data['recLen']) {
|
|
$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.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd950259(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtSecondaryFOPT(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x3 == $data['recVer'] && 0xF121 == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies information about a shape.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see : https://msdn.microsoft.com/en-us/library/dd950927(v=office.12).aspx
|
|
*/
|
|
private function readRecordOfficeArtClientData(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && 0xF011 == $data['recType']) {
|
|
$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 = [
|
|
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:
|
|
// var_dump('0x' . dechex($dataHeaderRG['recType']));
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
}
|
|
} 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.
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd952680(v=office.12).aspx
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*/
|
|
private function readRecordPersistDirectoryAtom(string $stream, int $pos): void
|
|
{
|
|
$rHeader = $this->loadRecordHeader($stream, $pos);
|
|
$pos += 8;
|
|
if (0x0 != $rHeader['recVer'] || 0x000 != $rHeader['recInstance'] || self::RT_PERSISTDIRECTORYATOM != $rHeader['recType']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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 = [];
|
|
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.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd904856(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordPerSlideHeadersFootersContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_HEADERSFOOTERS == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies whether a shape is a placeholder shape.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd923930(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordPlaceholderAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PLACEHOLDERATOM == $data['recType'] && 0x00000008 == $data['recLen']) {
|
|
// 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]).
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd904899(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordRecolorInfoAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_RECOLORINFOATOM == $data['recType']) {
|
|
// 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.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd910800(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordRoundTripHFPlaceholder12Atom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ROUNDTRIPHFPLACEHOLDER12ATOM == $data['recType'] && 0x00000001 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies a shape identifier.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd772926(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordRoundTripShapeId12Atom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ROUNDTRIPSHAPEID12ATOM == $data['recType'] && 0x00000004 == $data['recLen']) {
|
|
// 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.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordRoundTripSlideSyncInfo12Container(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_ROUNDTRIPSLIDESYNCINFO12 == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies shape-level Boolean flags.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd908949(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordShapeFlags10Atom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SHAPEFLAGS10ATOM == $data['recType'] && 0x00000001 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies shape-level Boolean flags.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd925824(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordShapeFlagsAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SHAPEATOM == $data['recType'] && 0x00000001 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies programmable tags with additional binary shape data.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd911033(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordShapeProgBinaryTagContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PROGBINARYTAG == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies programmable tags with additional shape data.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd911266(v=office.12).aspx
|
|
*/
|
|
private function readRecordShapeProgTagsContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PROGTAGS == $data['recType']) {
|
|
// 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:
|
|
// var_dump('0x' . dechex($dataHeaderRG['recType']));
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
} while ($length < $data['recLen']);
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* An atom record that specifies information about a slide.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd923801(v=office.12).aspx
|
|
*/
|
|
private function readRecordSlideAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x2 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SLIDEATOM == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// slideAtom > geom
|
|
$arrayReturn['length'] += 4;
|
|
// slideAtom > rgPlaceholderTypes
|
|
$rgPlaceholderTypes = [];
|
|
for ($inc = 0; $inc < 8; ++$inc) {
|
|
$rgPlaceholderTypes[] = self::getInt1d($this->streamPowerpointDocument, $pos);
|
|
++$arrayReturn['length'];
|
|
}
|
|
|
|
// 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.
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd946323(v=office.12).aspx
|
|
*/
|
|
private function readRecordSlideContainer(string $stream, int $pos): void
|
|
{
|
|
// Core
|
|
$this->oPhpPresentation->createSlide();
|
|
$this->oPhpPresentation->setActiveSlideIndex($this->oPhpPresentation->getSlideCount() - 1);
|
|
|
|
// *** slideAtom (32 bytes)
|
|
$slideAtom = $this->readRecordSlideAtom($stream, $pos);
|
|
if (0 == $slideAtom['length']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
|
|
}
|
|
$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 (0 == $slideSchemeColorAtom['length']) {
|
|
// Record SlideSchemeColorSchemeAtom
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class);
|
|
}
|
|
$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.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd906297(v=office.12).aspx
|
|
*
|
|
* @return array{'length': int, 'slideName': string}
|
|
*/
|
|
private function readRecordSlideNameAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
'slideName' => '',
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x003 == $data['recInstance'] && self::RT_CSTRING == $data['recType'] && $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.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd945703(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordSlideNumberMCAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SLIDENUMBERMETACHARATOM == $data['recType'] && 0x00000004 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Datas
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies programmable tags with additional slide data.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd951946(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordSlideProgTagsContainer(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0xF == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_PROGTAGS == $data['recType']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A container record that specifies the color scheme used by a slide.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd949420(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordSlideSchemeColorSchemeAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x001 == $data['recInstance'] && self::RT_COLORSCHEMEATOM == $data['recType'] && 0x00000020 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length
|
|
$rgSchemeColor = [];
|
|
for ($inc = 0; $inc <= 7; ++$inc) {
|
|
$rgSchemeColor[] = [
|
|
'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.
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd943408(v=office.12).aspx
|
|
*
|
|
* @return array<string, int>
|
|
*/
|
|
private function readRecordSlideShowSlideInfoAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x0 == $data['recVer'] && 0x000 == $data['recInstance'] && self::RT_SLIDESHOWSLIDEINFOATOM == $data['recType'] && 0x00000010 == $data['recLen']) {
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// Length;
|
|
$arrayReturn['length'] += $data['recLen'];
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* UserEditAtom.
|
|
*
|
|
* @see http://msdn.microsoft.com/en-us/library/dd945746(v=office.12).aspx
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*/
|
|
private function readRecordUserEditAtom(string $stream, int $pos): void
|
|
{
|
|
$rHeader = $this->loadRecordHeader($stream, $pos);
|
|
$pos += 8;
|
|
if (0x0 != $rHeader['recVer'] || 0x000 != $rHeader['recInstance'] || self::RT_USEREDITATOM != $rHeader['recType'] || (0x0000001C != $rHeader['recLen'] && 0x00000020 != $rHeader['recLen'])) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > RecordHeader');
|
|
}
|
|
|
|
// lastSlideIdRef
|
|
$pos += 4;
|
|
// version
|
|
$pos += 2;
|
|
|
|
// minorVersion
|
|
$minorVersion = self::getInt1d($stream, $pos);
|
|
++$pos;
|
|
if (0x00 != $minorVersion) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > minorVersion');
|
|
}
|
|
|
|
// majorVersion
|
|
$majorVersion = self::getInt1d($stream, $pos);
|
|
++$pos;
|
|
if (0x03 != $majorVersion) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : UserEditAtom > majorVersion');
|
|
}
|
|
|
|
// offsetLastEdit
|
|
$pos += 4;
|
|
// offsetPersistDirectory
|
|
$this->offsetPersistDirectory = self::getInt4d($stream, $pos);
|
|
$pos += 4;
|
|
|
|
// docPersistIdRef
|
|
$docPersistIdRef = self::getInt4d($stream, $pos);
|
|
$pos += 4;
|
|
if (0x00000001 != $docPersistIdRef) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, '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.
|
|
*
|
|
* @return array{'length': int, 'strLenRT': int, 'partLength': int, 'bold': bool, 'italic': bool, 'underline': bool, 'fontName': string, 'fontSize': int, 'color': Color}
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd945870(v=office.12).aspx
|
|
*/
|
|
private function readStructureTextCFRun(string $stream, int $pos, int $strLenRT): array
|
|
{
|
|
$arrayReturn = [
|
|
'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 = [];
|
|
$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 (1 == $masksData['bold'] || 1 == $masksData['italic'] || 1 == $masksData['underline'] || 1 == $masksData['shadow'] || 1 == $masksData['fehint'] || 1 == $masksData['kumi'] || 1 == $masksData['emboss'] || 1 == $masksData['fHasStyle']) {
|
|
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
|
|
$fontStyleFlags = [];
|
|
$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'] = (1 == $fontStyleFlags['bold']) ? true : false;
|
|
$arrayReturn['italic'] = (1 == $fontStyleFlags['italic']) ? true : false;
|
|
$arrayReturn['underline'] = (1 == $fontStyleFlags['underline']) ? true : false;
|
|
}
|
|
if (1 == $masksData['typeface']) {
|
|
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
$arrayReturn['fontName'] = isset($this->arrayFonts[$data]) ? $this->arrayFonts[$data] : '';
|
|
}
|
|
if (1 == $masksData['oldEATypeface']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['ansiTypeface']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['symbolTypeface']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['size']) {
|
|
$arrayReturn['fontSize'] = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['color']) {
|
|
$red = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
$green = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
$blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
$index = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
|
|
if (0xFE == $index) {
|
|
$strColor = \str_pad(\dechex($red), 2, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex($green), 2, '0', STR_PAD_LEFT);
|
|
$strColor .= \str_pad(\dechex($blue), 2, '0', STR_PAD_LEFT);
|
|
|
|
$arrayReturn['color'] = new Color('FF' . $strColor);
|
|
}
|
|
}
|
|
if (1 == $masksData['position']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A structure that specifies the paragraph-level formatting of a run of text.
|
|
*
|
|
* @return array{'length': int, 'strLenRT': int, 'alignH': string|null, 'bulletChar': string, 'leftMargin': int, 'indent': int}
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd923535(v=office.12).aspx
|
|
*/
|
|
private function readStructureTextPFRun(string $stream, int $pos, int $strLenRT): array
|
|
{
|
|
$arrayReturn = [
|
|
'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 = [];
|
|
$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 = [];
|
|
if (1 == $masksData['hasBullet'] || 1 == $masksData['bulletHasFont'] || 1 == $masksData['bulletHasColor'] || 1 == $masksData['bulletHasSize']) {
|
|
$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 (1 == $masksData['bulletChar']) {
|
|
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
$arrayReturn['bulletChar'] = \chr($data);
|
|
}
|
|
if (1 == $masksData['bulletFont']) {
|
|
// $data = self::getInt2d($stream, $pos);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['bulletSize']) {
|
|
// $data = self::getInt2d($stream, $pos);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['bulletColor']) {
|
|
// $red = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
// $green = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
// $blue = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
$index = self::getInt1d($stream, $pos + $arrayReturn['length']);
|
|
++$arrayReturn['length'];
|
|
|
|
if (0xFE == $index) {
|
|
// $strColor = str_pad(dechex($red), 2, '0', STR_PAD_LEFT);
|
|
// $strColor .= str_pad(dechex($green), 2, '0', STR_PAD_LEFT);
|
|
// $strColor .= str_pad(dechex($blue), 2, '0', STR_PAD_LEFT);
|
|
}
|
|
}
|
|
if (1 == $masksData['align']) {
|
|
$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 (1 == $masksData['lineSpacing']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['spaceBefore']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['spaceAfter']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['leftMargin']) {
|
|
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
$arrayReturn['leftMargin'] = (int) \round($data / 6);
|
|
}
|
|
if (1 == $masksData['indent']) {
|
|
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
$arrayReturn['indent'] = (int) \round($data / 6);
|
|
}
|
|
if (1 == $masksData['defaultTabSize']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['tabStops']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
if (1 == $masksData['fontAlign']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['charWrap'] || 1 == $masksData['wordWrap'] || 1 == $masksData['overflow']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['textDirection']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A structure that specifies language and spelling information for a run of text.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd909603(v=office.12).aspx
|
|
*/
|
|
private function readStructureTextSIRun(string $stream, int $pos, int $strLenRT): array
|
|
{
|
|
$arrayReturn = [
|
|
'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 = [];
|
|
$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 (1 == $masksData['spell']) {
|
|
$data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
$masksSpell = [];
|
|
$masksSpell['error'] = ($data >> 0) & \bindec('1');
|
|
$masksSpell['clean'] = ($data >> 1) & \bindec('1');
|
|
$masksSpell['grammar'] = ($data >> 2) & \bindec('1');
|
|
}
|
|
if (1 == $masksData['lang']) {
|
|
// $data = self::getInt2d($stream, $pos);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['altLang']) {
|
|
// $data = self::getInt2d($stream, $pos);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fBidi']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
if (1 == $masksData['fPp10ext']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
if (1 == $masksData['smartTag']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
/**
|
|
* A structure that specifies tabbing, margins, and indentation for text.
|
|
*
|
|
* @return array<string, int>
|
|
*
|
|
* @throws FeatureNotImplementedException
|
|
*
|
|
* @see https://msdn.microsoft.com/en-us/library/dd922749(v=office.12).aspx
|
|
*/
|
|
private function readStructureTextRuler(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = self::getInt4d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 4;
|
|
|
|
$masksData = [];
|
|
$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 (1 == $masksData['fCLevels']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
if (1 == $masksData['fDefaultTabSize']) {
|
|
throw new FeatureNotImplementedException();
|
|
}
|
|
if (1 == $masksData['fTabStops']) {
|
|
$count = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
$arrayTabStops = [];
|
|
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[] = [
|
|
'position' => $position,
|
|
'type' => $type,
|
|
];
|
|
}
|
|
}
|
|
if (1 == $masksData['fLeftMargin1']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fIndent1']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fLeftMargin2']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fIndent2']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fLeftMargin3']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fIndent3']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fLeftMargin4']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fIndent4']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fLeftMargin5']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
if (1 == $masksData['fIndent5']) {
|
|
// $data = self::getInt2d($stream, $pos + $arrayReturn['length']);
|
|
$arrayReturn['length'] += 2;
|
|
}
|
|
|
|
return $arrayReturn;
|
|
}
|
|
|
|
private function readRecordNotesContainer(string $stream, int $pos): void
|
|
{
|
|
// notesAtom
|
|
$notesAtom = $this->readRecordNotesAtom($stream, $pos);
|
|
$pos += $notesAtom['length'];
|
|
|
|
// drawing
|
|
$drawing = $this->readRecordDrawingContainer($stream, $pos);
|
|
$pos += $drawing['length'];
|
|
|
|
// slideSchemeColorSchemeAtom
|
|
// slideNameAtom
|
|
// slideProgTagsContainer
|
|
// rgNotesRoundTripAtom
|
|
}
|
|
|
|
/**
|
|
* @return array<string, int>
|
|
*
|
|
* @throws InvalidFileFormatException
|
|
*/
|
|
private function readRecordNotesAtom(string $stream, int $pos): array
|
|
{
|
|
$arrayReturn = [
|
|
'length' => 0,
|
|
];
|
|
|
|
$data = $this->loadRecordHeader($stream, $pos);
|
|
if (0x1 != $data['recVer'] || 0x000 != $data['recInstance'] || self::RT_NOTESATOM != $data['recType'] || 0x00000008 != $data['recLen']) {
|
|
throw new InvalidFileFormatException($this->filename, PowerPoint97::class, 'Location : NotesAtom > RecordHeader)');
|
|
}
|
|
// Record Header
|
|
$arrayReturn['length'] += 8;
|
|
// NotesAtom > slideIdRef
|
|
$notesIdRef = self::getInt4d($stream, $pos + $arrayReturn['length']);
|
|
if (-2147483648 == $notesIdRef) {
|
|
$notesIdRef = 0;
|
|
}
|
|
$this->currentNote = $notesIdRef;
|
|
$arrayReturn['length'] += 4;
|
|
|
|
// NotesAtom > slideFlags
|
|
$arrayReturn['length'] += 2;
|
|
// NotesAtom > unused
|
|
$arrayReturn['length'] += 2;
|
|
|
|
return $arrayReturn;
|
|
}
|
|
}
|