|
7 | 7 | use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
|
8 | 8 | use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
9 | 9 | use PhpOffice\PhpSpreadsheet\NamedRange;
|
| 10 | +use PhpOffice\PhpSpreadsheet\Reader\Xls\ConditionalFormatting; |
10 | 11 | use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\CellFont;
|
11 | 12 | use PhpOffice\PhpSpreadsheet\RichText\RichText;
|
12 | 13 | use PhpOffice\PhpSpreadsheet\Shared\CodePage;
|
@@ -142,6 +143,8 @@ class Xls extends BaseReader
|
142 | 143 | const XLS_TYPE_SHEETLAYOUT = 0x0862;
|
143 | 144 | const XLS_TYPE_XFEXT = 0x087d;
|
144 | 145 | const XLS_TYPE_PAGELAYOUTVIEW = 0x088b;
|
| 146 | + const XLS_TYPE_CFHEADER = 0x01b0; |
| 147 | + const XLS_TYPE_CFRULE = 0x01b1; |
145 | 148 | const XLS_TYPE_UNKNOWN = 0xffff;
|
146 | 149 |
|
147 | 150 | // Encryption type
|
@@ -1031,6 +1034,14 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
|
1031 | 1034 | case self::XLS_TYPE_DATAVALIDATION:
|
1032 | 1035 | $this->readDataValidation();
|
1033 | 1036 |
|
| 1037 | + break; |
| 1038 | + case self::XLS_TYPE_CFHEADER: |
| 1039 | + $this->readCFHeader(); |
| 1040 | + |
| 1041 | + break; |
| 1042 | + case self::XLS_TYPE_CFRULE: |
| 1043 | + $this->readCFRule(); |
| 1044 | + |
1034 | 1045 | break;
|
1035 | 1046 | case self::XLS_TYPE_SHEETLAYOUT:
|
1036 | 1047 | $this->readSheetLayout();
|
@@ -7921,4 +7932,128 @@ public function getMapCellStyleXfIndex(): array
|
7921 | 7932 | {
|
7922 | 7933 | return $this->mapCellStyleXfIndex;
|
7923 | 7934 | }
|
| 7935 | + |
| 7936 | + private function readCFHeader(): void |
| 7937 | + { |
| 7938 | + var_dump('FOUND CF HEADER'); |
| 7939 | + $length = self::getUInt2d($this->data, $this->pos + 2); |
| 7940 | + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); |
| 7941 | + |
| 7942 | + // move stream pointer forward to next record |
| 7943 | + $this->pos += 4 + $length; |
| 7944 | + |
| 7945 | + if ($this->readDataOnly) { |
| 7946 | + return; |
| 7947 | + } |
| 7948 | + |
| 7949 | + // offset: 0; size: 2; Rule Count |
| 7950 | + $ruleCount = self::getUInt2d($recordData, 0); |
| 7951 | + |
| 7952 | + // offset: var; size: var; cell range address list with |
| 7953 | + $cellRangeAddressList = ($this->version == self::XLS_BIFF8) |
| 7954 | + ? $this->readBIFF8CellRangeAddressList(substr($recordData, 12)) |
| 7955 | + : $this->readBIFF5CellRangeAddressList(substr($recordData, 12)); |
| 7956 | + $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; |
| 7957 | + |
| 7958 | + var_dump($ruleCount, $cellRangeAddresses); |
| 7959 | + } |
| 7960 | + |
| 7961 | + private function readCFRule(): void |
| 7962 | + { |
| 7963 | + var_dump('FOUND CF RULE'); |
| 7964 | + $length = self::getUInt2d($this->data, $this->pos + 2); |
| 7965 | + $recordData = $this->readRecordData($this->data, $this->pos + 4, $length); |
| 7966 | + |
| 7967 | + // move stream pointer forward to next record |
| 7968 | + $this->pos += 4 + $length; |
| 7969 | + |
| 7970 | + if ($this->readDataOnly) { |
| 7971 | + return; |
| 7972 | + } |
| 7973 | + |
| 7974 | + // offset: 0; size: 2; Options |
| 7975 | + $cfRule = self::getUInt2d($recordData, 0); |
| 7976 | + |
| 7977 | + // bit: 8-15; mask: 0x00FF; type |
| 7978 | + $type = (0x00FF & $cfRule) >> 0; |
| 7979 | + $type = ConditionalFormatting::type($type); |
| 7980 | + |
| 7981 | + // bit: 0-7; mask: 0xFF00; type |
| 7982 | + $operator = (0xFF00 & $cfRule) >> 8; |
| 7983 | + $operator = ConditionalFormatting::operator($operator); |
| 7984 | + |
| 7985 | + if ($type === null || $operator === null) { |
| 7986 | + return; |
| 7987 | + } |
| 7988 | + |
| 7989 | + // offset: 2; size: 2; Size1 |
| 7990 | + $size1 = self::getUInt2d($recordData, 2); |
| 7991 | + |
| 7992 | + // offset: 4; size: 2; Size2 |
| 7993 | + $size2 = self::getUInt2d($recordData, 4); |
| 7994 | + |
| 7995 | + // offset: 6; size: 4; Options |
| 7996 | + $options = self::getInt4d($recordData, 6); |
| 7997 | + |
| 7998 | + $hasFontRecord = (bool) ((0x04000000 & $options) >> 26); |
| 7999 | + $hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27); |
| 8000 | + $hasBorderRecord = (bool) ((0x10000000 & $options) >> 28); |
| 8001 | + $hasFillRecord = (bool) ((0x20000000 & $options) >> 29); |
| 8002 | + $hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30); |
| 8003 | + |
| 8004 | + $offset = 12; |
| 8005 | + |
| 8006 | + if ($hasFontRecord === true) { |
| 8007 | + $offset += 118; |
| 8008 | + } |
| 8009 | + |
| 8010 | + if ($hasAlignmentRecord === true) { |
| 8011 | + $offset += 8; |
| 8012 | + } |
| 8013 | + |
| 8014 | + if ($hasBorderRecord === true) { |
| 8015 | + $offset += 8; |
| 8016 | + } |
| 8017 | + |
| 8018 | + if ($hasFillRecord === true) { |
| 8019 | + $offset += 4; |
| 8020 | + } |
| 8021 | + |
| 8022 | + if ($hasProtectionRecord === true) { |
| 8023 | + $offset += 2; |
| 8024 | + } |
| 8025 | + |
| 8026 | + var_dump($type, $operator); |
| 8027 | + |
| 8028 | + if ($size1 > 0) { |
| 8029 | + $formula1 = $this->readCFFormula($recordData, $offset, $size1); |
| 8030 | + if ($formula1 === null) { |
| 8031 | + return; |
| 8032 | + } |
| 8033 | + var_dump($formula1); |
| 8034 | + |
| 8035 | + $offset += $size1; |
| 8036 | + } |
| 8037 | + |
| 8038 | + if ($size2 > 0) { |
| 8039 | + $formula2 = $this->readCFFormula($recordData, $offset, $size2); |
| 8040 | + if ($formula2 === null) { |
| 8041 | + return; |
| 8042 | + } |
| 8043 | + var_dump($formula2); |
| 8044 | + } |
| 8045 | + } |
| 8046 | + |
| 8047 | + private function readCFFormula(string $recordData, int $offset, int $size): ?string |
| 8048 | + { |
| 8049 | + try { |
| 8050 | + $formula = substr($recordData, $offset, $size); |
| 8051 | + $formula = pack('v', $size) . $formula; // prepend the length |
| 8052 | + |
| 8053 | + return $this->getFormulaFromStructure($formula); |
| 8054 | + } catch (PhpSpreadsheetException $e) { |
| 8055 | + } |
| 8056 | + |
| 8057 | + return null; |
| 8058 | + } |
7924 | 8059 | }
|
0 commit comments