PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Reader / Xls / ConditionalFormatting.php

ConditionalFormatting.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Reader/Xls/ConditionalFormatting.php

350 lines 10.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace TablePress\PhpOffice\PhpSpreadsheet\Reader\Xls;
4
5 use TablePress\PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
6 use TablePress\PhpOffice\PhpSpreadsheet\Reader\Xls;
7 use TablePress\PhpOffice\PhpSpreadsheet\Reader\Xls\Style\FillPattern;
8 use TablePress\PhpOffice\PhpSpreadsheet\Style\Conditional;
9 use TablePress\PhpOffice\PhpSpreadsheet\Style\Fill;
10 use TablePress\PhpOffice\PhpSpreadsheet\Style\Style;
11
12 class ConditionalFormatting extends Xls
13 {
14 /**
15 * @var array<int, string>
16 */
17 private static array $types = [
18 0x01 => Conditional::CONDITION_CELLIS,
19 0x02 => Conditional::CONDITION_EXPRESSION,
20 ];
21
22 /**
23 * @var array<int, string>
24 */
25 private static array $operators = [
26 0x00 => Conditional::OPERATOR_NONE,
27 0x01 => Conditional::OPERATOR_BETWEEN,
28 0x02 => Conditional::OPERATOR_NOTBETWEEN,
29 0x03 => Conditional::OPERATOR_EQUAL,
30 0x04 => Conditional::OPERATOR_NOTEQUAL,
31 0x05 => Conditional::OPERATOR_GREATERTHAN,
32 0x06 => Conditional::OPERATOR_LESSTHAN,
33 0x07 => Conditional::OPERATOR_GREATERTHANOREQUAL,
34 0x08 => Conditional::OPERATOR_LESSTHANOREQUAL,
35 ];
36
37 public static function type(int $type): ?string
38 {
39 return self::$types[$type] ?? null;
40 }
41
42 public static function operator(int $operator): ?string
43 {
44 return self::$operators[$operator] ?? null;
45 }
46
47 /**
48 * Parse conditional formatting blocks.
49 *
50 * @see https://www.openoffice.org/sc/excelfileformat.pdf Search for CFHEADER followed by CFRULE
51 *
52 * @return mixed[]
53 */
54 protected function readCFHeader2(Xls $xls): array
55 {
56 $length = self::getUInt2d($xls->data, $xls->pos + 2);
57 $recordData = $xls->readRecordData($xls->data, $xls->pos + 4, $length);
58
59 // move stream pointer forward to next record
60 $xls->pos += 4 + $length;
61
62 if ($xls->readDataOnly) {
63 return [];
64 }
65
66 // offset: 0; size: 2; Rule Count
67 // $ruleCount = self::getUInt2d($recordData, 0);
68
69 // offset: var; size: var; cell range address list with
70 $cellRangeAddressList = ($xls->version == self::XLS_BIFF8)
71 ? Biff8::readBIFF8CellRangeAddressList((string) substr($recordData, 12))
72 : Biff5::readBIFF5CellRangeAddressList((string) substr($recordData, 12));
73 $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
74
75 return $cellRangeAddresses;
76 }
77
78 /** @param string[] $cellRangeAddresses */
79 protected function readCFRule2(array $cellRangeAddresses, Xls $xls): void
80 {
81 $length = self::getUInt2d($xls->data, $xls->pos + 2);
82 $recordData = $xls->readRecordData($xls->data, $xls->pos + 4, $length);
83
84 // move stream pointer forward to next record
85 $xls->pos += 4 + $length;
86
87 if ($xls->readDataOnly) {
88 return;
89 }
90
91 // offset: 0; size: 2; Options
92 $cfRule = self::getUInt2d($recordData, 0);
93
94 // bit: 8-15; mask: 0x00FF; type
95 $type = (0x00FF & $cfRule) >> 0;
96 $type = self::type($type);
97
98 // bit: 0-7; mask: 0xFF00; type
99 $operator = (0xFF00 & $cfRule) >> 8;
100 $operator = self::operator($operator);
101
102 if ($type === null || $operator === null) {
103 return;
104 }
105
106 // offset: 2; size: 2; Size1
107 $size1 = self::getUInt2d($recordData, 2);
108
109 // offset: 4; size: 2; Size2
110 $size2 = self::getUInt2d($recordData, 4);
111
112 // offset: 6; size: 4; Options
113 $options = self::getInt4d($recordData, 6);
114
115 $style = new Style(false, true); // non-supervisor, conditional
116 $noFormatSet = true;
117 //$xls->getCFStyleOptions($options, $style);
118
119 $hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
120 $hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
121 $hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
122 $hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
123 $hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
124 // note unexpected values for following 4
125 $hasBorderLeft = !(bool) (0x00000400 & $options);
126 $hasBorderRight = !(bool) (0x00000800 & $options);
127 $hasBorderTop = !(bool) (0x00001000 & $options);
128 $hasBorderBottom = !(bool) (0x00002000 & $options);
129
130 $offset = 12;
131
132 if ($hasFontRecord === true) {
133 $fontStyle = (string) substr($recordData, $offset, 118);
134 $this->getCFFontStyle($fontStyle, $style, $xls);
135 $offset += 118;
136 $noFormatSet = false;
137 }
138
139 if ($hasAlignmentRecord === true) {
140 //$alignmentStyle = substr($recordData, $offset, 8);
141 //$this->getCFAlignmentStyle($alignmentStyle, $style, $xls);
142 $offset += 8;
143 }
144
145 if ($hasBorderRecord === true) {
146 $borderStyle = (string) substr($recordData, $offset, 8);
147 $this->getCFBorderStyle($borderStyle, $style, $hasBorderLeft, $hasBorderRight, $hasBorderTop, $hasBorderBottom, $xls);
148 $offset += 8;
149 $noFormatSet = false;
150 }
151
152 if ($hasFillRecord === true) {
153 $fillStyle = (string) substr($recordData, $offset, 4);
154 $this->getCFFillStyle($fillStyle, $style, $xls);
155 $offset += 4;
156 $noFormatSet = false;
157 }
158
159 if ($hasProtectionRecord === true) {
160 //$protectionStyle = substr($recordData, $offset, 4);
161 //$this->getCFProtectionStyle($protectionStyle, $style, $xls);
162 $offset += 2;
163 }
164
165 $formula1 = $formula2 = null;
166 if ($size1 > 0) {
167 $formula1 = $this->readCFFormula($recordData, $offset, $size1, $xls);
168 if ($formula1 === null) {
169 return;
170 }
171
172 $offset += $size1;
173 }
174
175 if ($size2 > 0) {
176 $formula2 = $this->readCFFormula($recordData, $offset, $size2, $xls);
177 if ($formula2 === null) {
178 return;
179 }
180
181 $offset += $size2;
182 }
183
184 $this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style, $noFormatSet, $xls);
185 }
186
187 /*private function getCFStyleOptions(int $options, Style $style, Xls $xls): void
188 {
189 }*/
190
191 private function getCFFontStyle(string $options, Style $style, Xls $xls): void
192 {
193 $fontSize = self::getInt4d($options, 64);
194 if ($fontSize !== -1) {
195 $style->getFont()->setSize($fontSize / 20); // Convert twips to points
196 }
197 $options68 = self::getInt4d($options, 68);
198 $options88 = self::getInt4d($options, 88);
199
200 if (($options88 & 2) === 0) {
201 $bold = self::getUInt2d($options, 72); // 400 = normal, 700 = bold
202 if ($bold !== 0) {
203 $style->getFont()->setBold($bold >= 550);
204 }
205 if (($options68 & 2) !== 0) {
206 $style->getFont()->setItalic(true);
207 }
208 }
209 if (($options88 & 0x80) === 0) {
210 if (($options68 & 0x80) !== 0) {
211 $style->getFont()->setStrikethrough(true);
212 }
213 }
214
215 $color = self::getInt4d($options, 80);
216
217 if ($color !== -1) {
218 $style->getFont()
219 ->getColor()
220 ->setRGB(Color::map($color, $xls->palette, $xls->version)['rgb']);
221 }
222 }
223
224 /*private function getCFAlignmentStyle(string $options, Style $style, Xls $xls): void
225 {
226 }*/
227
228 private function getCFBorderStyle(string $options, Style $style, bool $hasBorderLeft, bool $hasBorderRight, bool $hasBorderTop, bool $hasBorderBottom, Xls $xls): void
229 {
230 /** @var false|int[] */
231 $valueArray = unpack('V', $options);
232 $value = is_array($valueArray) ? $valueArray[1] : 0;
233 $left = $value & 15;
234 $right = ($value >> 4) & 15;
235 $top = ($value >> 8) & 15;
236 $bottom = ($value >> 12) & 15;
237 $leftc = ($value >> 16) & 0x7F;
238 $rightc = ($value >> 23) & 0x7F;
239 /** @var false|int[] */
240 $valueArray = unpack('V', (string) substr($options, 4));
241 $value = is_array($valueArray) ? $valueArray[1] : 0;
242 $topc = $value & 0x7F;
243 $bottomc = ($value & 0x3F80) >> 7;
244 if ($hasBorderLeft) {
245 $style->getBorders()->getLeft()
246 ->setBorderStyle(self::BORDER_STYLE_MAP[$left]);
247 $style->getBorders()->getLeft()->getColor()
248 ->setRGB(Color::map($leftc, $xls->palette, $xls->version)['rgb']);
249 }
250 if ($hasBorderRight) {
251 $style->getBorders()->getRight()
252 ->setBorderStyle(self::BORDER_STYLE_MAP[$right]);
253 $style->getBorders()->getRight()->getColor()
254 ->setRGB(Color::map($rightc, $xls->palette, $xls->version)['rgb']);
255 }
256 if ($hasBorderTop) {
257 $style->getBorders()->getTop()
258 ->setBorderStyle(self::BORDER_STYLE_MAP[$top]);
259 $style->getBorders()->getTop()->getColor()
260 ->setRGB(Color::map($topc, $xls->palette, $xls->version)['rgb']);
261 }
262 if ($hasBorderBottom) {
263 $style->getBorders()->getBottom()
264 ->setBorderStyle(self::BORDER_STYLE_MAP[$bottom]);
265 $style->getBorders()->getBottom()->getColor()
266 ->setRGB(Color::map($bottomc, $xls->palette, $xls->version)['rgb']);
267 }
268 }
269
270 private function getCFFillStyle(string $options, Style $style, Xls $xls): void
271 {
272 $fillPattern = self::getUInt2d($options, 0);
273 // bit: 10-15; mask: 0xFC00; type
274 $fillPattern = (0xFC00 & $fillPattern) >> 10;
275 $fillPattern = FillPattern::lookup($fillPattern);
276 $fillPattern = $fillPattern === Fill::FILL_NONE ? Fill::FILL_SOLID : $fillPattern;
277
278 if ($fillPattern !== Fill::FILL_NONE) {
279 $style->getFill()->setFillType($fillPattern);
280
281 $fillColors = self::getUInt2d($options, 2);
282
283 // bit: 0-6; mask: 0x007F; type
284 $color1 = (0x007F & $fillColors) >> 0;
285
286 // bit: 7-13; mask: 0x3F80; type
287 $color2 = (0x3F80 & $fillColors) >> 7;
288 if ($fillPattern === Fill::FILL_SOLID) {
289 $style->getFill()->getStartColor()->setRGB(Color::map($color2, $xls->palette, $xls->version)['rgb']);
290 } else {
291 $style->getFill()->getStartColor()->setRGB(Color::map($color1, $xls->palette, $xls->version)['rgb']);
292 $style->getFill()->getEndColor()->setRGB(Color::map($color2, $xls->palette, $xls->version)['rgb']);
293 }
294 }
295 }
296
297 /*private function getCFProtectionStyle(string $options, Style $style, Xls $xls): void
298 {
299 }*/
300 /**
301 * @return float|int|string|null
302 */
303 private function readCFFormula(string $recordData, int $offset, int $size, Xls $xls)
304 {
305 try {
306 $formula = (string) substr($recordData, $offset, $size);
307 $formula = pack('v', $size) . $formula; // prepend the length
308
309 $formula = $xls->getFormulaFromStructure($formula);
310 if (is_numeric($formula)) {
311 return (str_contains($formula, '.')) ? (float) $formula : (int) $formula;
312 }
313
314 return $formula;
315 } catch (PhpSpreadsheetException $exception) {
316 return null;
317 }
318 }
319
320 /** @param string[] $cellRanges
321 * @param null|float|int|string $formula1
322 * @param null|float|int|string $formula2 */
323 private function setCFRules(array $cellRanges, string $type, string $operator, $formula1, $formula2, Style $style, bool $noFormatSet, Xls $xls): void
324 {
325 foreach ($cellRanges as $cellRange) {
326 $conditional = new Conditional();
327 $conditional->setNoFormatSet($noFormatSet);
328 $conditional->setConditionType($type);
329 $conditional->setOperatorType($operator);
330 $conditional->setStopIfTrue(true);
331 if ($formula1 !== null) {
332 $conditional->addCondition($formula1);
333 }
334 if ($formula2 !== null) {
335 $conditional->addCondition($formula2);
336 }
337 $conditional->setStyle($style);
338
339 $conditionalStyles = $xls->phpSheet
340 ->getStyle($cellRange)
341 ->getConditionalStyles();
342 $conditionalStyles[] = $conditional;
343
344 $xls->phpSheet
345 ->getStyle($cellRange)
346 ->setConditionalStyles($conditionalStyles);
347 }
348 }
349 }
350