416 parent::__construct();
435 $ole->read($pFilename);
463 $worksheetNames = [];
469 $this->dataSize = strlen($this->
data);
475 while ($this->pos < $this->dataSize) {
476 $code = self::getUInt2d($this->
data, $this->pos);
479 case self::XLS_TYPE_BOF:
483 case self::XLS_TYPE_SHEET:
487 case self::XLS_TYPE_EOF:
498 foreach ($this->sheets as $sheet) {
499 if ($sheet[
'sheetType'] != 0x00) {
504 $worksheetNames[] = $sheet[
'name'];
507 return $worksheetNames;
527 $this->dataSize = strlen($this->
data);
534 while ($this->pos < $this->dataSize) {
535 $code = self::getUInt2d($this->
data, $this->pos);
538 case self::XLS_TYPE_BOF:
542 case self::XLS_TYPE_SHEET:
546 case self::XLS_TYPE_EOF:
558 foreach ($this->sheets as $sheet) {
559 if ($sheet[
'sheetType'] != 0x00) {
567 $tmpInfo[
'worksheetName'] = $sheet[
'name'];
568 $tmpInfo[
'lastColumnLetter'] =
'A';
569 $tmpInfo[
'lastColumnIndex'] = 0;
570 $tmpInfo[
'totalRows'] = 0;
571 $tmpInfo[
'totalColumns'] = 0;
573 $this->pos = $sheet[
'offset'];
575 while ($this->pos <= $this->dataSize - 4) {
576 $code = self::getUInt2d($this->
data, $this->pos);
579 case self::XLS_TYPE_RK:
580 case self::XLS_TYPE_LABELSST:
581 case self::XLS_TYPE_NUMBER:
582 case self::XLS_TYPE_FORMULA:
583 case self::XLS_TYPE_BOOLERR:
584 case self::XLS_TYPE_LABEL:
585 $length = self::getUInt2d($this->
data, $this->pos + 2);
589 $this->pos += 4 + $length;
591 $rowIndex = self::getUInt2d($recordData, 0) + 1;
592 $columnIndex = self::getUInt2d($recordData, 2);
594 $tmpInfo[
'totalRows'] = max($tmpInfo[
'totalRows'], $rowIndex);
595 $tmpInfo[
'lastColumnIndex'] = max($tmpInfo[
'lastColumnIndex'], $columnIndex);
598 case self::XLS_TYPE_BOF:
602 case self::XLS_TYPE_EOF:
614 $tmpInfo[
'totalColumns'] = $tmpInfo[
'lastColumnIndex'] + 1;
616 $worksheetInfo[] = $tmpInfo;
619 return $worksheetInfo;
629 public function load($pFilename)
636 $this->spreadsheet->removeSheetByIndex(0);
637 if (!$this->readDataOnly) {
638 $this->spreadsheet->removeCellStyleXfByIndex(0);
639 $this->spreadsheet->removeCellXfByIndex(0);
649 $this->dataSize = strlen($this->
data);
655 $this->objFonts = [];
658 $this->externalBooks = [];
660 $this->definedname = [];
662 $this->drawingGroupData =
'';
664 $this->mapCellXfIndex = [];
665 $this->mapCellStyleXfIndex = [];
668 while ($this->pos < $this->dataSize) {
669 $code = self::getUInt2d($this->
data, $this->pos);
672 case self::XLS_TYPE_BOF:
676 case self::XLS_TYPE_FILEPASS:
680 case self::XLS_TYPE_CODEPAGE:
684 case self::XLS_TYPE_DATEMODE:
688 case self::XLS_TYPE_FONT:
692 case self::XLS_TYPE_FORMAT:
696 case self::XLS_TYPE_XF:
700 case self::XLS_TYPE_XFEXT:
704 case self::XLS_TYPE_STYLE:
708 case self::XLS_TYPE_PALETTE:
712 case self::XLS_TYPE_SHEET:
716 case self::XLS_TYPE_EXTERNALBOOK:
720 case self::XLS_TYPE_EXTERNNAME:
724 case self::XLS_TYPE_EXTERNSHEET:
728 case self::XLS_TYPE_DEFINEDNAME:
732 case self::XLS_TYPE_MSODRAWINGGROUP:
736 case self::XLS_TYPE_SST:
740 case self::XLS_TYPE_EOF:
753 if (!$this->readDataOnly) {
754 foreach ($this->objFonts as $objFont) {
755 if (isset($objFont->colorIndex)) {
756 $color =
Xls\Color::map($objFont->colorIndex, $this->palette, $this->version);
757 $objFont->getColor()->setRGB($color[
'rgb']);
761 foreach ($this->spreadsheet->getCellXfCollection() as $objStyle) {
763 $fill = $objStyle->getFill();
765 if (isset($fill->startcolorIndex)) {
766 $startColor =
Xls\Color::map($fill->startcolorIndex, $this->palette, $this->version);
767 $fill->getStartColor()->setRGB($startColor[
'rgb']);
769 if (isset($fill->endcolorIndex)) {
770 $endColor =
Xls\Color::map($fill->endcolorIndex, $this->palette, $this->version);
771 $fill->getEndColor()->setRGB($endColor[
'rgb']);
775 $top = $objStyle->getBorders()->getTop();
776 $right = $objStyle->getBorders()->getRight();
777 $bottom = $objStyle->getBorders()->getBottom();
778 $left = $objStyle->getBorders()->getLeft();
779 $diagonal = $objStyle->getBorders()->getDiagonal();
781 if (isset($top->colorIndex)) {
782 $borderTopColor =
Xls\Color::map($top->colorIndex, $this->palette, $this->version);
783 $top->getColor()->setRGB($borderTopColor[
'rgb']);
785 if (isset($right->colorIndex)) {
786 $borderRightColor =
Xls\Color::map($right->colorIndex, $this->palette, $this->version);
787 $right->getColor()->setRGB($borderRightColor[
'rgb']);
789 if (isset($bottom->colorIndex)) {
790 $borderBottomColor =
Xls\Color::map($bottom->colorIndex, $this->palette, $this->version);
791 $bottom->getColor()->setRGB($borderBottomColor[
'rgb']);
793 if (isset($left->colorIndex)) {
794 $borderLeftColor =
Xls\Color::map($left->colorIndex, $this->palette, $this->version);
795 $left->getColor()->setRGB($borderLeftColor[
'rgb']);
797 if (isset($diagonal->colorIndex)) {
798 $borderDiagonalColor =
Xls\Color::map($diagonal->colorIndex, $this->palette, $this->version);
799 $diagonal->getColor()->setRGB($borderDiagonalColor[
'rgb']);
805 $escherWorkbook = null;
806 if (!$this->readDataOnly && $this->drawingGroupData) {
808 $reader =
new Xls\Escher($escher);
809 $escherWorkbook =
$reader->load($this->drawingGroupData);
813 foreach ($this->sheets as $sheet) {
814 if ($sheet[
'sheetType'] != 0x00) {
820 if (isset($this->loadSheetsOnly) && !in_array($sheet[
'name'], $this->loadSheetsOnly)) {
825 $this->phpSheet = $this->spreadsheet->createSheet();
829 $this->phpSheet->setTitle($sheet[
'name'],
false,
false);
830 $this->phpSheet->setSheetState($sheet[
'sheetState']);
832 $this->pos = $sheet[
'offset'];
835 $this->isFitToPages =
false;
838 $this->drawingData =
'';
844 $this->sharedFormulaParts = [];
847 $this->sharedFormulas = [];
850 $this->textObjects = [];
853 $this->cellNotes = [];
854 $this->textObjRef = -1;
856 while ($this->pos <= $this->dataSize - 4) {
857 $code = self::getUInt2d($this->
data, $this->pos);
860 case self::XLS_TYPE_BOF:
864 case self::XLS_TYPE_PRINTGRIDLINES:
868 case self::XLS_TYPE_DEFAULTROWHEIGHT:
872 case self::XLS_TYPE_SHEETPR:
876 case self::XLS_TYPE_HORIZONTALPAGEBREAKS:
880 case self::XLS_TYPE_VERTICALPAGEBREAKS:
884 case self::XLS_TYPE_HEADER:
888 case self::XLS_TYPE_FOOTER:
892 case self::XLS_TYPE_HCENTER:
896 case self::XLS_TYPE_VCENTER:
900 case self::XLS_TYPE_LEFTMARGIN:
904 case self::XLS_TYPE_RIGHTMARGIN:
908 case self::XLS_TYPE_TOPMARGIN:
912 case self::XLS_TYPE_BOTTOMMARGIN:
916 case self::XLS_TYPE_PAGESETUP:
920 case self::XLS_TYPE_PROTECT:
924 case self::XLS_TYPE_SCENPROTECT:
928 case self::XLS_TYPE_OBJECTPROTECT:
932 case self::XLS_TYPE_PASSWORD:
936 case self::XLS_TYPE_DEFCOLWIDTH:
940 case self::XLS_TYPE_COLINFO:
944 case self::XLS_TYPE_DIMENSION:
948 case self::XLS_TYPE_ROW:
952 case self::XLS_TYPE_DBCELL:
956 case self::XLS_TYPE_RK:
960 case self::XLS_TYPE_LABELSST:
964 case self::XLS_TYPE_MULRK:
968 case self::XLS_TYPE_NUMBER:
972 case self::XLS_TYPE_FORMULA:
976 case self::XLS_TYPE_SHAREDFMLA:
980 case self::XLS_TYPE_BOOLERR:
984 case self::XLS_TYPE_MULBLANK:
988 case self::XLS_TYPE_LABEL:
992 case self::XLS_TYPE_BLANK:
996 case self::XLS_TYPE_MSODRAWING:
1000 case self::XLS_TYPE_OBJ:
1004 case self::XLS_TYPE_WINDOW2:
1008 case self::XLS_TYPE_PAGELAYOUTVIEW:
1012 case self::XLS_TYPE_SCL:
1016 case self::XLS_TYPE_PANE:
1020 case self::XLS_TYPE_SELECTION:
1024 case self::XLS_TYPE_MERGEDCELLS:
1028 case self::XLS_TYPE_HYPERLINK:
1032 case self::XLS_TYPE_DATAVALIDATIONS:
1036 case self::XLS_TYPE_DATAVALIDATION:
1040 case self::XLS_TYPE_SHEETLAYOUT:
1044 case self::XLS_TYPE_SHEETPROTECTION:
1048 case self::XLS_TYPE_RANGEPROTECTION:
1052 case self::XLS_TYPE_NOTE:
1056 case self::XLS_TYPE_TXO:
1060 case self::XLS_TYPE_CONTINUE:
1064 case self::XLS_TYPE_EOF:
1076 if (!$this->readDataOnly && $this->drawingData) {
1077 $escherWorksheet =
new Escher();
1078 $reader =
new Xls\Escher($escherWorksheet);
1079 $escherWorksheet =
$reader->load($this->drawingData);
1082 $allSpContainers = $escherWorksheet->getDgContainer()->getSpgrContainer()->getAllSpContainers();
1086 foreach ($this->objs as
$n => $obj) {
1088 if (isset($allSpContainers[
$n + 1]) && is_object($allSpContainers[
$n + 1])) {
1089 $spContainer = $allSpContainers[
$n + 1];
1092 if ($spContainer->getNestingLevel() > 1) {
1100 $startOffsetX = $spContainer->getStartOffsetX();
1101 $startOffsetY = $spContainer->getStartOffsetY();
1102 $endOffsetX = $spContainer->getEndOffsetX();
1103 $endOffsetY = $spContainer->getEndOffsetY();
1112 switch ($obj[
'otObjType']) {
1115 if (isset($this->cellNotes[$obj[
'idObjID']])) {
1116 $cellNote = $this->cellNotes[$obj[
'idObjID']];
1118 if (isset($this->textObjects[$obj[
'idObjID']])) {
1119 $textObject = $this->textObjects[$obj[
'idObjID']];
1120 $this->cellNotes[$obj[
'idObjID']][
'objTextData'] = $textObject;
1128 $BSEindex = $spContainer->getOPT(0x0104);
1138 if ($escherWorkbook) {
1139 $BSECollection = $escherWorkbook->getDggContainer()->getBstoreContainer()->getBSECollection();
1140 $BSE = $BSECollection[$BSEindex - 1];
1141 $blipType = $BSE->getBlipType();
1144 if ($blip = $BSE->getBlip()) {
1145 $ih = imagecreatefromstring($blip->getData());
1147 $drawing->setImageResource($ih);
1150 $drawing->setResizeProportional(
false);
1151 $drawing->setWidth($width);
1152 $drawing->setHeight($height);
1153 $drawing->setOffsetX($offsetX);
1154 $drawing->setOffsetY($offsetY);
1156 switch ($blipType) {
1169 $drawing->setWorksheet($this->phpSheet);
1170 $drawing->setCoordinates($spContainer->getStartCoordinates());
1183 if ($this->version == self::XLS_BIFF8) {
1184 foreach ($this->sharedFormulaParts as $cell =>
$baseCell) {
1193 if (!empty($this->cellNotes)) {
1194 foreach ($this->cellNotes as $note => $noteDetails) {
1195 if (!isset($noteDetails[
'objTextData'])) {
1196 if (isset($this->textObjects[$note])) {
1197 $textObject = $this->textObjects[$note];
1198 $noteDetails[
'objTextData'] = $textObject;
1200 $noteDetails[
'objTextData'][
'text'] =
'';
1203 $cellAddress = str_replace(
'$',
'', $noteDetails[
'cellRef']);
1204 $this->phpSheet->getComment($cellAddress)->setAuthor($noteDetails[
'author'])->setText($this->
parseRichText($noteDetails[
'objTextData'][
'text']));
1210 foreach ($this->definedname as $definedName) {
1211 if ($definedName[
'isBuiltInName']) {
1212 switch ($definedName[
'name']) {
1213 case pack(
'C', 0x06):
1216 $ranges = explode(
',', $definedName[
'formula']);
1218 $extractedRanges = [];
1219 foreach ($ranges as $range) {
1224 $sheetName = trim($explodes[0],
"'");
1225 if (count($explodes) == 2) {
1226 if (strpos($explodes[1],
':') ===
false) {
1227 $explodes[1] = $explodes[1] .
':' . $explodes[1];
1229 $extractedRanges[] = str_replace(
'$',
'', $explodes[1]);
1232 if ($docSheet = $this->spreadsheet->getSheetByName($sheetName)) {
1233 $docSheet->getPageSetup()->setPrintArea(implode(
',', $extractedRanges));
1237 case pack(
'C', 0x07):
1248 $ranges = explode(
',', $definedName[
'formula']);
1249 foreach ($ranges as $range) {
1253 if (strpos($range,
'!') !==
false) {
1255 if ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) {
1256 $extractedRange = $explodes[1];
1257 $extractedRange = str_replace(
'$',
'', $extractedRange);
1259 $coordinateStrings = explode(
':', $extractedRange);
1260 if (count($coordinateStrings) == 2) {
1264 if ($firstColumn ==
'A' && $lastColumn ==
'IV') {
1266 $docSheet->getPageSetup()->setRowsToRepeatAtTop([$firstRow, $lastRow]);
1267 } elseif ($firstRow == 1 && $lastRow == 65536) {
1269 $docSheet->getPageSetup()->setColumnsToRepeatAtLeft([$firstColumn, $lastColumn]);
1280 if (strpos($definedName[
'formula'],
'!') !==
false) {
1283 ($docSheet = $this->spreadsheet->getSheetByName($explodes[0])) ||
1284 ($docSheet = $this->spreadsheet->getSheetByName(trim($explodes[0],
"'")))
1286 $extractedRange = $explodes[1];
1287 $extractedRange = str_replace(
'$',
'', $extractedRange);
1289 $localOnly = ($definedName[
'scope'] == 0) ?
false :
true;
1291 $scope = ($definedName[
'scope'] == 0) ? null : $this->spreadsheet->getSheetByName($this->sheets[$definedName[
'scope'] - 1][
'name']);
1293 $this->spreadsheet->addNamedRange(
new NamedRange((
string) $definedName[
'name'], $docSheet, $extractedRange, $localOnly, $scope));
1319 if ($this->encryption == self::MS_BIFF_CRYPTO_NONE || $pos < $this->encryptionStartPos) {
1324 if ($this->encryption == self::MS_BIFF_CRYPTO_RC4) {
1325 $oldBlock = floor($this->rc4Pos / self::REKEY_BLOCK);
1326 $block = floor(
$pos / self::REKEY_BLOCK);
1327 $endBlock = floor((
$pos + $len) / self::REKEY_BLOCK);
1331 if ($block != $oldBlock || $pos < $this->rc4Pos || !$this->rc4Key) {
1332 $this->rc4Key = $this->
makeKey($block, $this->md5Ctxt);
1337 $this->rc4Key->RC4(str_repeat(
"\0",
$step));
1340 while ($block != $endBlock) {
1341 $step = self::REKEY_BLOCK - (
$pos % self::REKEY_BLOCK);
1342 $recordData .= $this->rc4Key->RC4(substr(
$data, 0,
$step));
1347 $this->rc4Key = $this->
makeKey($block, $this->md5Ctxt);
1349 $recordData .= $this->rc4Key->RC4(substr(
$data, 0, $len));
1353 $this->rc4Pos =
$pos + $len;
1354 } elseif ($this->encryption == self::MS_BIFF_CRYPTO_XOR) {
1355 throw new Exception(
'XOr encryption not supported');
1371 $ole->read($pFilename);
1373 $this->
data = $ole->getStream($ole->wrkbook);
1375 $this->summaryInformation = $ole->getStream($ole->summaryInformation);
1377 $this->documentSummaryInformation = $ole->getStream($ole->documentSummaryInformation);
1385 if (!isset($this->summaryInformation)) {
1395 $secCount = self::getInt4d($this->summaryInformation, 24);
1399 $secOffset = self::getInt4d($this->summaryInformation, 44);
1403 $secLength = self::getInt4d($this->summaryInformation, $secOffset);
1406 $countProperties = self::getInt4d($this->summaryInformation, $secOffset + 4);
1409 $codePage =
'CP1252';
1413 for (
$i = 0;
$i < $countProperties; ++
$i) {
1415 $id = self::getInt4d($this->summaryInformation, ($secOffset + 8) + (8 *
$i));
1419 $offset = self::getInt4d($this->summaryInformation, ($secOffset + 12) + (8 *
$i));
1421 $type = self::getInt4d($this->summaryInformation, $secOffset + $offset);
1429 $value = self::getUInt2d($this->summaryInformation, $secOffset + 4 + $offset);
1433 $value = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1440 $byteLength = self::getInt4d($this->summaryInformation, $secOffset + 4 + $offset);
1441 $value = substr($this->summaryInformation, $secOffset + 8 + $offset, $byteLength);
1443 $value = rtrim($value);
1448 $value =
OLE::OLE2LocalDate(substr($this->summaryInformation, $secOffset + 4 + $offset, 8));
1462 $this->spreadsheet->getProperties()->setTitle(
"$value");
1466 $this->spreadsheet->getProperties()->setSubject(
"$value");
1470 $this->spreadsheet->getProperties()->setCreator(
"$value");
1474 $this->spreadsheet->getProperties()->setKeywords(
"$value");
1478 $this->spreadsheet->getProperties()->setDescription(
"$value");
1485 $this->spreadsheet->getProperties()->setLastModifiedBy(
"$value");
1498 $this->spreadsheet->getProperties()->setCreated($value);
1502 $this->spreadsheet->getProperties()->setModified($value);
1532 if (!isset($this->documentSummaryInformation)) {
1542 $secCount = self::getInt4d($this->documentSummaryInformation, 24);
1546 $secOffset = self::getInt4d($this->documentSummaryInformation, 44);
1550 $secLength = self::getInt4d($this->documentSummaryInformation, $secOffset);
1553 $countProperties = self::getInt4d($this->documentSummaryInformation, $secOffset + 4);
1556 $codePage =
'CP1252';
1560 for (
$i = 0;
$i < $countProperties; ++
$i) {
1562 $id = self::getInt4d($this->documentSummaryInformation, ($secOffset + 8) + (8 *
$i));
1566 $offset = self::getInt4d($this->documentSummaryInformation, ($secOffset + 12) + (8 *
$i));
1568 $type = self::getInt4d($this->documentSummaryInformation, $secOffset + $offset);
1576 $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1580 $value = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1584 $value = self::getUInt2d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1585 $value = ($value == 0 ? false :
true);
1592 $byteLength = self::getInt4d($this->documentSummaryInformation, $secOffset + 4 + $offset);
1593 $value = substr($this->documentSummaryInformation, $secOffset + 8 + $offset, $byteLength);
1595 $value = rtrim($value);
1600 $value =
OLE::OLE2LocalDate(substr($this->documentSummaryInformation, $secOffset + 4 + $offset, 8));
1614 $this->spreadsheet->getProperties()->setCategory(
"$value");
1651 $this->spreadsheet->getProperties()->setManager(
"$value");
1655 $this->spreadsheet->getProperties()->setCompany(
"$value");
1670 $length = self::getUInt2d($this->
data, $this->pos + 2);
1673 $this->pos += 4 + $length;
1682 $length = self::getUInt2d($this->
data, $this->pos + 2);
1686 $this->pos += 4 + $length;
1688 if ($this->readDataOnly) {
1693 if ($this->version == self::XLS_BIFF8) {
1694 $noteObjID = self::getUInt2d($recordData, 6);
1695 $noteAuthor = self::readUnicodeStringLong(substr($recordData, 8));
1696 $noteAuthor = $noteAuthor[
'value'];
1697 $this->cellNotes[$noteObjID] = [
1698 'cellRef' => $cellAddress,
1699 'objectID' => $noteObjID,
1700 'author' => $noteAuthor,
1704 if ($cellAddress ==
'$B$65536') {
1708 $row = self::getUInt2d($recordData, 0);
1710 $arrayKeys = array_keys($this->phpSheet->getComments());
1711 $cellAddress = array_pop($arrayKeys);
1714 $cellAddress = str_replace(
'$',
'', $cellAddress);
1715 $noteLength = self::getUInt2d($recordData, 4);
1716 $noteText = trim(substr($recordData, 6));
1720 $comment = $this->phpSheet->getComment($cellAddress);
1721 $commentText =
$comment->getText()->getPlainText();
1725 $this->phpSheet->getComment($cellAddress)->setText($this->
parseRichText($noteText));
1736 $length = self::getUInt2d($this->
data, $this->pos + 2);
1740 $this->pos += 4 + $length;
1742 if ($this->readDataOnly) {
1752 $grbitOpts = self::getUInt2d($recordData, 0);
1753 $rot = self::getUInt2d($recordData, 2);
1754 $cchText = self::getUInt2d($recordData, 10);
1755 $cbRuns = self::getUInt2d($recordData, 12);
1758 $textByte =
$text[
'spliceOffsets'][1] -
$text[
'spliceOffsets'][0] - 1;
1759 $textStr = substr($text[
'recordData'], $text[
'spliceOffsets'][0] + 1, $textByte);
1761 $is16Bit = ord($text[
'recordData'][0]);
1764 if (($is16Bit & 0x01) === 0) {
1765 $textStr = StringHelper::ConvertEncoding($textStr,
'UTF-8',
'ISO-8859-1');
1772 'format' => substr($text[
'recordData'], $text[
'spliceOffsets'][1], $cbRuns),
1773 'alignment' => $grbitOpts,
1783 $length = self::getUInt2d($this->
data, $this->pos + 2);
1784 $recordData = substr($this->
data, $this->pos + 4, $length);
1787 $this->pos += 4 + $length;
1790 $substreamType = self::getUInt2d($recordData, 2);
1792 switch ($substreamType) {
1793 case self::XLS_WORKBOOKGLOBALS:
1794 $version = self::getUInt2d($recordData, 0);
1796 throw new Exception(
'Cannot read this Excel file. Version is too old.');
1801 case self::XLS_WORKSHEET:
1809 $code = self::getUInt2d($this->
data, $this->pos);
1811 }
while (
$code != self::XLS_TYPE_EOF && $this->pos < $this->dataSize);
1834 $length = self::getUInt2d($this->
data, $this->pos + 2);
1836 if ($length != 54) {
1837 throw new Exception(
'Unexpected file pass record length');
1843 $this->pos += 4 + $length;
1845 if (!$this->
verifyPassword(
'VelvetSweatshop', substr($recordData, 6, 16), substr($recordData, 22, 16), substr($recordData, 38, 16), $this->md5Ctxt)) {
1846 throw new Exception(
'Decryption password incorrect');
1849 $this->encryption = self::MS_BIFF_CRYPTO_RC4;
1852 $this->encryptionStartPos = $this->pos + self::getUInt2d($this->
data, $this->pos + 2);
1865 $pwarray = str_repeat(
"\0", 64);
1867 for (
$i = 0;
$i < 5; ++
$i) {
1868 $pwarray[
$i] = $valContext[
$i];
1871 $pwarray[5] = chr($block & 0xff);
1872 $pwarray[6] = chr(($block >> 8) & 0xff);
1873 $pwarray[7] = chr(($block >> 16) & 0xff);
1874 $pwarray[8] = chr(($block >> 24) & 0xff);
1876 $pwarray[9] =
"\x80";
1877 $pwarray[56] =
"\x48";
1879 $md5 =
new Xls\MD5();
1880 $md5->add($pwarray);
1882 $s = $md5->getContext();
1884 return new Xls\RC4(
$s);
1900 $pwarray = str_repeat(
"\0", 64);
1903 for (
$i = 0;
$i < $iMax; ++
$i) {
1905 $pwarray[2 *
$i] = chr($o & 0xff);
1906 $pwarray[2 *
$i + 1] = chr(($o >> 8) & 0xff);
1908 $pwarray[2 *
$i] = chr(0x80);
1909 $pwarray[56] = chr((
$i << 4) & 0xff);
1911 $md5 =
new Xls\MD5();
1912 $md5->add($pwarray);
1914 $mdContext1 = $md5->getContext();
1922 while ($offset != 16) {
1923 if ((64 - $offset) < 5) {
1924 $tocopy = 64 - $offset;
1926 for (
$i = 0;
$i <= $tocopy; ++
$i) {
1927 $pwarray[$offset +
$i] = $mdContext1[$keyoffset +
$i];
1931 if ($offset == 64) {
1932 $md5->add($pwarray);
1933 $keyoffset = $tocopy;
1934 $tocopy = 5 - $tocopy;
1942 for (
$i = 0;
$i < 16; ++
$i) {
1943 $pwarray[$offset +
$i] = $docid[
$i];
1948 $pwarray[16] =
"\x80";
1949 for (
$i = 0;
$i < 47; ++
$i) {
1950 $pwarray[17 +
$i] =
"\0";
1952 $pwarray[56] =
"\x80";
1953 $pwarray[57] =
"\x0a";
1955 $md5->add($pwarray);
1956 $valContext = $md5->getContext();
1960 $salt =
$key->RC4($salt_data);
1961 $hashedsalt =
$key->RC4($hashedsalt_data);
1963 $salt .=
"\x80" . str_repeat(
"\0", 47);
1968 $mdContext2 = $md5->getContext();
1970 return $mdContext2 == $hashedsalt;
1984 $length = self::getUInt2d($this->
data, $this->pos + 2);
1988 $this->pos += 4 + $length;
1991 $codepage = self::getUInt2d($recordData, 0);
2010 $length = self::getUInt2d($this->
data, $this->pos + 2);
2014 $this->pos += 4 + $length;
2018 if (ord($recordData[0]) == 1) {
2028 $length = self::getUInt2d($this->
data, $this->pos + 2);
2032 $this->pos += 4 + $length;
2034 if (!$this->readDataOnly) {
2035 $objFont =
new Font();
2038 $size = self::getUInt2d($recordData, 0);
2039 $objFont->setSize(
$size / 20);
2044 $isItalic = (0x0002 & self::getUInt2d($recordData, 2)) >> 1;
2046 $objFont->setItalic(
true);
2051 $isStrike = (0x0008 & self::getUInt2d($recordData, 2)) >> 3;
2053 $objFont->setStrikethrough(
true);
2057 $colorIndex = self::getUInt2d($recordData, 4);
2058 $objFont->colorIndex = $colorIndex;
2061 $weight = self::getUInt2d($recordData, 6);
2064 $objFont->setBold(
true);
2070 $escapement = self::getUInt2d($recordData, 8);
2074 $underlineType = ord($recordData[10]);
2081 if ($this->version == self::XLS_BIFF8) {
2082 $string = self::readUnicodeStringShort(substr($recordData, 14));
2086 $objFont->setName($string[
'value']);
2088 $this->objFonts[] = $objFont;
2108 $length = self::getUInt2d($this->
data, $this->pos + 2);
2112 $this->pos += 4 + $length;
2114 if (!$this->readDataOnly) {
2115 $indexCode = self::getUInt2d($recordData, 0);
2117 if ($this->version == self::XLS_BIFF8) {
2118 $string = self::readUnicodeStringLong(substr($recordData, 2));
2124 $formatString = $string[
'value'];
2125 $this->formats[$indexCode] = $formatString;
2145 $length = self::getUInt2d($this->
data, $this->pos + 2);
2149 $this->pos += 4 + $length;
2151 $objStyle =
new Style();
2153 if (!$this->readDataOnly) {
2155 if (self::getUInt2d($recordData, 0) < 4) {
2156 $fontIndex = self::getUInt2d($recordData, 0);
2160 $fontIndex = self::getUInt2d($recordData, 0) - 1;
2162 $objStyle->setFont($this->objFonts[$fontIndex]);
2165 $numberFormatIndex = self::getUInt2d($recordData, 2);
2166 if (isset($this->formats[$numberFormatIndex])) {
2168 $numberFormat = [
'formatCode' => $this->formats[$numberFormatIndex]];
2171 $numberFormat = [
'formatCode' =>
$code];
2174 $numberFormat = [
'formatCode' =>
'General'];
2176 $objStyle->getNumberFormat()->setFormatCode($numberFormat[
'formatCode']);
2180 $xfTypeProt = self::getUInt2d($recordData, 4);
2182 $isLocked = (0x01 & $xfTypeProt) >> 0;
2186 $isHidden = (0x02 & $xfTypeProt) >> 1;
2190 $isCellStyleXf = (0x04 & $xfTypeProt) >> 2;
2194 $horAlign = (0x07 & ord($recordData[6])) >> 0;
2198 $wrapText = (0x08 & ord($recordData[6])) >> 3;
2202 $vertAlign = (0x70 & ord($recordData[6])) >> 4;
2205 if ($this->version == self::XLS_BIFF8) {
2207 $angle = ord($recordData[7]);
2211 } elseif (
$angle <= 180) {
2216 $objStyle->getAlignment()->setTextRotation($rotation);
2220 $indent = (0x0F & ord($recordData[8])) >> 0;
2221 $objStyle->getAlignment()->setIndent($indent);
2224 $shrinkToFit = (0x10 & ord($recordData[8])) >> 4;
2225 switch ($shrinkToFit) {
2227 $objStyle->getAlignment()->setShrinkToFit(
false);
2231 $objStyle->getAlignment()->setShrinkToFit(
true);
2240 if ($bordersLeftStyle =
Xls\
Style\Border::lookup((0x0000000F & self::getInt4d($recordData, 10)) >> 0)) {
2241 $objStyle->getBorders()->getLeft()->setBorderStyle($bordersLeftStyle);
2244 if ($bordersRightStyle =
Xls\
Style\Border::lookup((0x000000F0 & self::getInt4d($recordData, 10)) >> 4)) {
2245 $objStyle->getBorders()->getRight()->setBorderStyle($bordersRightStyle);
2248 if ($bordersTopStyle =
Xls\
Style\Border::lookup((0x00000F00 & self::getInt4d($recordData, 10)) >> 8)) {
2249 $objStyle->getBorders()->getTop()->setBorderStyle($bordersTopStyle);
2252 if ($bordersBottomStyle =
Xls\
Style\Border::lookup((0x0000F000 & self::getInt4d($recordData, 10)) >> 12)) {
2253 $objStyle->getBorders()->getBottom()->setBorderStyle($bordersBottomStyle);
2256 $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & self::getInt4d($recordData, 10)) >> 16;
2259 $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & self::getInt4d($recordData, 10)) >> 23;
2262 $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ?
true :
false;
2265 $diagonalUp = (0x80000000 & self::getInt4d($recordData, 10)) >> 31 ?
true :
false;
2267 if ($diagonalUp ==
false && $diagonalDown ==
false) {
2269 } elseif ($diagonalUp ==
true && $diagonalDown ==
false) {
2271 } elseif ($diagonalUp ==
false && $diagonalDown ==
true) {
2273 } elseif ($diagonalUp ==
true && $diagonalDown ==
true) {
2279 $objStyle->getBorders()->getTop()->colorIndex = (0x0000007F & self::getInt4d($recordData, 14)) >> 0;
2282 $objStyle->getBorders()->getBottom()->colorIndex = (0x00003F80 & self::getInt4d($recordData, 14)) >> 7;
2285 $objStyle->getBorders()->getDiagonal()->colorIndex = (0x001FC000 & self::getInt4d($recordData, 14)) >> 14;
2288 if ($bordersDiagonalStyle =
Xls\
Style\Border::lookup((0x01E00000 & self::getInt4d($recordData, 14)) >> 21)) {
2289 $objStyle->getBorders()->getDiagonal()->setBorderStyle($bordersDiagonalStyle);
2293 if ($fillType =
Xls\
Style\FillPattern::lookup((0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2294 $objStyle->getFill()->setFillType($fillType);
2298 $objStyle->getFill()->startcolorIndex = (0x007F & self::getUInt2d($recordData, 18)) >> 0;
2301 $objStyle->getFill()->endcolorIndex = (0x3F80 & self::getUInt2d($recordData, 18)) >> 7;
2306 $orientationAndFlags = ord($recordData[7]);
2309 $xfOrientation = (0x03 & $orientationAndFlags) >> 0;
2310 switch ($xfOrientation) {
2312 $objStyle->getAlignment()->setTextRotation(0);
2320 $objStyle->getAlignment()->setTextRotation(90);
2324 $objStyle->getAlignment()->setTextRotation(-90);
2330 $borderAndBackground = self::getInt4d($recordData, 8);
2333 $objStyle->getFill()->startcolorIndex = (0x0000007F & $borderAndBackground) >> 0;
2336 $objStyle->getFill()->endcolorIndex = (0x00003F80 & $borderAndBackground) >> 7;
2339 $objStyle->getFill()->setFillType(
Xls\
Style\FillPattern::lookup((0x003F0000 & $borderAndBackground) >> 16));
2342 $objStyle->getBorders()->getBottom()->setBorderStyle(
Xls\
Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
2345 $objStyle->getBorders()->getBottom()->colorIndex = (0xFE000000 & $borderAndBackground) >> 25;
2348 $borderLines = self::getInt4d($recordData, 12);
2351 $objStyle->getBorders()->getTop()->setBorderStyle(
Xls\
Style\Border::lookup((0x00000007 & $borderLines) >> 0));
2354 $objStyle->getBorders()->getLeft()->setBorderStyle(
Xls\
Style\Border::lookup((0x00000038 & $borderLines) >> 3));
2357 $objStyle->getBorders()->getRight()->setBorderStyle(
Xls\
Style\Border::lookup((0x000001C0 & $borderLines) >> 6));
2360 $objStyle->getBorders()->getTop()->colorIndex = (0x0000FE00 & $borderLines) >> 9;
2363 $objStyle->getBorders()->getLeft()->colorIndex = (0x007F0000 & $borderLines) >> 16;
2366 $objStyle->getBorders()->getRight()->colorIndex = (0x3F800000 & $borderLines) >> 23;
2370 if ($isCellStyleXf) {
2372 if ($this->xfIndex == 0) {
2373 $this->spreadsheet->addCellStyleXf($objStyle);
2378 $this->spreadsheet->addCellXf($objStyle);
2379 $this->mapCellXfIndex[
$this->xfIndex] = count($this->spreadsheet->getCellXfCollection()) - 1;
2389 $length = self::getUInt2d($this->
data, $this->pos + 2);
2393 $this->pos += 4 + $length;
2395 if (!$this->readDataOnly) {
2405 $ixfe = self::getUInt2d($recordData, 14);
2410 $cexts = self::getUInt2d($recordData, 18);
2414 while ($offset < $length) {
2416 $extType = self::getUInt2d($recordData, $offset);
2419 $cb = self::getUInt2d($recordData, $offset + 2);
2422 $extData = substr($recordData, $offset + 4, $cb);
2426 $xclfType = self::getUInt2d($extData, 0);
2427 $xclrValue = substr($extData, 4, 4);
2429 if ($xclfType == 2) {
2430 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2433 if (isset($this->mapCellXfIndex[$ixfe])) {
2434 $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2435 $fill->getStartColor()->setRGB($rgb);
2436 $fill->startcolorIndex = null;
2442 $xclfType = self::getUInt2d($extData, 0);
2443 $xclrValue = substr($extData, 4, 4);
2445 if ($xclfType == 2) {
2446 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2449 if (isset($this->mapCellXfIndex[$ixfe])) {
2450 $fill = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFill();
2451 $fill->getEndColor()->setRGB($rgb);
2452 $fill->endcolorIndex = null;
2458 $xclfType = self::getUInt2d($extData, 0);
2459 $xclrValue = substr($extData, 4, 4);
2461 if ($xclfType == 2) {
2462 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2465 if (isset($this->mapCellXfIndex[$ixfe])) {
2466 $top = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getTop();
2467 $top->getColor()->setRGB($rgb);
2468 $top->colorIndex = null;
2474 $xclfType = self::getUInt2d($extData, 0);
2475 $xclrValue = substr($extData, 4, 4);
2477 if ($xclfType == 2) {
2478 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2481 if (isset($this->mapCellXfIndex[$ixfe])) {
2482 $bottom = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getBottom();
2483 $bottom->getColor()->setRGB($rgb);
2484 $bottom->colorIndex = null;
2490 $xclfType = self::getUInt2d($extData, 0);
2491 $xclrValue = substr($extData, 4, 4);
2493 if ($xclfType == 2) {
2494 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2497 if (isset($this->mapCellXfIndex[$ixfe])) {
2498 $left = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getLeft();
2499 $left->getColor()->setRGB($rgb);
2500 $left->colorIndex = null;
2506 $xclfType = self::getUInt2d($extData, 0);
2507 $xclrValue = substr($extData, 4, 4);
2509 if ($xclfType == 2) {
2510 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2513 if (isset($this->mapCellXfIndex[$ixfe])) {
2514 $right = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getRight();
2515 $right->getColor()->setRGB($rgb);
2516 $right->colorIndex = null;
2522 $xclfType = self::getUInt2d($extData, 0);
2523 $xclrValue = substr($extData, 4, 4);
2525 if ($xclfType == 2) {
2526 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2529 if (isset($this->mapCellXfIndex[$ixfe])) {
2530 $diagonal = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getBorders()->getDiagonal();
2531 $diagonal->getColor()->setRGB($rgb);
2532 $diagonal->colorIndex = null;
2538 $xclfType = self::getUInt2d($extData, 0);
2539 $xclrValue = substr($extData, 4, 4);
2541 if ($xclfType == 2) {
2542 $rgb = sprintf(
'%02X%02X%02X', ord($xclrValue[0]), ord($xclrValue[1]), ord($xclrValue[2]));
2545 if (isset($this->mapCellXfIndex[$ixfe])) {
2546 $font = $this->spreadsheet->getCellXfByIndex($this->mapCellXfIndex[$ixfe])->getFont();
2547 $font->getColor()->setRGB($rgb);
2548 $font->colorIndex = null;
2565 $length = self::getUInt2d($this->
data, $this->pos + 2);
2569 $this->pos += 4 + $length;
2571 if (!$this->readDataOnly) {
2573 $ixfe = self::getUInt2d($recordData, 0);
2579 $isBuiltIn = (bool) ((0x8000 & $ixfe) >> 15);
2583 $builtInId = ord($recordData[2]);
2585 switch ($builtInId) {
2602 $length = self::getUInt2d($this->
data, $this->pos + 2);
2606 $this->pos += 4 + $length;
2608 if (!$this->readDataOnly) {
2610 $nm = self::getUInt2d($recordData, 0);
2613 for (
$i = 0;
$i < $nm; ++
$i) {
2614 $rgb = substr($recordData, 2 + 4 *
$i, 4);
2615 $this->palette[] = self::readRGB($rgb);
2634 $length = self::getUInt2d($this->
data, $this->pos + 2);
2639 $rec_offset = self::getInt4d($this->
data, $this->pos + 4);
2642 $this->pos += 4 + $length;
2645 switch (ord($recordData[4])) {
2665 $sheetType = ord($recordData[5]);
2669 if ($this->version == self::XLS_BIFF8) {
2670 $string = self::readUnicodeStringShort(substr($recordData, 6));
2671 $rec_name = $string[
'value'];
2672 } elseif ($this->version == self::XLS_BIFF7) {
2674 $rec_name = $string[
'value'];
2678 'name' => $rec_name,
2679 'offset' => $rec_offset,
2680 'sheetState' => $sheetState,
2681 'sheetType' => $sheetType,
2690 $length = self::getUInt2d($this->
data, $this->pos + 2);
2694 $this->pos += 4 + $length;
2700 if (strlen($recordData) > 4) {
2703 $nm = self::getUInt2d($recordData, 0);
2707 $encodedUrlString = self::readUnicodeStringLong(substr($recordData, 2));
2708 $offset += $encodedUrlString[
'size'];
2711 $externalSheetNames = [];
2712 for (
$i = 0;
$i < $nm; ++
$i) {
2713 $externalSheetNameString = self::readUnicodeStringLong(substr($recordData, $offset));
2714 $externalSheetNames[] = $externalSheetNameString[
'value'];
2715 $offset += $externalSheetNameString[
'size'];
2719 $this->externalBooks[] = [
2720 'type' =>
'external',
2721 'encodedUrl' => $encodedUrlString[
'value'],
2722 'externalSheetNames' => $externalSheetNames,
2724 } elseif (substr($recordData, 2, 2) == pack(
'CC', 0x01, 0x04)) {
2728 $this->externalBooks[] = [
2729 'type' =>
'internal',
2731 } elseif (substr($recordData, 0, 4) == pack(
'vCC', 0x0001, 0x01, 0x3A)) {
2734 $this->externalBooks[] = [
2735 'type' =>
'addInFunction',
2737 } elseif (substr($recordData, 0, 2) == pack(
'v', 0x0000)) {
2741 $this->externalBooks[] = [
2742 'type' =>
'DDEorOLE',
2752 $length = self::getUInt2d($this->
data, $this->pos + 2);
2756 $this->pos += 4 + $length;
2759 if ($this->version == self::XLS_BIFF8) {
2761 $options = self::getUInt2d($recordData, 0);
2768 $nameString = self::readUnicodeStringShort(substr($recordData, 6));
2771 $offset = 6 + $nameString[
'size'];
2774 $this->externalNames[] = [
2775 'name' => $nameString[
'value'],
2776 'formula' => $formula,
2786 $length = self::getUInt2d($this->
data, $this->pos + 2);
2790 $this->pos += 4 + $length;
2793 if ($this->version == self::XLS_BIFF8) {
2795 $nm = self::getUInt2d($recordData, 0);
2796 for (
$i = 0;
$i < $nm; ++
$i) {
2799 'externalBookIndex' => self::getUInt2d($recordData, 2 + 6 *
$i),
2801 'firstSheetIndex' => self::getUInt2d($recordData, 4 + 6 *
$i),
2803 'lastSheetIndex' => self::getUInt2d($recordData, 6 + 6 *
$i),
2822 $length = self::getUInt2d($this->
data, $this->pos + 2);
2826 $this->pos += 4 + $length;
2828 if ($this->version == self::XLS_BIFF8) {
2832 $opts = self::getUInt2d($recordData, 0);
2835 $isBuiltInName = (0x0020 & $opts) >> 5;
2840 $nlen = ord($recordData[3]);
2844 $flen = self::getUInt2d($recordData, 4);
2847 $scope = self::getUInt2d($recordData, 8);
2850 $string = self::readUnicodeString(substr($recordData, 14), $nlen);
2853 $offset = 14 + $string[
'size'];
2854 $formulaStructure = pack(
'v', $flen) . substr($recordData, $offset);
2862 $this->definedname[] = [
2863 'isBuiltInName' => $isBuiltInName,
2864 'name' => $string[
'value'],
2865 'formula' => $formula,
2876 $length = self::getUInt2d($this->
data, $this->pos + 2);
2880 $recordData = $splicedRecordData[
'recordData'];
2882 $this->drawingGroupData .= $recordData;
2907 $recordData = $splicedRecordData[
'recordData'];
2908 $spliceOffsets = $splicedRecordData[
'spliceOffsets'];
2914 $nm = self::getInt4d($recordData, 4);
2918 foreach ($spliceOffsets as $spliceOffset) {
2921 if (
$pos <= $spliceOffset) {
2922 $limitposSST = $spliceOffset;
2927 for (
$i = 0;
$i < $nm &&
$pos < $limitposSST; ++
$i) {
2929 $numChars = self::getUInt2d($recordData,
$pos);
2933 $optionFlags = ord($recordData[
$pos]);
2937 $isCompressed = (($optionFlags & 0x01) == 0);
2940 $hasAsian = (($optionFlags & 0x04) != 0);
2943 $hasRichText = (($optionFlags & 0x08) != 0);
2945 $formattingRuns = 0;
2948 $formattingRuns = self::getUInt2d($recordData, $pos);
2952 $extendedRunLength = 0;
2955 $extendedRunLength = self::getInt4d($recordData, $pos);
2960 $len = ($isCompressed) ? $numChars : $numChars * 2;
2964 foreach ($spliceOffsets as $spliceOffset) {
2967 if ($pos <= $spliceOffset) {
2968 $limitpos = $spliceOffset;
2974 if ($pos + $len <= $limitpos) {
2977 $retstr = substr($recordData, $pos, $len);
2983 $retstr = substr($recordData, $pos, $limitpos - $pos);
2985 $bytesRead = $limitpos -
$pos;
2988 $charsLeft = $numChars - (($isCompressed) ? $bytesRead : ($bytesRead / 2));
2993 while ($charsLeft > 0) {
2995 foreach ($spliceOffsets as $spliceOffset) {
2996 if ($pos < $spliceOffset) {
2997 $limitpos = $spliceOffset;
3005 $option = ord($recordData[$pos]);
3008 if ($isCompressed && ($option == 0)) {
3011 $len = min($charsLeft, $limitpos - $pos);
3012 $retstr .= substr($recordData, $pos, $len);
3014 $isCompressed =
true;
3015 } elseif (!$isCompressed && ($option != 0)) {
3018 $len = min($charsLeft * 2, $limitpos - $pos);
3019 $retstr .= substr($recordData, $pos, $len);
3020 $charsLeft -= $len / 2;
3021 $isCompressed =
false;
3022 } elseif (!$isCompressed && ($option == 0)) {
3025 $len = min($charsLeft, $limitpos - $pos);
3026 for ($j = 0; $j < $len; ++$j) {
3027 $retstr .= $recordData[$pos + $j]
3031 $isCompressed =
false;
3036 $jMax = strlen($retstr);
3037 for ($j = 0; $j < $jMax; ++$j) {
3038 $newstr .= $retstr[$j] . chr(0);
3041 $len = min($charsLeft * 2, $limitpos - $pos);
3042 $retstr .= substr($recordData, $pos, $len);
3043 $charsLeft -= $len / 2;
3044 $isCompressed =
false;
3052 $retstr = self::encodeUTF16($retstr, $isCompressed);
3058 for ($j = 0; $j < $formattingRuns; ++$j) {
3060 $charPos = self::getUInt2d($recordData, $pos + $j * 4);
3063 $fontIndex = self::getUInt2d($recordData, $pos + 2 + $j * 4);
3066 'charPos' => $charPos,
3067 'fontIndex' => $fontIndex,
3070 $pos += 4 * $formattingRuns;
3076 $pos += $extendedRunLength;
3082 'fmtRuns' => $fmtRuns,
3094 $length = self::getUInt2d($this->
data, $this->pos + 2);
3098 $this->pos += 4 + $length;
3100 if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3102 $printGridlines = (bool) self::getUInt2d($recordData, 0);
3103 $this->phpSheet->setPrintGridlines($printGridlines);
3112 $length = self::getUInt2d($this->
data, $this->pos + 2);
3116 $this->pos += 4 + $length;
3120 $height = self::getUInt2d($recordData, 2);
3121 $this->phpSheet->getDefaultRowDimension()->setRowHeight($height / 20);
3129 $length = self::getUInt2d($this->
data, $this->pos + 2);
3133 $this->pos += 4 + $length;
3138 $isSummaryBelow = (0x0040 & self::getUInt2d($recordData, 0)) >> 6;
3139 $this->phpSheet->setShowSummaryBelow($isSummaryBelow);
3142 $isSummaryRight = (0x0080 & self::getUInt2d($recordData, 0)) >> 7;
3143 $this->phpSheet->setShowSummaryRight($isSummaryRight);
3147 $this->isFitToPages = (bool) ((0x0100 & self::getUInt2d($recordData, 0)) >> 8);
3155 $length = self::getUInt2d($this->
data, $this->pos + 2);
3159 $this->pos += 4 + $length;
3161 if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3163 $nm = self::getUInt2d($recordData, 0);
3166 for (
$i = 0;
$i < $nm; ++
$i) {
3167 $r = self::getUInt2d($recordData, 2 + 6 *
$i);
3168 $cf = self::getUInt2d($recordData, 2 + 6 *
$i + 2);
3169 $cl = self::getUInt2d($recordData, 2 + 6 *
$i + 4);
3182 $length = self::getUInt2d($this->
data, $this->pos + 2);
3186 $this->pos += 4 + $length;
3188 if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
3190 $nm = self::getUInt2d($recordData, 0);
3193 for (
$i = 0;
$i < $nm; ++
$i) {
3194 $c = self::getUInt2d($recordData, 2 + 6 *
$i);
3195 $rf = self::getUInt2d($recordData, 2 + 6 *
$i + 2);
3196 $rl = self::getUInt2d($recordData, 2 + 6 *
$i + 4);
3209 $length = self::getUInt2d($this->
data, $this->pos + 2);
3213 $this->pos += 4 + $length;
3215 if (!$this->readDataOnly) {
3219 if ($this->version == self::XLS_BIFF8) {
3220 $string = self::readUnicodeStringLong($recordData);
3225 $this->phpSheet->getHeaderFooter()->setOddHeader($string[
'value']);
3226 $this->phpSheet->getHeaderFooter()->setEvenHeader($string[
'value']);
3236 $length = self::getUInt2d($this->
data, $this->pos + 2);
3240 $this->pos += 4 + $length;
3242 if (!$this->readDataOnly) {
3246 if ($this->version == self::XLS_BIFF8) {
3247 $string = self::readUnicodeStringLong($recordData);
3251 $this->phpSheet->getHeaderFooter()->setOddFooter($string[
'value']);
3252 $this->phpSheet->getHeaderFooter()->setEvenFooter($string[
'value']);
3262 $length = self::getUInt2d($this->
data, $this->pos + 2);
3266 $this->pos += 4 + $length;
3268 if (!$this->readDataOnly) {
3270 $isHorizontalCentered = (bool) self::getUInt2d($recordData, 0);
3272 $this->phpSheet->getPageSetup()->setHorizontalCentered($isHorizontalCentered);
3281 $length = self::getUInt2d($this->
data, $this->pos + 2);
3285 $this->pos += 4 + $length;
3287 if (!$this->readDataOnly) {
3289 $isVerticalCentered = (bool) self::getUInt2d($recordData, 0);
3291 $this->phpSheet->getPageSetup()->setVerticalCentered($isVerticalCentered);
3300 $length = self::getUInt2d($this->
data, $this->pos + 2);
3304 $this->pos += 4 + $length;
3306 if (!$this->readDataOnly) {
3308 $this->phpSheet->getPageMargins()->setLeft(self::extractNumber($recordData));
3317 $length = self::getUInt2d($this->
data, $this->pos + 2);
3321 $this->pos += 4 + $length;
3323 if (!$this->readDataOnly) {
3325 $this->phpSheet->getPageMargins()->setRight(self::extractNumber($recordData));
3334 $length = self::getUInt2d($this->
data, $this->pos + 2);
3338 $this->pos += 4 + $length;
3340 if (!$this->readDataOnly) {
3342 $this->phpSheet->getPageMargins()->setTop(self::extractNumber($recordData));
3351 $length = self::getUInt2d($this->
data, $this->pos + 2);
3355 $this->pos += 4 + $length;
3357 if (!$this->readDataOnly) {
3359 $this->phpSheet->getPageMargins()->setBottom(self::extractNumber($recordData));
3368 $length = self::getUInt2d($this->
data, $this->pos + 2);
3372 $this->pos += 4 + $length;
3374 if (!$this->readDataOnly) {
3376 $paperSize = self::getUInt2d($recordData, 0);
3379 $scale = self::getUInt2d($recordData, 2);
3382 $fitToWidth = self::getUInt2d($recordData, 6);
3385 $fitToHeight = self::getUInt2d($recordData, 8);
3390 $isOverThenDown = (0x0001 & self::getUInt2d($recordData, 10));
3393 $isPortrait = (0x0002 & self::getUInt2d($recordData, 10)) >> 1;
3397 $isNotInit = (0x0004 & self::getUInt2d($recordData, 10)) >> 2;
3400 $this->phpSheet->getPageSetup()->setPaperSize($paperSize);
3401 $this->phpSheet->getPageSetup()->setPageOrder(((
bool) $isOverThenDown) ? PageSetup::PAGEORDER_OVER_THEN_DOWN : PageSetup::PAGEORDER_DOWN_THEN_OVER);
3402 $this->phpSheet->getPageSetup()->setOrientation(((
bool) $isPortrait) ? PageSetup::ORIENTATION_PORTRAIT : PageSetup::ORIENTATION_LANDSCAPE);
3404 $this->phpSheet->getPageSetup()->setScale($scale,
false);
3405 $this->phpSheet->getPageSetup()->setFitToPage((
bool) $this->isFitToPages);
3406 $this->phpSheet->getPageSetup()->setFitToWidth($fitToWidth,
false);
3407 $this->phpSheet->getPageSetup()->setFitToHeight($fitToHeight,
false);
3411 $marginHeader = self::extractNumber(substr($recordData, 16, 8));
3412 $this->phpSheet->getPageMargins()->setHeader($marginHeader);
3415 $marginFooter = self::extractNumber(substr($recordData, 24, 8));
3416 $this->phpSheet->getPageMargins()->setFooter($marginFooter);
3426 $length = self::getUInt2d($this->
data, $this->pos + 2);
3430 $this->pos += 4 + $length;
3432 if ($this->readDataOnly) {
3439 $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
3440 $this->phpSheet->getProtection()->setSheet((
bool) $bool);
3448 $length = self::getUInt2d($this->
data, $this->pos + 2);
3452 $this->pos += 4 + $length;
3454 if ($this->readDataOnly) {
3461 $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
3463 $this->phpSheet->getProtection()->setScenarios((
bool) $bool);
3471 $length = self::getUInt2d($this->
data, $this->pos + 2);
3475 $this->pos += 4 + $length;
3477 if ($this->readDataOnly) {
3484 $bool = (0x01 & self::getUInt2d($recordData, 0)) >> 0;
3486 $this->phpSheet->getProtection()->setObjects((
bool) $bool);
3494 $length = self::getUInt2d($this->
data, $this->pos + 2);
3498 $this->pos += 4 + $length;
3500 if (!$this->readDataOnly) {
3502 $password = strtoupper(dechex(self::getUInt2d($recordData, 0)));
3503 $this->phpSheet->getProtection()->setPassword(
$password,
true);
3512 $length = self::getUInt2d($this->
data, $this->pos + 2);
3516 $this->pos += 4 + $length;
3519 $width = self::getUInt2d($recordData, 0);
3521 $this->phpSheet->getDefaultColumnDimension()->setWidth($width);
3530 $length = self::getUInt2d($this->
data, $this->pos + 2);
3534 $this->pos += 4 + $length;
3536 if (!$this->readDataOnly) {
3538 $firstColumnIndex = self::getUInt2d($recordData, 0);
3541 $lastColumnIndex = self::getUInt2d($recordData, 2);
3544 $width = self::getUInt2d($recordData, 4);
3547 $xfIndex = self::getUInt2d($recordData, 6);
3551 $isHidden = (0x0001 & self::getUInt2d($recordData, 8)) >> 0;
3554 $level = (0x0700 & self::getUInt2d($recordData, 8)) >> 8;
3557 $isCollapsed = (bool) ((0x1000 & self::getUInt2d($recordData, 8)) >> 12);
3561 for (
$i = $firstColumnIndex + 1;
$i <= $lastColumnIndex + 1; ++
$i) {
3562 if ($lastColumnIndex == 255 || $lastColumnIndex == 256) {
3563 $this->phpSheet->getDefaultColumnDimension()->setWidth($width / 256);
3567 $this->phpSheet->getColumnDimensionByColumn(
$i)->setWidth($width / 256);
3568 $this->phpSheet->getColumnDimensionByColumn(
$i)->setVisible(!$isHidden);
3569 $this->phpSheet->getColumnDimensionByColumn(
$i)->setOutlineLevel($level);
3570 $this->phpSheet->getColumnDimensionByColumn(
$i)->setCollapsed($isCollapsed);
3571 if (isset($this->mapCellXfIndex[
$xfIndex])) {
3572 $this->phpSheet->getColumnDimensionByColumn(
$i)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3590 $length = self::getUInt2d($this->
data, $this->pos + 2);
3594 $this->pos += 4 + $length;
3596 if (!$this->readDataOnly) {
3598 $r = self::getUInt2d($recordData, 0);
3607 $height = (0x7FFF & self::getUInt2d($recordData, 6)) >> 0;
3610 $useDefaultHeight = (0x8000 & self::getUInt2d($recordData, 6)) >> 15;
3612 if (!$useDefaultHeight) {
3613 $this->phpSheet->getRowDimension(
$r + 1)->setRowHeight($height / 20);
3623 $level = (0x00000007 & self::getInt4d($recordData, 12)) >> 0;
3624 $this->phpSheet->getRowDimension(
$r + 1)->setOutlineLevel($level);
3627 $isCollapsed = (bool) ((0x00000010 & self::getInt4d($recordData, 12)) >> 4);
3628 $this->phpSheet->getRowDimension(
$r + 1)->setCollapsed($isCollapsed);
3631 $isHidden = (0x00000020 & self::getInt4d($recordData, 12)) >> 5;
3632 $this->phpSheet->getRowDimension(
$r + 1)->setVisible(!$isHidden);
3635 $hasExplicitFormat = (0x00000080 & self::getInt4d($recordData, 12)) >> 7;
3638 $xfIndex = (0x0FFF0000 & self::getInt4d($recordData, 12)) >> 16;
3640 if ($hasExplicitFormat && isset($this->mapCellXfIndex[
$xfIndex])) {
3641 $this->phpSheet->getRowDimension(
$r + 1)->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3659 $length = self::getUInt2d($this->
data, $this->pos + 2);
3663 $this->pos += 4 + $length;
3666 $row = self::getUInt2d($recordData, 0);
3669 $column = self::getUInt2d($recordData, 2);
3675 $xfIndex = self::getUInt2d($recordData, 4);
3678 $rknum = self::getInt4d($recordData, 6);
3679 $numValue = self::getIEEE754($rknum);
3681 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
3682 if (!$this->readDataOnly && isset($this->mapCellXfIndex[
$xfIndex])) {
3684 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3703 $length = self::getUInt2d($this->
data, $this->pos + 2);
3707 $this->pos += 4 + $length;
3710 $row = self::getUInt2d($recordData, 0);
3713 $column = self::getUInt2d($recordData, 2);
3720 $xfIndex = self::getUInt2d($recordData, 4);
3723 $index = self::getInt4d($recordData, 6);
3726 if (($fmtRuns = $this->sst[
$index][
'fmtRuns']) && !$this->readDataOnly) {
3730 $sstCount = count($this->sst[$index][
'fmtRuns']);
3731 for (
$i = 0;
$i <= $sstCount; ++
$i) {
3732 if (isset($fmtRuns[
$i])) {
3734 $charPos = $fmtRuns[
$i][
'charPos'];
3741 $richText->createText(
$text);
3743 $textRun = $richText->createTextRun(
$text);
3744 if (isset($fmtRuns[$i - 1])) {
3745 if ($fmtRuns[$i - 1][
'fontIndex'] < 4) {
3746 $fontIndex = $fmtRuns[$i - 1][
'fontIndex'];
3750 $fontIndex = $fmtRuns[$i - 1][
'fontIndex'] - 1;
3752 $textRun->setFont(clone $this->objFonts[$fontIndex]);
3757 if ($this->readEmptyCells || trim($richText->getPlainText()) !==
'') {
3758 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
3763 if ($this->readEmptyCells || trim($this->sst[$index][
'value']) !==
'') {
3764 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
3770 if (!$this->readDataOnly && !$emptyCell && isset($this->mapCellXfIndex[
$xfIndex])) {
3772 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3787 $length = self::getUInt2d($this->
data, $this->pos + 2);
3791 $this->pos += 4 + $length;
3794 $row = self::getUInt2d($recordData, 0);
3797 $colFirst = self::getUInt2d($recordData, 2);
3800 $colLast = self::getUInt2d($recordData, $length - 2);
3801 $columns = $colLast - $colFirst + 1;
3812 $xfIndex = self::getUInt2d($recordData, $offset);
3815 $numValue = self::getIEEE754(self::getInt4d($recordData, $offset + 2));
3816 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
3817 if (!$this->readDataOnly && isset($this->mapCellXfIndex[
$xfIndex])) {
3819 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3840 $length = self::getUInt2d($this->
data, $this->pos + 2);
3844 $this->pos += 4 + $length;
3847 $row = self::getUInt2d($recordData, 0);
3850 $column = self::getUInt2d($recordData, 2);
3856 $xfIndex = self::getUInt2d($recordData, 4);
3858 $numValue = self::extractNumber(substr($recordData, 6, 8));
3860 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
3861 if (!$this->readDataOnly && isset($this->mapCellXfIndex[
$xfIndex])) {
3863 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3881 $length = self::getUInt2d($this->
data, $this->pos + 2);
3885 $this->pos += 4 + $length;
3888 $row = self::getUInt2d($recordData, 0);
3891 $column = self::getUInt2d($recordData, 2);
3895 $formulaStructure = substr($recordData, 20);
3898 $options = self::getUInt2d($recordData, 14);
3903 $isPartOfSharedFormula = (bool) (0x0008 &
$options);
3909 $isPartOfSharedFormula = $isPartOfSharedFormula && ord($formulaStructure[2]) == 0x01;
3911 if ($isPartOfSharedFormula) {
3914 $baseRow = self::getUInt2d($formulaStructure, 3);
3915 $baseCol = self::getUInt2d($formulaStructure, 5);
3921 if ($isPartOfSharedFormula) {
3923 $this->sharedFormulaParts[$columnString . (
$row + 1)] = $this->baseCell;
3929 $xfIndex = self::getUInt2d($recordData, 4);
3932 if ((ord($recordData[6]) == 0) && (ord($recordData[12]) == 255) && (ord($recordData[13]) == 255)) {
3937 $code = self::getUInt2d($this->
data, $this->pos);
3938 if (
$code == self::XLS_TYPE_SHAREDFMLA) {
3945 (ord($recordData[6]) == 1)
3946 && (ord($recordData[12]) == 255)
3947 && (ord($recordData[13]) == 255)
3951 $value = (bool) ord($recordData[8]);
3953 (ord($recordData[6]) == 2)
3954 && (ord($recordData[12]) == 255)
3955 && (ord($recordData[13]) == 255)
3961 (ord($recordData[6]) == 3)
3962 && (ord($recordData[12]) == 255)
3963 && (ord($recordData[13]) == 255)
3971 $value = self::extractNumber(substr($recordData, 6, 8));
3974 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
3975 if (!$this->readDataOnly && isset($this->mapCellXfIndex[$xfIndex])) {
3977 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
3981 if (!$isPartOfSharedFormula) {
3985 if ($this->version != self::XLS_BIFF8) {
3986 throw new Exception(
'Not BIFF8. Can only read BIFF8 formulas');
3991 $cell->setValueExplicit($value, $dataType);
3994 if ($this->version == self::XLS_BIFF8) {
3997 $cell->setValueExplicit($value, $dataType);
4002 $cell->setCalculatedValue($value);
4013 $length = self::getUInt2d($this->
data, $this->pos + 2);
4017 $this->pos += 4 + $length;
4020 $cellRange = substr($recordData, 0, 6);
4026 $no = ord($recordData[7]);
4029 $formula = substr($recordData, 8);
4044 $length = self::getUInt2d($this->
data, $this->pos + 2);
4048 $this->pos += 4 + $length;
4050 if ($this->version == self::XLS_BIFF8) {
4051 $string = self::readUnicodeStringLong($recordData);
4052 $value = $string[
'value'];
4055 $value = $string[
'value'];
4071 $length = self::getUInt2d($this->
data, $this->pos + 2);
4075 $this->pos += 4 + $length;
4078 $row = self::getUInt2d($recordData, 0);
4081 $column = self::getUInt2d($recordData, 2);
4087 $xfIndex = self::getUInt2d($recordData, 4);
4090 $boolErr = ord($recordData[6]);
4093 $isError = ord($recordData[7]);
4095 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
4098 $value = (bool) $boolErr;
4113 if (!$this->readDataOnly && isset($this->mapCellXfIndex[
$xfIndex])) {
4115 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4130 $length = self::getUInt2d($this->
data, $this->pos + 2);
4134 $this->pos += 4 + $length;
4137 $row = self::getUInt2d($recordData, 0);
4140 $fc = self::getUInt2d($recordData, 2);
4144 if (!$this->readDataOnly && $this->readEmptyCells) {
4145 for (
$i = 0;
$i < $length / 2 - 3; ++
$i) {
4150 $xfIndex = self::getUInt2d($recordData, 4 + 2 *
$i);
4151 if (isset($this->mapCellXfIndex[
$xfIndex])) {
4152 $this->phpSheet->getCell($columnString . (
$row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4173 $length = self::getUInt2d($this->
data, $this->pos + 2);
4177 $this->pos += 4 + $length;
4180 $row = self::getUInt2d($recordData, 0);
4183 $column = self::getUInt2d($recordData, 2);
4189 $xfIndex = self::getUInt2d($recordData, 4);
4193 if ($this->version == self::XLS_BIFF8) {
4194 $string = self::readUnicodeStringLong(substr($recordData, 6));
4195 $value = $string[
'value'];
4198 $value = $string[
'value'];
4200 if ($this->readEmptyCells || trim($value) !==
'') {
4201 $cell = $this->phpSheet->getCell($columnString . (
$row + 1));
4204 if (!$this->readDataOnly && isset($this->mapCellXfIndex[
$xfIndex])) {
4206 $cell->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4217 $length = self::getUInt2d($this->
data, $this->pos + 2);
4221 $this->pos += 4 + $length;
4224 $row = self::getUInt2d($recordData, 0);
4227 $col = self::getUInt2d($recordData, 2);
4233 $xfIndex = self::getUInt2d($recordData, 4);
4236 if (!$this->readDataOnly && $this->readEmptyCells && isset($this->mapCellXfIndex[
$xfIndex])) {
4237 $this->phpSheet->getCell($columnString . (
$row + 1))->setXfIndex($this->mapCellXfIndex[$xfIndex]);
4247 $length = self::getUInt2d($this->
data, $this->pos + 2);
4251 $recordData = $splicedRecordData[
'recordData'];
4253 $this->drawingData .= $recordData;
4261 $length = self::getUInt2d($this->
data, $this->pos + 2);
4265 $this->pos += 4 + $length;
4267 if ($this->readDataOnly || $this->version != self::XLS_BIFF8) {
4280 $ftCmoType = self::getUInt2d($recordData, 0);
4281 $cbCmoSize = self::getUInt2d($recordData, 2);
4282 $otObjType = self::getUInt2d($recordData, 4);
4283 $idObjID = self::getUInt2d($recordData, 6);
4284 $grbitOpts = self::getUInt2d($recordData, 6);
4287 'ftCmoType' => $ftCmoType,
4288 'cbCmoSize' => $cbCmoSize,
4289 'otObjType' => $otObjType,
4290 'idObjID' => $idObjID,
4291 'grbitOpts' => $grbitOpts,
4293 $this->textObjRef = $idObjID;
4301 $length = self::getUInt2d($this->
data, $this->pos + 2);
4305 $this->pos += 4 + $length;
4308 $options = self::getUInt2d($recordData, 0);
4311 $firstVisibleRow = self::getUInt2d($recordData, 2);
4314 $firstVisibleColumn = self::getUInt2d($recordData, 4);
4315 $zoomscaleInPageBreakPreview = 0;
4316 $zoomscaleInNormalView = 0;
4317 if ($this->version === self::XLS_BIFF8) {
4322 if (!isset($recordData[10])) {
4323 $zoomscaleInPageBreakPreview = 0;
4325 $zoomscaleInPageBreakPreview = self::getUInt2d($recordData, 10);
4328 if ($zoomscaleInPageBreakPreview === 0) {
4329 $zoomscaleInPageBreakPreview = 60;
4332 if (!isset($recordData[12])) {
4333 $zoomscaleInNormalView = 0;
4335 $zoomscaleInNormalView = self::getUInt2d($recordData, 12);
4338 if ($zoomscaleInNormalView === 0) {
4339 $zoomscaleInNormalView = 100;
4344 $showGridlines = (bool) ((0x0002 &
$options) >> 1);
4345 $this->phpSheet->setShowGridlines($showGridlines);
4348 $showRowColHeaders = (bool) ((0x0004 & $options) >> 2);
4349 $this->phpSheet->setShowRowColHeaders($showRowColHeaders);
4352 $this->frozen = (bool) ((0x0008 & $options) >> 3);
4355 $this->phpSheet->setRightToLeft((
bool) ((0x0040 & $options) >> 6));
4358 $isActive = (bool) ((0x0400 & $options) >> 10);
4360 $this->spreadsheet->setActiveSheetIndex($this->spreadsheet->getIndex($this->phpSheet));
4364 $isPageBreakPreview = (bool) ((0x0800 & $options) >> 11);
4371 $this->phpSheet->getSheetView()->setView($view);
4372 if ($this->version === self::XLS_BIFF8) {
4373 $zoomScale = $isPageBreakPreview ? $zoomscaleInPageBreakPreview : $zoomscaleInNormalView;
4374 $this->phpSheet->getSheetView()->setZoomScale($zoomScale);
4375 $this->phpSheet->getSheetView()->setZoomScaleNormal($zoomscaleInNormalView);
4385 $length = self::getUInt2d($this->
data, $this->pos + 2);
4389 $this->pos += 4 + $length;
4393 $rt = self::getUInt2d($recordData, 0);
4396 $grbitFrt = self::getUInt2d($recordData, 2);
4401 $wScalePLV = self::getUInt2d($recordData, 12);
4403 $grbit = self::getUInt2d($recordData, 14);
4406 $fPageLayoutView = $grbit & 0x01;
4407 $fRulerVisible = ($grbit >> 1) & 0x01;
4408 $fWhitespaceHidden = ($grbit >> 3) & 0x01;
4410 if ($fPageLayoutView === 1) {
4412 $this->phpSheet->getSheetView()->setZoomScale($wScalePLV);
4422 $length = self::getUInt2d($this->
data, $this->pos + 2);
4426 $this->pos += 4 + $length;
4429 $numerator = self::getUInt2d($recordData, 0);
4432 $denumerator = self::getUInt2d($recordData, 2);
4435 $this->phpSheet->getSheetView()->setZoomScale($numerator * 100 / $denumerator);
4443 $length = self::getUInt2d($this->
data, $this->pos + 2);
4447 $this->pos += 4 + $length;
4449 if (!$this->readDataOnly) {
4451 $px = self::getUInt2d($recordData, 0);
4454 $py = self::getUInt2d($recordData, 2);
4457 $rwTop = self::getUInt2d($recordData, 4);
4460 $colLeft = self::getUInt2d($recordData, 6);
4462 if ($this->frozen) {
4466 $this->phpSheet->freezePane($cell, $topLeftCell);
4477 $length = self::getUInt2d($this->
data, $this->pos + 2);
4481 $this->pos += 4 + $length;
4483 if (!$this->readDataOnly) {
4485 $paneId = ord($recordData[0]);
4488 $r = self::getUInt2d($recordData, 1);
4491 $c = self::getUInt2d($recordData, 3);
4495 $index = self::getUInt2d($recordData, 5);
4498 $data = substr($recordData, 7);
4501 $selectedCells = $cellRangeAddressList[
'cellRangeAddresses'][0];
4504 if (preg_match(
'/^([A-Z]+1\:[A-Z]+)16384$/', $selectedCells)) {
4505 $selectedCells = preg_replace(
'/^([A-Z]+1\:[A-Z]+)16384$/',
'${1}1048576', $selectedCells);
4509 if (preg_match(
'/^([A-Z]+1\:[A-Z]+)65536$/', $selectedCells)) {
4510 $selectedCells = preg_replace(
'/^([A-Z]+1\:[A-Z]+)65536$/',
'${1}1048576', $selectedCells);
4514 if (preg_match(
'/^(A\d+\:)IV(\d+)$/', $selectedCells)) {
4515 $selectedCells = preg_replace(
'/^(A\d+\:)IV(\d+)$/',
'${1}XFD${2}', $selectedCells);
4518 $this->phpSheet->setSelectedCells($selectedCells);
4524 $includeCellRange =
true;
4526 $includeCellRange =
false;
4528 ++$rangeBoundaries[1][0];
4529 for (
$row = $rangeBoundaries[0][1];
$row <= $rangeBoundaries[1][1]; ++
$row) {
4530 for ($column = $rangeBoundaries[0][0]; $column != $rangeBoundaries[1][0]; ++$column) {
4531 if ($this->
getReadFilter()->readCell($column,
$row, $this->phpSheet->getTitle())) {
4532 $includeCellRange =
true;
4540 return $includeCellRange;
4554 $length = self::getUInt2d($this->
data, $this->pos + 2);
4558 $this->pos += 4 + $length;
4560 if ($this->version == self::XLS_BIFF8 && !$this->readDataOnly) {
4562 foreach ($cellRangeAddressList[
'cellRangeAddresses'] as $cellRangeAddress) {
4564 (strpos($cellRangeAddress,
':') !==
false) &&
4567 $this->phpSheet->mergeCells($cellRangeAddress);
4578 $length = self::getUInt2d($this->
data, $this->pos + 2);
4582 $this->pos += 4 + $length;
4584 if (!$this->readDataOnly) {
4598 $isFileLinkOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 0;
4601 $isAbsPathOrUrl = (0x00000001 & self::getUInt2d($recordData, 28)) >> 1;
4604 $hasDesc = (0x00000014 & self::getUInt2d($recordData, 28)) >> 2;
4607 $hasText = (0x00000008 & self::getUInt2d($recordData, 28)) >> 3;
4610 $hasFrame = (0x00000080 & self::getUInt2d($recordData, 28)) >> 7;
4613 $isUNC = (0x00000100 & self::getUInt2d($recordData, 28)) >> 8;
4620 $dl = self::getInt4d($recordData, 32);
4622 $desc = self::encodeUTF16(substr($recordData, 36, 2 * ($dl - 1)),
false);
4623 $offset += 4 + 2 * $dl;
4626 $fl = self::getInt4d($recordData, $offset);
4627 $offset += 4 + 2 * $fl;
4631 $hyperlinkType = null;
4634 $hyperlinkType =
'UNC';
4635 } elseif (!$isFileLinkOrUrl) {
4636 $hyperlinkType =
'workbook';
4637 } elseif (ord($recordData[$offset]) == 0x03) {
4638 $hyperlinkType =
'local';
4639 } elseif (ord($recordData[$offset]) == 0xE0) {
4640 $hyperlinkType =
'URL';
4643 switch ($hyperlinkType) {
4651 $us = self::getInt4d($recordData, $offset);
4654 $url = self::encodeUTF16(substr($recordData, $offset, $us - 2),
false);
4655 $nullOffset = strpos(
$url, chr(0x00));
4657 $url = substr(
$url, 0, $nullOffset);
4659 $url .= $hasText ?
'#' :
'';
4673 $upLevelCount = self::getUInt2d($recordData, $offset);
4677 $sl = self::getInt4d($recordData, $offset);
4681 $shortenedFilePath = substr($recordData, $offset, $sl);
4682 $shortenedFilePath = self::encodeUTF16($shortenedFilePath,
true);
4683 $shortenedFilePath = substr($shortenedFilePath, 0, -1);
4692 $sz = self::getInt4d($recordData, $offset);
4698 $xl = self::getInt4d($recordData, $offset);
4705 $extendedFilePath = substr($recordData, $offset, $xl);
4706 $extendedFilePath = self::encodeUTF16($extendedFilePath,
false);
4711 $url = str_repeat(
'..\\', $upLevelCount);
4712 $url .= ($sz > 0) ? $extendedFilePath : $shortenedFilePath;
4713 $url .= $hasText ?
'#' :
'';
4732 $tl = self::getInt4d($recordData, $offset);
4735 $text = self::encodeUTF16(substr($recordData, $offset, 2 * ($tl - 1)),
false);
4741 $this->phpSheet->getCell($coordinate)->getHyperLink()->setUrl(
$url);
4751 $length = self::getUInt2d($this->
data, $this->pos + 2);
4755 $this->pos += 4 + $length;
4763 $length = self::getUInt2d($this->
data, $this->pos + 2);
4767 $this->pos += 4 + $length;
4769 if ($this->readDataOnly) {
4774 $options = self::getInt4d($recordData, 0);
4814 $errorStyle = (0x00000070 &
$options) >> 4;
4815 switch ($errorStyle) {
4832 $explicitFormula = (0x00000080 &
$options) >> 7;
4835 $allowBlank = (0x00000100 &
$options) >> 8;
4838 $suppressDropDown = (0x00000200 &
$options) >> 9;
4841 $showInputMessage = (0x00040000 &
$options) >> 18;
4844 $showErrorMessage = (0x00080000 &
$options) >> 19;
4847 $operator = (0x00F00000 &
$options) >> 20;
4848 switch ($operator) {
4885 $string = self::readUnicodeStringLong(substr($recordData, $offset));
4886 $promptTitle = $string[
'value'] !== chr(0) ? $string[
'value'] :
'';
4887 $offset += $string[
'size'];
4890 $string = self::readUnicodeStringLong(substr($recordData, $offset));
4891 $errorTitle = $string[
'value'] !== chr(0) ? $string[
'value'] :
'';
4892 $offset += $string[
'size'];
4895 $string = self::readUnicodeStringLong(substr($recordData, $offset));
4896 $prompt = $string[
'value'] !== chr(0) ? $string[
'value'] :
'';
4897 $offset += $string[
'size'];
4900 $string = self::readUnicodeStringLong(substr($recordData, $offset));
4901 $error = $string[
'value'] !== chr(0) ? $string[
'value'] :
'';
4902 $offset += $string[
'size'];
4905 $sz1 = self::getUInt2d($recordData, $offset);
4912 $formula1 = substr($recordData, $offset, $sz1);
4913 $formula1 = pack(
'v', $sz1) . $formula1;
4920 $formula1 = str_replace(chr(0),
',', $formula1);
4928 $sz2 = self::getUInt2d($recordData, $offset);
4935 $formula2 = substr($recordData, $offset, $sz2);
4936 $formula2 = pack(
'v', $sz2) . $formula2;
4947 $cellRangeAddresses = $cellRangeAddressList[
'cellRangeAddresses'];
4949 foreach ($cellRangeAddresses as $cellRange) {
4950 $stRange = $this->phpSheet->shrinkRangeToFit($cellRange);
4952 $objValidation = $this->phpSheet->getCell($coordinate)->getDataValidation();
4953 $objValidation->setType(
$type);
4954 $objValidation->setErrorStyle($errorStyle);
4955 $objValidation->setAllowBlank((
bool) $allowBlank);
4956 $objValidation->setShowInputMessage((
bool) $showInputMessage);
4957 $objValidation->setShowErrorMessage((
bool) $showErrorMessage);
4958 $objValidation->setShowDropDown(!$suppressDropDown);
4959 $objValidation->setOperator($operator);
4960 $objValidation->setErrorTitle($errorTitle);
4961 $objValidation->setError($error);
4962 $objValidation->setPromptTitle($promptTitle);
4963 $objValidation->setPrompt($prompt);
4964 $objValidation->setFormula1($formula1);
4965 $objValidation->setFormula2($formula2);
4975 $length = self::getUInt2d($this->
data, $this->pos + 2);
4979 $this->pos += 4 + $length;
4984 if (!$this->readDataOnly) {
4991 $sz = self::getInt4d($recordData, 12);
4996 $colorIndex = self::getUInt2d($recordData, 16);
4997 $color =
Xls\Color::map($colorIndex, $this->palette, $this->version);
4998 $this->phpSheet->getTabColor()->setRGB($color[
'rgb']);
5015 $length = self::getUInt2d($this->
data, $this->pos + 2);
5019 $this->pos += 4 + $length;
5021 if ($this->readDataOnly) {
5032 $isf = self::getUInt2d($recordData, 12);
5043 $options = self::getUInt2d($recordData, 19);
5047 $this->phpSheet->getProtection()->setObjects(!$bool);
5051 $this->phpSheet->getProtection()->setScenarios(!$bool);
5055 $this->phpSheet->getProtection()->setFormatCells(!$bool);
5059 $this->phpSheet->getProtection()->setFormatColumns(!$bool);
5063 $this->phpSheet->getProtection()->setFormatRows(!$bool);
5067 $this->phpSheet->getProtection()->setInsertColumns(!$bool);
5071 $this->phpSheet->getProtection()->setInsertRows(!$bool);
5075 $this->phpSheet->getProtection()->setInsertHyperlinks(!$bool);
5079 $this->phpSheet->getProtection()->setDeleteColumns(!$bool);
5083 $this->phpSheet->getProtection()->setDeleteRows(!$bool);
5087 $this->phpSheet->getProtection()->setSelectLockedCells(!$bool);
5091 $this->phpSheet->getProtection()->setSort(!$bool);
5095 $this->phpSheet->getProtection()->setAutoFilter(!$bool);
5099 $this->phpSheet->getProtection()->setPivotTables(!$bool);
5103 $this->phpSheet->getProtection()->setSelectUnlockedCells(!$bool);
5115 $length = self::getUInt2d($this->
data, $this->pos + 2);
5119 $this->pos += 4 + $length;
5124 if (!$this->readDataOnly) {
5128 $isf = self::getUInt2d($recordData, 12);
5138 $cref = self::getUInt2d($recordData, 19);
5145 for (
$i = 0;
$i < $cref; ++
$i) {
5151 $cellRanges[] = $cellRange;
5156 $rgbFeat = substr($recordData, $offset);
5160 $wPassword = self::getInt4d($recordData, $offset);
5165 $this->phpSheet->protectCells(implode(
' ', $cellRanges), strtoupper(dechex($wPassword)),
true);
5177 $length = self::getUInt2d($this->
data, $this->pos + 2);
5182 if ($this->drawingData ==
'') {
5184 $this->pos += 4 + $length;
5192 $this->pos += 4 + $length;
5203 $validSplitPoints = [0xF003, 0xF004, 0xF00D];
5205 $splitPoint = self::getUInt2d($recordData, 2);
5206 if (in_array($splitPoint, $validSplitPoints)) {
5209 $this->drawingData .= $splicedRecordData[
'recordData'];
5215 $this->pos += 4 + $length;
5229 $spliceOffsets = [];
5232 $spliceOffsets[0] = 0;
5238 $identifier = self::getUInt2d($this->
data, $this->pos);
5240 $length = self::getUInt2d($this->
data, $this->pos + 2);
5243 $spliceOffsets[
$i] = $spliceOffsets[
$i - 1] + $length;
5245 $this->pos += 4 + $length;
5246 $nextIdentifier = self::getUInt2d($this->
data, $this->pos);
5247 }
while ($nextIdentifier == self::XLS_TYPE_CONTINUE);
5250 'recordData' =>
$data,
5251 'spliceOffsets' => $spliceOffsets,
5266 $sz = self::getUInt2d($formulaStructure, 0);
5269 $formulaData = substr($formulaStructure, 2, $sz);
5272 if (strlen($formulaStructure) > 2 + $sz) {
5273 $additionalData = substr($formulaStructure, 2 + $sz);
5275 $additionalData =
'';
5297 $formulaData = substr($formulaData,
$token[
'size']);
5302 return $formulaString;
5316 if (empty($tokens)) {
5320 $formulaStrings = [];
5321 foreach ($tokens as
$token) {
5323 $space0 = $space0 ??
'';
5324 $space1 = $space1 ??
'';
5325 $space2 = $space2 ??
'';
5326 $space3 = $space3 ??
'';
5327 $space4 = $space4 ??
'';
5328 $space5 = $space5 ??
'';
5330 switch ($token[
'name']) {
5346 $op2 = array_pop($formulaStrings);
5347 $op1 = array_pop($formulaStrings);
5348 $formulaStrings[] =
"$op1$space1$space0{$token['data']}$op2";
5349 unset($space0, $space1);
5354 $op = array_pop($formulaStrings);
5355 $formulaStrings[] =
"$space1$space0{$token['data']}$op";
5356 unset($space0, $space1);
5360 $op = array_pop($formulaStrings);
5361 $formulaStrings[] =
"$op$space1$space0{$token['data']}";
5362 unset($space0, $space1);
5365 case 'tAttrVolatile':
5374 switch ($token[
'data'][
'spacetype']) {
5376 $space0 = str_repeat(
' ', $token[
'data'][
'spacecount']);
5380 $space1 = str_repeat(
"\n", $token[
'data'][
'spacecount']);
5384 $space2 = str_repeat(
' ', $token[
'data'][
'spacecount']);
5388 $space3 = str_repeat(
"\n", $token[
'data'][
'spacecount']);
5392 $space4 = str_repeat(
' ', $token[
'data'][
'spacecount']);
5396 $space5 = str_repeat(
"\n", $token[
'data'][
'spacecount']);
5403 $op = array_pop($formulaStrings);
5404 $formulaStrings[] =
"{$space1}{$space0}SUM($op)";
5405 unset($space0, $space1);
5410 if ($token[
'data'][
'function'] !=
'') {
5413 for (
$i = 0;
$i < $token[
'data'][
'args']; ++
$i) {
5414 $ops[] = array_pop($formulaStrings);
5416 $ops = array_reverse($ops);
5417 $formulaStrings[] =
"$space1$space0{$token['data']['function']}(" . implode(
',', $ops) .
')';
5418 unset($space0, $space1);
5422 for (
$i = 0;
$i < $token[
'data'][
'args'] - 1; ++
$i) {
5423 $ops[] = array_pop($formulaStrings);
5425 $ops = array_reverse($ops);
5426 $function = array_pop($formulaStrings);
5427 $formulaStrings[] =
"$space1$space0$function(" . implode(
',', $ops) .
')';
5428 unset($space0, $space1);
5433 $expression = array_pop($formulaStrings);
5434 $formulaStrings[] =
"$space3$space2($expression$space5$space4)";
5435 unset($space2, $space3, $space4, $space5);
5439 $constantArray = self::readBIFF8ConstantArray($additionalData);
5440 $formulaStrings[] = $space1 . $space0 . $constantArray[
'value'];
5441 $additionalData = substr($additionalData, $constantArray[
'size']);
5442 unset($space0, $space1);
5448 $additionalData = substr($additionalData, $cellRangeAddressList[
'size']);
5449 $formulaStrings[] =
"$space1$space0{$token['data']}";
5450 unset($space0, $space1);
5469 $formulaStrings[] =
"$space1$space0{$token['data']}";
5470 unset($space0, $space1);
5475 $formulaString = $formulaStrings[0];
5477 return $formulaString;
5491 $id = ord($formulaData[0]);
5618 $string = self::readUnicodeStringShort(substr($formulaData, 1));
5619 $size = 1 + $string[
'size'];
5620 $data = self::UTF8toExcelDoubleQuoted($string[
'value']);
5625 switch (ord($formulaData[1])) {
5627 $name =
'tAttrVolatile';
5639 $name =
'tAttrChoose';
5641 $nc = self::getUInt2d($formulaData, 2);
5644 $size = 2 * $nc + 6;
5649 $name =
'tAttrSkip';
5662 $name =
'tAttrSpace';
5665 switch (ord($formulaData[2])) {
5667 $spacetype =
'type0';
5671 $spacetype =
'type1';
5675 $spacetype =
'type2';
5679 $spacetype =
'type3';
5683 $spacetype =
'type4';
5687 $spacetype =
'type5';
5691 throw new Exception(
'Unrecognized space type in tAttrSpace token');
5696 $spacecount = ord($formulaData[3]);
5698 $data = [
'spacetype' => $spacetype,
'spacecount' => $spacecount];
5702 throw new Exception(
'Unrecognized attribute flag in tAttr token');
5719 $data = ord($formulaData[1]) ?
'TRUE' :
'FALSE';
5726 $data = self::getUInt2d($formulaData, 1);
5733 $data = self::extractNumber(substr($formulaData, 1));
5734 $data = str_replace(
',',
'.', (
string)
$data);
5752 switch (self::getUInt2d($formulaData, 1)) {
5759 $function =
'ISERROR';
5809 $function =
'LOG10';
5829 $function =
'ROUND';
5849 $function =
'VALUE';
5859 $function =
'FALSE';
5874 $function =
'DCOUNT';
5884 $function =
'DAVERAGE';
5899 $function =
'DSTDEV';
5934 $function =
'MONTH';
5949 $function =
'MINUTE';
5954 $function =
'SECOND';
5964 $function =
'AREAS';
5974 $function =
'COLUMNS';
5979 $function =
'TRANSPOSE';
5989 $function =
'ATAN2';
6004 $function =
'ISREF';
6014 $function =
'LOWER';
6019 $function =
'UPPER';
6024 $function =
'PROPER';
6029 $function =
'EXACT';
6039 $function =
'REPLACE';
6049 $function =
'ISERR';
6054 $function =
'ISTEXT';
6059 $function =
'ISNUMBER';
6064 $function =
'ISBLANK';
6079 $function =
'DATEVALUE';
6084 $function =
'TIMEVALUE';
6099 $function =
'CLEAN';
6104 $function =
'MDETERM';
6109 $function =
'MINVERSE';
6114 $function =
'MMULT';
6124 $function =
'DPRODUCT';
6129 $function =
'ISNONTEXT';
6134 $function =
'DSTDEVP';
6139 $function =
'DVARP';
6144 $function =
'ISLOGICAL';
6149 $function =
'DCOUNTA';
6154 $function =
'REPLACEB';
6169 $function =
'ROUNDUP';
6174 $function =
'ROUNDDOWN';
6189 $function =
'TODAY';
6209 $function =
'ASINH';
6214 $function =
'ACOSH';
6219 $function =
'ATANH';
6234 $function =
'FREQUENCY';
6239 $function =
'ERROR.TYPE';
6244 $function =
'GAMMALN';
6249 $function =
'BINOMDIST';
6254 $function =
'CHIDIST';
6259 $function =
'CHIINV';
6264 $function =
'COMBIN';
6269 $function =
'CONFIDENCE';
6274 $function =
'CRITBINOM';
6284 $function =
'EXPONDIST';
6289 $function =
'FDIST';
6299 $function =
'FISHER';
6304 $function =
'FISHERINV';
6309 $function =
'FLOOR';
6314 $function =
'GAMMADIST';
6319 $function =
'GAMMAINV';
6324 $function =
'CEILING';
6329 $function =
'HYPGEOMDIST';
6334 $function =
'LOGNORMDIST';
6339 $function =
'LOGINV';
6344 $function =
'NEGBINOMDIST';
6349 $function =
'NORMDIST';
6354 $function =
'NORMSDIST';
6359 $function =
'NORMINV';
6364 $function =
'NORMSINV';
6369 $function =
'STANDARDIZE';
6379 $function =
'PERMUT';
6384 $function =
'POISSON';
6389 $function =
'TDIST';
6394 $function =
'WEIBULL';
6399 $function =
'SUMXMY2';
6404 $function =
'SUMX2MY2';
6409 $function =
'SUMX2PY2';
6414 $function =
'CHITEST';
6419 $function =
'CORREL';
6424 $function =
'COVAR';
6429 $function =
'FORECAST';
6434 $function =
'FTEST';
6439 $function =
'INTERCEPT';
6444 $function =
'PEARSON';
6454 $function =
'STEYX';
6459 $function =
'SLOPE';
6464 $function =
'TTEST';
6469 $function =
'LARGE';
6474 $function =
'SMALL';
6479 $function =
'QUARTILE';
6484 $function =
'PERCENTILE';
6489 $function =
'TRIMMEAN';
6499 $function =
'POWER';
6504 $function =
'RADIANS';
6509 $function =
'DEGREES';
6514 $function =
'COUNTIF';
6519 $function =
'COUNTBLANK';
6524 $function =
'ISPMT';
6529 $function =
'DATEDIF';
6534 $function =
'DATESTRING';
6539 $function =
'NUMBERSTRING';
6544 $function =
'PHONETIC';
6549 $function =
'BAHTTEXT';
6554 throw new Exception(
'Unrecognized function in formula');
6558 $data = [
'function' => $function,
'args' => $args];
6567 $args = ord($formulaData[1]);
6569 $index = self::getUInt2d($formulaData, 2);
6572 $function =
'COUNT';
6584 $function =
'AVERAGE';
6600 $function =
'COLUMN';
6608 $function =
'STDEV';
6612 $function =
'DOLLAR';
6616 $function =
'FIXED';
6620 $function =
'LOOKUP';
6624 $function =
'INDEX';
6640 $function =
'LINEST';
6644 $function =
'TREND';
6648 $function =
'LOGEST';
6652 $function =
'GROWTH';
6680 $function =
'MATCH';
6684 $function =
'WEEKDAY';
6688 $function =
'OFFSET';
6692 $function =
'SEARCH';
6696 $function =
'CHOOSE';
6700 $function =
'HLOOKUP';
6704 $function =
'VLOOKUP';
6716 $function =
'RIGHT';
6720 $function =
'SUBSTITUTE';
6736 $function =
'INDIRECT';
6748 $function =
'COUNTA';
6752 $function =
'PRODUCT';
6756 $function =
'STDEVP';
6764 $function =
'TRUNC';
6768 $function =
'USDOLLAR';
6772 $function =
'FINDB';
6776 $function =
'SEARCHB';
6780 $function =
'LEFTB';
6784 $function =
'RIGHTB';
6792 $function =
'ADDRESS';
6796 $function =
'DAYS360';
6804 $function =
'MEDIAN';
6808 $function =
'SUMPRODUCT';
6820 $function =
'AVEDEV';
6824 $function =
'BETADIST';
6828 $function =
'BETAINV';
6836 $function =
'DEVSQ';
6840 $function =
'GEOMEAN';
6844 $function =
'HARMEAN';
6848 $function =
'SUMSQ';
6860 $function =
'ZTEST';
6864 $function =
'PERCENTRANK';
6872 $function =
'CONCATENATE';
6876 $function =
'SUBTOTAL';
6880 $function =
'SUMIF';
6884 $function =
'ROMAN';
6888 $function =
'GETPIVOTDATA';
6892 $function =
'HYPERLINK';
6896 $function =
'AVERAGEA';
6908 $function =
'STDEVPA';
6912 $function =
'VARPA';
6916 $function =
'STDEVA';
6924 throw new Exception(
'Unrecognized function in formula');
6928 $data = [
'function' => $function,
'args' => $args];
6937 $definedNameIndex = self::getUInt2d($formulaData, 1) - 1;
6939 $data = $this->definedname[$definedNameIndex][
'name'];
6964 $subSize = self::getUInt2d($formulaData, 5);
6965 $size = 7 + $subSize;
6975 $subSize = self::getUInt2d($formulaData, 5);
6976 $size = 7 + $subSize;
6985 $subSize = self::getUInt2d($formulaData, 1);
6986 $size = 3 + $subSize;
7013 $index = self::getUInt2d($formulaData, 3);
7015 $data = $this->externalNames[
$index - 1][
'name'];
7030 $data =
"$sheetRange!$cellAddress";
7049 $data =
"$sheetRange!$cellRangeAddress";
7058 throw new Exception(
'Unrecognized token ' . sprintf(
'%02X',
$id) .
' in formula');
7082 $row = self::getUInt2d($cellAddressStructure, 0) + 1;
7089 if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
7090 $column =
'$' . $column;
7093 if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
7097 return $column .
$row;
7114 $baseRow = (int) $baseRow;
7117 $rowIndex = self::getUInt2d($cellAddressStructure, 0);
7118 $row = self::getUInt2d($cellAddressStructure, 0) + 1;
7121 if (!(0x4000 & self::getUInt2d($cellAddressStructure, 2))) {
7124 $colIndex = 0x00FF & self::getUInt2d($cellAddressStructure, 2);
7127 $column =
'$' . $column;
7131 $relativeColIndex = 0x00FF & self::getInt2d($cellAddressStructure, 2);
7132 $colIndex = $baseCol + $relativeColIndex;
7133 $colIndex = ($colIndex < 256) ? $colIndex : $colIndex - 256;
7134 $colIndex = ($colIndex >= 0) ? $colIndex : $colIndex + 256;
7139 if (!(0x8000 & self::getUInt2d($cellAddressStructure, 2))) {
7142 $rowIndex = ($rowIndex <= 32767) ? $rowIndex : $rowIndex - 65536;
7143 $row = $baseRow + $rowIndex;
7146 return $column .
$row;
7161 $fr = self::getUInt2d($subData, 0) + 1;
7164 $lr = self::getUInt2d($subData, 2) + 1;
7167 $fc = ord($subData[4]);
7170 $lc = ord($subData[5]);
7173 if ($fr >
$lr || $fc >
$lc) {
7174 throw new Exception(
'Not a cell range address');
7181 if ($fr ==
$lr && $fc ==
$lc) {
7185 return "$fc$fr:$lc$lr";
7200 $fr = self::getUInt2d($subData, 0) + 1;
7203 $lr = self::getUInt2d($subData, 2) + 1;
7206 $fc = self::getUInt2d($subData, 4);
7209 $lc = self::getUInt2d($subData, 6);
7212 if ($fr >
$lr || $fc >
$lc) {
7213 throw new Exception(
'Not a cell range address');
7220 if ($fr ==
$lr && $fc ==
$lc) {
7224 return "$fc$fr:$lc$lr";
7242 $fr = self::getUInt2d($subData, 0) + 1;
7245 $lr = self::getUInt2d($subData, 2) + 1;
7253 if (!(0x4000 & self::getUInt2d($subData, 4))) {
7258 if (!(0x8000 & self::getUInt2d($subData, 4))) {
7268 if (!(0x4000 & self::getUInt2d($subData, 6))) {
7273 if (!(0x8000 & self::getUInt2d($subData, 6))) {
7277 return "$fc$fr:$lc$lr";
7293 $baseCol = $baseCol - 1;
7299 $frIndex = self::getUInt2d($subData, 0);
7302 $lrIndex = self::getUInt2d($subData, 2);
7305 if (!(0x4000 & self::getUInt2d($subData, 4))) {
7309 $fcIndex = 0x00FF & self::getUInt2d($subData, 4);
7316 $relativeFcIndex = 0x00FF & self::getInt2d($subData, 4);
7317 $fcIndex = $baseCol + $relativeFcIndex;
7318 $fcIndex = ($fcIndex < 256) ? $fcIndex : $fcIndex - 256;
7319 $fcIndex = ($fcIndex >= 0) ? $fcIndex : $fcIndex + 256;
7324 if (!(0x8000 & self::getUInt2d($subData, 4))) {
7330 $frIndex = ($frIndex <= 32767) ? $frIndex : $frIndex - 65536;
7331 $fr = $baseRow + $frIndex;
7335 if (!(0x4000 & self::getUInt2d($subData, 6))) {
7339 $lcIndex = 0x00FF & self::getUInt2d($subData, 6);
7346 $relativeLcIndex = 0x00FF & self::getInt2d($subData, 4);
7347 $lcIndex = $baseCol + $relativeLcIndex;
7348 $lcIndex = ($lcIndex < 256) ? $lcIndex : $lcIndex - 256;
7349 $lcIndex = ($lcIndex >= 0) ? $lcIndex : $lcIndex + 256;
7354 if (!(0x8000 & self::getUInt2d($subData, 6))) {
7360 $lrIndex = ($lrIndex <= 32767) ? $lrIndex : $lrIndex - 65536;
7361 $lr = $baseRow + $lrIndex;
7364 return "$fc$fr:$lc$lr";
7377 $cellRangeAddresses = [];
7380 $nm = self::getUInt2d($subData, 0);
7384 for (
$i = 0;
$i < $nm; ++
$i) {
7390 'size' => 2 + 8 * $nm,
7391 'cellRangeAddresses' => $cellRangeAddresses,
7405 $cellRangeAddresses = [];
7408 $nm = self::getUInt2d($subData, 0);
7412 for (
$i = 0;
$i < $nm; ++
$i) {
7418 'size' => 2 + 6 * $nm,
7419 'cellRangeAddresses' => $cellRangeAddresses,
7435 if (isset($this->ref[
$index])) {
7436 $type = $this->externalBooks[$this->ref[
$index][
'externalBookIndex']][
'type'];
7441 if ($this->ref[$index][
'firstSheetIndex'] == 0xFFFF || $this->ref[$index][
'lastSheetIndex'] == 0xFFFF) {
7442 throw new Exception(
'Deleted sheet reference');
7446 $firstSheetName = $this->sheets[$this->ref[
$index][
'firstSheetIndex']][
'name'];
7447 $lastSheetName = $this->sheets[$this->ref[
$index][
'lastSheetIndex']][
'name'];
7449 if ($firstSheetName == $lastSheetName) {
7451 $sheetRange = $firstSheetName;
7453 $sheetRange =
"$firstSheetName:$lastSheetName";
7457 $sheetRange = str_replace(
"'",
"''", $sheetRange);
7463 if (preg_match(
"/[ !\"@#£$%&{()}<>=+'|^,;-]/u", $sheetRange)) {
7464 $sheetRange =
"'$sheetRange'";
7472 throw new Exception(
'Xls reader only supports internal sheets in formulas');
7493 $nc = ord($arrayData[0]);
7496 $nr = self::getUInt2d($arrayData, 1);
7498 $arrayData = substr($arrayData, 3);
7502 for (
$r = 1;
$r <= $nr + 1; ++
$r) {
7504 for (
$c = 1;
$c <= $nc + 1; ++
$c) {
7505 $constant = self::readBIFF8Constant($arrayData);
7506 $items[] = $constant[
'value'];
7507 $arrayData = substr($arrayData, $constant[
'size']);
7508 $size += $constant[
'size'];
7510 $matrixChunks[] = implode(
',', $items);
7512 $matrix =
'{' . implode(
';', $matrixChunks) .
'}';
7532 $identifier = ord($valueData[0]);
7534 switch ($identifier) {
7542 $value = self::extractNumber(substr($valueData, 1, 8));
7548 $string = self::readUnicodeStringLong(substr($valueData, 1));
7549 $value =
'"' . $string[
'value'] .
'"';
7550 $size = 1 + $string[
'size'];
7555 if (ord($valueData[1])) {
7599 $rgb = sprintf(
'%02X%02X%02X',
$r, $g, $b);
7601 return [
'rgb' => $rgb];
7615 $ln = ord($subData[0]);
7637 $ln = self::getUInt2d($subData, 0);
7663 $characterCount = ord($subData[0]);
7665 $string = self::readUnicodeString(substr($subData, 1), $characterCount);
7687 $characterCount = self::getUInt2d($subData, 0);
7689 $string = self::readUnicodeString(substr($subData, 2), $characterCount);
7692 $string[
'size'] += 2;
7713 $isCompressed = !((0x01 & ord($subData[0])) >> 0);
7716 $hasAsian = (0x04) & ord($subData[0]) >> 2;
7719 $hasRichText = (0x08) & ord($subData[0]) >> 3;
7724 $value = self::encodeUTF16(substr($subData, 1, $isCompressed ? $characterCount : 2 * $characterCount), $isCompressed);
7728 'size' => $isCompressed ? 1 + $characterCount : 1 + 2 * $characterCount,
7742 return '"' . str_replace(
'"',
'""', $value) .
'"';
7754 $rknumhigh = self::getInt4d(
$data, 4);
7755 $rknumlow = self::getInt4d(
$data, 0);
7756 $sign = ($rknumhigh & 0x80000000) >> 31;
7757 $exp = (($rknumhigh & 0x7ff00000) >> 20) - 1023;
7758 $mantissa = (0x100000 | ($rknumhigh & 0x000fffff));
7759 $mantissalow1 = ($rknumlow & 0x80000000) >> 31;
7760 $mantissalow2 = ($rknumlow & 0x7fffffff);
7761 $value = $mantissa / 2 ** (20 - $exp);
7763 if ($mantissalow1 != 0) {
7764 $value += 1 / 2 ** (21 - $exp);
7767 $value += $mantissalow2 / 2 ** (52 - $exp);
7782 if (($rknum & 0x02) != 0) {
7783 $value = $rknum >> 2;
7790 $sign = ($rknum & 0x80000000) >> 31;
7791 $exp = ($rknum & 0x7ff00000) >> 20;
7792 $mantissa = (0x100000 | ($rknum & 0x000ffffc));
7793 $value = $mantissa / 2 ** (20 - ($exp - 1023));
7795 $value = -1 * $value;
7799 if (($rknum & 0x01) != 0) {
7817 $string = self::uncompressByteString($string);
7832 $uncompressedString =
'';
7833 $strLen = strlen($string);
7834 for (
$i = 0;
$i < $strLen; ++
$i) {
7835 $uncompressedString .= $string[
$i] .
"\0";
7838 return $uncompressedString;
7893 if ($_or_24 >= 128) {
7895 $_ord_24 = -abs((256 - $_or_24) << 24);
7897 $_ord_24 = ($_or_24 & 127) << 24;
7900 return ord(
$data[
$pos]) | (ord(
$data[$pos + 1]) << 8) | (ord(
$data[$pos + 2]) << 16) | $_ord_24;
7906 $value->createText($is);
static sizeCol($sheet, $col='A')
Get the width of a column in pixels.
readVcenter()
Read VCENTER record.
readMulBlank()
Read MULBLANK record This record represents a cell range of empty cells.
decodeCodepage($string)
Convert string to UTF-8.
readBottomMargin()
Read BOTTOMMARGIN record.
readDefault()
Reads a general type of BIFF record.
readBlank()
Read BLANK record.
const TEXTROTATION_STACK_EXCEL
static static validate(string $codePage)
readSheetLayout()
Read SHEETLAYOUT record.
const OPERATOR_GREATERTHANOREQUAL
const XLS_TYPE_DATAVALIDATION
const XLS_TYPE_SCENPROTECT
readDataValidation()
Read DATAVALIDATION record.
canRead($pFilename)
Can the current IReader read the file?
static readUnicodeStringShort($subData)
Extracts an Excel Unicode short string (8-bit string length) OpenOffice documentation: 2...
const XLS_TYPE_MSODRAWING
const OPERATOR_LESSTHANOREQUAL
readHcenter()
Read HCENTER record.
const XLS_TYPE_DATAVALIDATIONS
const XLS_TYPE_SHEETPROTECTION
const XLS_TYPE_DEFCOLWIDTH
readXf()
XF - Extended Format.
readPalette()
Read PALETTE record.
static extractSheetTitle($pRange, $returnRange=false)
Extract worksheet title from range.
listWorksheetInfo($pFilename)
Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns)...
const XLS_TYPE_PAGELAYOUTVIEW
static readBIFF8Constant($valueData)
read BIFF8 constant value which may be 'Empty Value', 'Number', 'String Value', 'Boolean Value'...
static countCharacters($value, $enc='UTF-8')
Get character count.
getNextToken($formulaData, $baseCell='A1')
Fetch next token from binary formula data.
const XLS_TYPE_MERGEDCELLS
readDataValidations()
Read DATAVALIDATIONS record.
static readRGB($rgb)
Extract RGB color OpenOffice.org's Documentation of the Microsoft Excel File Format, section 2.5.4.
readNumber()
Read NUMBER record This record represents a cell that contains a floating-point value.
readMergedCells()
MERGEDCELLS.
readRk()
Read RK record This record represents a cell that contains an RK value (encoded integer or floating-p...
readPane()
Read PANE record.
if(!array_key_exists('StateId', $_REQUEST)) $id
static getInt4d($data, $pos)
Read 32-bit signed integer.
readLabelSst()
Read LABELSST record This record represents a cell that contains a string.
readProtect()
PROTECT - Sheet protection (BIFF2 through BIFF8) if this record is omitted, then it also means no she...
loadOLE($pFilename)
Use OLE reader to extract the relevant data streams from the OLE file.
const XLS_TYPE_SHEETLAYOUT
static extractNumber($data)
Reads first 8 bytes of a string and return IEEE 754 float.
static getIEEE754($rknum)
readDocumentSummaryInformation()
Read additional document summary information.
readBIFF8CellRangeAddressB($subData, $baseCell='A1')
Reads a cell range address in BIFF8 for shared formulas.
makeKey($block, $valContext)
Make an RC4 decryptor for the given block.
readMsoDrawing()
Read MSODRAWING record.
const XLS_TYPE_DEFAULTROWHEIGHT
static static horizontal(Alignment $alignment, int $horizontal)
const XLS_TYPE_DEFINEDNAME
readPrintGridlines()
Read PRINTGRIDLINES record.
readHeader()
Read HEADER record.
static readUnicodeString($subData, $characterCount)
Read Unicode string with no string length field, but with known character count this function is unde...
readDefinedName()
DEFINEDNAME.
readPageLayoutView()
Read PLV Record(Created by Excel2007 or upper).
load($pFilename)
Loads PhpSpreadsheet from file.
const OPERATOR_NOTBETWEEN
const XLS_TYPE_MSODRAWINGGROUP
static UTF8toExcelDoubleQuoted($value)
Convert UTF-8 string to string surounded by double quotes.
const TEXTROTATION_STACK_PHPSPREADSHEET
const PROTECTION_PROTECTED
const PROTECTION_INHERIT
Protection styles.
static getUInt2d($data, $pos)
Read 16-bit unsigned integer.
readWindow2()
Read WINDOW2 record.
static readUnicodeStringLong($subData)
Extracts an Excel Unicode long string (16-bit string length) OpenOffice documentation: 2...
$documentSummaryInformation
readPageSetup()
Read PAGESETUP record.
readByteStringLong($subData)
Read byte string (16-bit string length) OpenOffice documentation: 2.5.2.
readPassword()
PASSWORD - Sheet protection (hashed) password (BIFF2 through BIFF8).
readMulRk()
Read MULRK record This record represents a cell range containing RK value cells.
const XLS_TYPE_VERTICALPAGEBREAKS
readBIFF5CellRangeAddressFixed($subData)
Reads a cell range address in BIFF5 e.g.
readRangeProtection()
Read RANGEPROTECTION record Reading of this record is based on Microsoft Office Excel 97-2000 Binary ...
const XLS_TYPE_EXTERNSHEET
verifyPassword($password, $docid, $salt_data, $hashedsalt_data, &$valContext)
Verify RC4 file password.
const XLS_TYPE_HORIZONTALPAGEBREAKS
getSplicedRecordData()
Reads a record from current position in data stream and continues reading data as long as CONTINUE re...
static map($color, $palette, $version)
Read color.
getReadFilter()
Read filter.
static uncompressByteString($string)
Convert UTF-16 string in compressed notation to uncompressed form.
readRecordData($data, $pos, $len)
Read record data from stream, decrypting as required.
readSheetRangeByRefIndex($index)
Get a sheet range like Sheet1:Sheet3 from REF index Note: If there is only one sheet in the range...
getFormulaFromData($formulaData, $additionalData='', $baseCell='A1')
Take formula data and additional data for formula and return human readable formula.
readRightMargin()
Read RIGHTMARGIN record.
readMsoDrawingGroup()
Read MSODRAWINGGROUP record.
readColInfo()
Read COLINFO record.
readBIFF8CellRangeAddressList($subData)
Read BIFF8 cell range address list section 2.5.15.
readLabel()
Read LABEL record This record represents a cell that contains a string.
readBIFF8CellRangeAddress($subData)
Reads a cell range address in BIFF8 e.g.
readObj()
Read OBJ record.
static static underline(Font $font, int $underline)
readDefColWidth()
Read DEFCOLWIDTH record.
readNote()
The NOTE record specifies a comment associated with a particular cell.
readDefaultRowHeight()
Read DEFAULTROWHEIGHT record.
const XLS_WORKBOOKGLOBALS
foreach( $_REQUEST as $var) foreach(array('_POST'=> 'HTTP_POST_VARS', '_GET'=> 'HTTP_GET_VARS', '_COOKIE'=> 'HTTP_COOKIE_VARS', '_SERVER'=> 'HTTP_SERVER_VARS', '_ENV'=> 'HTTP_ENV_VARS', '_FILES'=> 'HTTP_POST_FILES') as $array=> $other) $step
const CALENDAR_WINDOWS_1900
constants
readFont()
Read a FONT record.
readExternName()
Read EXTERNNAME record.
getFormulaFromStructure($formulaStructure, $baseCell='A1')
Convert formula structure into human readable Excel formula like 'A3+A5*5'.
readContinue()
Read a free CONTINUE record.
readFooter()
Read FOOTER record.
readSharedFmla()
Read a SHAREDFMLA record.
listWorksheetNames($pFilename)
Reads names of the worksheets from a file, without parsing the whole file to a PhpSpreadsheet object...
readTopMargin()
Read TOPMARGIN record.
static indexesFromString(string $coordinates)
Get indexes from a string coordinates.
readScenProtect()
SCENPROTECT.
readByteStringShort($subData)
Read byte string (8-bit string length) OpenOffice documentation: 2.5.2.
static convertEncoding($value, $to, $from)
Convert string from one encoding to another.
const XLS_TYPE_SHAREDFMLA
__construct()
Create a new Xls Reader instance.
const PROTECTION_UNPROTECTED
readSelection()
Read SELECTION record.
readObjectProtect()
OBJECTPROTECT.
readSheetProtection()
Read SHEETPROTECTION record (FEATHEADR).
readFormula()
Read FORMULA record + perhaps a following STRING record if formula result is a string This record con...
readExternalBook()
Read EXTERNALBOOK record.
setCodepage(string $codepage)
static coordinateFromString($pCoordinateString)
Coordinate from string.
const OPERATOR_GREATERTHAN
readHyperLink()
Read HYPERLINK record.
readStyle()
Read STYLE record.
readSummaryInformation()
Read summary information.
readBIFF8CellRangeAddressFixed($subData)
Reads a cell range address in BIFF8 e.g.
static escapement(Font $font, int $escapement)
readSheetPr()
Read SHEETPR record.
const XLS_TYPE_RIGHTMARGIN
static setExcelCalendar($baseDate)
Set the Excel calendar (Windows 1900 or Mac 1904).
static readBIFF8ConstantArray($arrayData)
read BIFF8 constant value array from array data returns e.g.
static encodeUTF16($string, $compressed=false)
Get UTF-8 string from (compressed or uncompressed) UTF-16 string.
static getDistanceY(Worksheet $sheet, $startRow=1, $startOffsetY=0, $endRow=1, $endOffsetY=0)
Get the vertical distance in pixels between two anchors The distanceY is found as sum of all the span...
readString()
Read a STRING record from current stream position and advance the stream pointer to next record This ...
const XLS_TYPE_EXTERNALBOOK
static getRangeBoundaries($pRange)
Calculate range boundaries.
static substring($pValue, $pStart, $pLength=0)
Get a substring of a UTF-8 encoded string.
includeCellRangeFiltered($cellRangeAddress)
const SHEETSTATE_VERYHIDDEN
const XLS_TYPE_EXTERNNAME
static extractAllCellReferencesInRange($cellRange)
Extract all cell references in range, which may be comprised of multiple cell ranges.
readBoolErr()
Read BOOLERR record This record represents a Boolean value or error value cell.
static vertical(Alignment $alignment, int $vertical)
readLeftMargin()
Read LEFTMARGIN record.
const XLS_TYPE_LEFTMARGIN
createFormulaFromTokens($tokens, $additionalData)
Take array of tokens together with additional data for formula and return human readable formula...
const XLS_TYPE_OBJECTPROTECT
readExternSheet()
Read EXTERNSHEET record.
static OLE2LocalDate($oleTimestamp)
Returns a timestamp from an OLE container's date.
static wrap(Alignment $alignment, int $wrap)
const XLS_TYPE_BOTTOMMARGIN
readVerticalPageBreaks()
Read VERTICALPAGEBREAKS record.
static assertFile($filename)
Assert that given path is an existing file and is readable, otherwise throw exception.
readBIFF5CellRangeAddressList($subData)
Read BIFF5 cell range address list section 2.5.15.
static getInt2d($data, $pos)
Read 16-bit signed integer.
static columnIndexFromString($pString)
Column index from string.
readScl()
Read SCL record.
static numberToName(int $codePage)
Convert Microsoft Code Page Identifier to Code Page Name which iconv and mbstring understands...
static static lookup($code)
Map error code, e.g.
static stringFromColumnIndex($columnIndex)
String from column index.
const MS_BIFF_CRYPTO_NONE
const SHEETVIEW_PAGE_LAYOUT
readHorizontalPageBreaks()
Read HORIZONTALPAGEBREAKS record.
readTextObject()
The TEXT Object record contains the text associated with a cell annotation.
readBIFF8CellAddressB($cellAddressStructure, $baseCell='A1')
Reads a cell address in BIFF8 for shared formulas.
const SHEETVIEW_PAGE_BREAK_PREVIEW
static sizeRow($sheet, $row=1)
Convert the height of a cell from user's units to pixels.
readBIFF8CellAddress($cellAddressStructure)
Reads a cell address in BIFF8 e.g.
const XLS_TYPE_PRINTGRIDLINES
readSst()
SST - Shared String Table.
static getDistanceX(Worksheet $sheet, $startColumn='A', $startOffsetX=0, $endColumn='A', $endOffsetX=0)
Get the horizontal distance in pixels between two anchors The distanceX is found as sum of all the sp...
const XLS_TYPE_RANGEPROTECTION