Viewing file: Escher.php (16.52 KB) -rwxrwxrwx Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
<?php
namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate; use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer; use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer; use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer\SpContainer; use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer; use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer; use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE; use PhpOffice\PhpSpreadsheet\Shared\Escher\DggContainer\BstoreContainer\BSE\Blip;
class Escher { /** * The object we are writing. */ private $object;
/** * The written binary data. */ private $data;
/** * Shape offsets. Positions in binary stream where a new shape record begins. * * @var array */ private $spOffsets;
/** * Shape types. * * @var array */ private $spTypes;
/** * Constructor. * * @param mixed $object */ public function __construct($object) { $this->object = $object; }
/** * Process the object to be written. * * @return string */ public function close() { // initialize $this->data = '';
switch (get_class($this->object)) { case \PhpOffice\PhpSpreadsheet\Shared\Escher::class: if ($dggContainer = $this->object->getDggContainer()) { $writer = new self($dggContainer); $this->data = $writer->close(); } elseif ($dgContainer = $this->object->getDgContainer()) { $writer = new self($dgContainer); $this->data = $writer->close(); $this->spOffsets = $writer->getSpOffsets(); $this->spTypes = $writer->getSpTypes(); }
break; case DggContainer::class: // this is a container record
// initialize $innerData = '';
// write the dgg $recVer = 0x0; $recInstance = 0x0000; $recType = 0xF006;
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
// dgg data $dggData = pack( 'VVVV', $this->object->getSpIdMax(), // maximum shape identifier increased by one $this->object->getCDgSaved() + 1, // number of file identifier clusters increased by one $this->object->getCSpSaved(), $this->object->getCDgSaved() // count total number of drawings saved );
// add file identifier clusters (one per drawing) $IDCLs = $this->object->getIDCLs();
foreach ($IDCLs as $dgId => $maxReducedSpId) { $dggData .= pack('VV', $dgId, $maxReducedSpId + 1); }
$header = pack('vvV', $recVerInstance, $recType, strlen($dggData)); $innerData .= $header . $dggData;
// write the bstoreContainer if ($bstoreContainer = $this->object->getBstoreContainer()) { $writer = new self($bstoreContainer); $innerData .= $writer->close(); }
// write the record $recVer = 0xF; $recInstance = 0x0000; $recType = 0xF000; $length = strlen($innerData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header . $innerData;
break; case BstoreContainer::class: // this is a container record
// initialize $innerData = '';
// treat the inner data if ($BSECollection = $this->object->getBSECollection()) { foreach ($BSECollection as $BSE) { $writer = new self($BSE); $innerData .= $writer->close(); } }
// write the record $recVer = 0xF; $recInstance = count($this->object->getBSECollection()); $recType = 0xF001; $length = strlen($innerData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header . $innerData;
break; case BSE::class: // this is a semi-container record
// initialize $innerData = '';
// here we treat the inner data if ($blip = $this->object->getBlip()) { $writer = new self($blip); $innerData .= $writer->close(); }
// initialize $data = '';
$btWin32 = $this->object->getBlipType(); $btMacOS = $this->object->getBlipType(); $data .= pack('CC', $btWin32, $btMacOS);
$rgbUid = pack('VVVV', 0, 0, 0, 0); // todo $data .= $rgbUid;
$tag = 0; $size = strlen($innerData); $cRef = 1; $foDelay = 0; //todo $unused1 = 0x0; $cbName = 0x0; $unused2 = 0x0; $unused3 = 0x0; $data .= pack('vVVVCCCC', $tag, $size, $cRef, $foDelay, $unused1, $cbName, $unused2, $unused3);
$data .= $innerData;
// write the record $recVer = 0x2; $recInstance = $this->object->getBlipType(); $recType = 0xF007; $length = strlen($data);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header;
$this->data .= $data;
break; case Blip::class: // this is an atom record
// write the record switch ($this->object->getParent()->getBlipType()) { case BSE::BLIPTYPE_JPEG: // initialize $innerData = '';
$rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo $innerData .= $rgbUid1;
$tag = 0xFF; // todo $innerData .= pack('C', $tag);
$innerData .= $this->object->getData();
$recVer = 0x0; $recInstance = 0x46A; $recType = 0xF01D; $length = strlen($innerData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header;
$this->data .= $innerData;
break; case BSE::BLIPTYPE_PNG: // initialize $innerData = '';
$rgbUid1 = pack('VVVV', 0, 0, 0, 0); // todo $innerData .= $rgbUid1;
$tag = 0xFF; // todo $innerData .= pack('C', $tag);
$innerData .= $this->object->getData();
$recVer = 0x0; $recInstance = 0x6E0; $recType = 0xF01E; $length = strlen($innerData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header;
$this->data .= $innerData;
break; }
break; case DgContainer::class: // this is a container record
// initialize $innerData = '';
// write the dg $recVer = 0x0; $recInstance = $this->object->getDgId(); $recType = 0xF008; $length = 8;
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
// number of shapes in this drawing (including group shape) $countShapes = count($this->object->getSpgrContainer()->getChildren()); $innerData .= $header . pack('VV', $countShapes, $this->object->getLastSpId());
// write the spgrContainer if ($spgrContainer = $this->object->getSpgrContainer()) { $writer = new self($spgrContainer); $innerData .= $writer->close();
// get the shape offsets relative to the spgrContainer record $spOffsets = $writer->getSpOffsets(); $spTypes = $writer->getSpTypes();
// save the shape offsets relative to dgContainer foreach ($spOffsets as &$spOffset) { $spOffset += 24; // add length of dgContainer header data (8 bytes) plus dg data (16 bytes) }
$this->spOffsets = $spOffsets; $this->spTypes = $spTypes; }
// write the record $recVer = 0xF; $recInstance = 0x0000; $recType = 0xF002; $length = strlen($innerData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header . $innerData;
break; case SpgrContainer::class: // this is a container record
// initialize $innerData = '';
// initialize spape offsets $totalSize = 8; $spOffsets = []; $spTypes = [];
// treat the inner data foreach ($this->object->getChildren() as $spContainer) { $writer = new self($spContainer); $spData = $writer->close(); $innerData .= $spData;
// save the shape offsets (where new shape records begin) $totalSize += strlen($spData); $spOffsets[] = $totalSize;
$spTypes = array_merge($spTypes, $writer->getSpTypes()); }
// write the record $recVer = 0xF; $recInstance = 0x0000; $recType = 0xF003; $length = strlen($innerData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header . $innerData; $this->spOffsets = $spOffsets; $this->spTypes = $spTypes;
break; case SpContainer::class: // initialize $data = '';
// build the data
// write group shape record, if necessary? if ($this->object->getSpgr()) { $recVer = 0x1; $recInstance = 0x0000; $recType = 0xF009; $length = 0x00000010;
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$data .= $header . pack('VVVV', 0, 0, 0, 0); } $this->spTypes[] = ($this->object->getSpType());
// write the shape record $recVer = 0x2; $recInstance = $this->object->getSpType(); // shape type $recType = 0xF00A; $length = 0x00000008;
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$data .= $header . pack('VV', $this->object->getSpId(), $this->object->getSpgr() ? 0x0005 : 0x0A00);
// the options if ($this->object->getOPTCollection()) { $optData = '';
$recVer = 0x3; $recInstance = count($this->object->getOPTCollection()); $recType = 0xF00B; foreach ($this->object->getOPTCollection() as $property => $value) { $optData .= pack('vV', $property, $value); } $length = strlen($optData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length); $data .= $header . $optData; }
// the client anchor if ($this->object->getStartCoordinates()) { $clientAnchorData = '';
$recVer = 0x0; $recInstance = 0x0; $recType = 0xF010;
// start coordinates [$column, $row] = Coordinate::coordinateFromString($this->object->getStartCoordinates()); $c1 = Coordinate::columnIndexFromString($column) - 1; $r1 = $row - 1;
// start offsetX $startOffsetX = $this->object->getStartOffsetX();
// start offsetY $startOffsetY = $this->object->getStartOffsetY();
// end coordinates [$column, $row] = Coordinate::coordinateFromString($this->object->getEndCoordinates()); $c2 = Coordinate::columnIndexFromString($column) - 1; $r2 = $row - 1;
// end offsetX $endOffsetX = $this->object->getEndOffsetX();
// end offsetY $endOffsetY = $this->object->getEndOffsetY();
$clientAnchorData = pack('vvvvvvvvv', $this->object->getSpFlag(), $c1, $startOffsetX, $r1, $startOffsetY, $c2, $endOffsetX, $r2, $endOffsetY);
$length = strlen($clientAnchorData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length); $data .= $header . $clientAnchorData; }
// the client data, just empty for now if (!$this->object->getSpgr()) { $clientDataData = '';
$recVer = 0x0; $recInstance = 0x0; $recType = 0xF011;
$length = strlen($clientDataData);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length); $data .= $header . $clientDataData; }
// write the record $recVer = 0xF; $recInstance = 0x0000; $recType = 0xF004; $length = strlen($data);
$recVerInstance = $recVer; $recVerInstance |= $recInstance << 4;
$header = pack('vvV', $recVerInstance, $recType, $length);
$this->data = $header . $data;
break; }
return $this->data; }
/** * Gets the shape offsets. * * @return array */ public function getSpOffsets() { return $this->spOffsets; }
/** * Gets the shape types. * * @return array */ public function getSpTypes() { return $this->spTypes; } }
|