ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Style.php
Go to the documentation of this file.
1<?php
2
4
7
8class Style extends Supervisor
9{
15 protected $font;
16
22 protected $fill;
23
29 protected $borders;
30
36 protected $alignment;
37
43 protected $numberFormat;
44
50 protected $protection;
51
57 protected $index;
58
64 protected $quotePrefix = false;
65
76 public function __construct($isSupervisor = false, $isConditional = false)
77 {
78 parent::__construct($isSupervisor);
79
80 // Initialise values
81 $this->font = new Font($isSupervisor, $isConditional);
82 $this->fill = new Fill($isSupervisor, $isConditional);
83 $this->borders = new Borders($isSupervisor);
84 $this->alignment = new Alignment($isSupervisor, $isConditional);
85 $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
86 $this->protection = new Protection($isSupervisor, $isConditional);
87
88 // bind parent if we are a supervisor
89 if ($isSupervisor) {
90 $this->font->bindParent($this);
91 $this->fill->bindParent($this);
92 $this->borders->bindParent($this);
93 $this->alignment->bindParent($this);
94 $this->numberFormat->bindParent($this);
95 $this->protection->bindParent($this);
96 }
97 }
98
105 public function getSharedComponent()
106 {
107 $activeSheet = $this->getActiveSheet();
108 $selectedCell = $this->getActiveCell(); // e.g. 'A1'
109
110 if ($activeSheet->cellExists($selectedCell)) {
111 $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
112 } else {
113 $xfIndex = 0;
114 }
115
116 return $this->parent->getCellXfByIndex($xfIndex);
117 }
118
124 public function getParent()
125 {
126 return $this->parent;
127 }
128
136 public function getStyleArray($array)
137 {
138 return ['quotePrefix' => $array];
139 }
140
186 public function applyFromArray(array $pStyles, $pAdvanced = true)
187 {
188 if ($this->isSupervisor) {
189 $pRange = $this->getSelectedCells();
190
191 // Uppercase coordinate
192 $pRange = strtoupper($pRange);
193
194 // Is it a cell range or a single cell?
195 if (strpos($pRange, ':') === false) {
196 $rangeA = $pRange;
197 $rangeB = $pRange;
198 } else {
199 [$rangeA, $rangeB] = explode(':', $pRange);
200 }
201
202 // Calculate range outer borders
203 $rangeStart = Coordinate::coordinateFromString($rangeA);
204 $rangeEnd = Coordinate::coordinateFromString($rangeB);
205 $rangeStartIndexes = Coordinate::indexesFromString($rangeA);
206 $rangeEndIndexes = Coordinate::indexesFromString($rangeB);
207
208 $columnStart = $rangeStart[0];
209 $columnEnd = $rangeEnd[0];
210
211 // Make sure we can loop upwards on rows and columns
212 if ($rangeStartIndexes[0] > $rangeEndIndexes[0] && $rangeStartIndexes[1] > $rangeEndIndexes[1]) {
213 $tmp = $rangeStartIndexes;
214 $rangeStartIndexes = $rangeEndIndexes;
215 $rangeEndIndexes = $tmp;
216 }
217
218 // ADVANCED MODE:
219 if ($pAdvanced && isset($pStyles['borders'])) {
220 // 'allBorders' is a shorthand property for 'outline' and 'inside' and
221 // it applies to components that have not been set explicitly
222 if (isset($pStyles['borders']['allBorders'])) {
223 foreach (['outline', 'inside'] as $component) {
224 if (!isset($pStyles['borders'][$component])) {
225 $pStyles['borders'][$component] = $pStyles['borders']['allBorders'];
226 }
227 }
228 unset($pStyles['borders']['allBorders']); // not needed any more
229 }
230 // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
231 // it applies to components that have not been set explicitly
232 if (isset($pStyles['borders']['outline'])) {
233 foreach (['top', 'right', 'bottom', 'left'] as $component) {
234 if (!isset($pStyles['borders'][$component])) {
235 $pStyles['borders'][$component] = $pStyles['borders']['outline'];
236 }
237 }
238 unset($pStyles['borders']['outline']); // not needed any more
239 }
240 // 'inside' is a shorthand property for 'vertical' and 'horizontal'
241 // it applies to components that have not been set explicitly
242 if (isset($pStyles['borders']['inside'])) {
243 foreach (['vertical', 'horizontal'] as $component) {
244 if (!isset($pStyles['borders'][$component])) {
245 $pStyles['borders'][$component] = $pStyles['borders']['inside'];
246 }
247 }
248 unset($pStyles['borders']['inside']); // not needed any more
249 }
250 // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
251 $xMax = min($rangeEndIndexes[0] - $rangeStartIndexes[0] + 1, 3);
252 $yMax = min($rangeEndIndexes[1] - $rangeStartIndexes[1] + 1, 3);
253
254 // loop through up to 3 x 3 = 9 regions
255 for ($x = 1; $x <= $xMax; ++$x) {
256 // start column index for region
257 $colStart = ($x == 3) ?
258 Coordinate::stringFromColumnIndex($rangeEndIndexes[0])
259 : Coordinate::stringFromColumnIndex($rangeStartIndexes[0] + $x - 1);
260 // end column index for region
261 $colEnd = ($x == 1) ?
262 Coordinate::stringFromColumnIndex($rangeStartIndexes[0])
263 : Coordinate::stringFromColumnIndex($rangeEndIndexes[0] - $xMax + $x);
264
265 for ($y = 1; $y <= $yMax; ++$y) {
266 // which edges are touching the region
267 $edges = [];
268 if ($x == 1) {
269 // are we at left edge
270 $edges[] = 'left';
271 }
272 if ($x == $xMax) {
273 // are we at right edge
274 $edges[] = 'right';
275 }
276 if ($y == 1) {
277 // are we at top edge?
278 $edges[] = 'top';
279 }
280 if ($y == $yMax) {
281 // are we at bottom edge?
282 $edges[] = 'bottom';
283 }
284
285 // start row index for region
286 $rowStart = ($y == 3) ?
287 $rangeEndIndexes[1] : $rangeStartIndexes[1] + $y - 1;
288
289 // end row index for region
290 $rowEnd = ($y == 1) ?
291 $rangeStartIndexes[1] : $rangeEndIndexes[1] - $yMax + $y;
292
293 // build range for region
294 $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
295
296 // retrieve relevant style array for region
297 $regionStyles = $pStyles;
298 unset($regionStyles['borders']['inside']);
299
300 // what are the inner edges of the region when looking at the selection
301 $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);
302
303 // inner edges that are not touching the region should take the 'inside' border properties if they have been set
304 foreach ($innerEdges as $innerEdge) {
305 switch ($innerEdge) {
306 case 'top':
307 case 'bottom':
308 // should pick up 'horizontal' border property if set
309 if (isset($pStyles['borders']['horizontal'])) {
310 $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
311 } else {
312 unset($regionStyles['borders'][$innerEdge]);
313 }
314
315 break;
316 case 'left':
317 case 'right':
318 // should pick up 'vertical' border property if set
319 if (isset($pStyles['borders']['vertical'])) {
320 $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
321 } else {
322 unset($regionStyles['borders'][$innerEdge]);
323 }
324
325 break;
326 }
327 }
328
329 // apply region style to region by calling applyFromArray() in simple mode
330 $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
331 }
332 }
333
334 // restore initial cell selection range
335 $this->getActiveSheet()->getStyle($pRange);
336
337 return $this;
338 }
339
340 // SIMPLE MODE:
341 // Selection type, inspect
342 if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
343 $selectionType = 'COLUMN';
344 } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
345 $selectionType = 'ROW';
346 } else {
347 $selectionType = 'CELL';
348 }
349
350 // First loop through columns, rows, or cells to find out which styles are affected by this operation
351 $oldXfIndexes = $this->getOldXfIndexes($selectionType, $rangeStartIndexes, $rangeEndIndexes, $columnStart, $columnEnd, $pStyles);
352
353 // clone each of the affected styles, apply the style array, and add the new styles to the workbook
354 $workbook = $this->getActiveSheet()->getParent();
355 $newXfIndexes = [];
356 foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
357 $style = $workbook->getCellXfByIndex($oldXfIndex);
358 $newStyle = clone $style;
359 $newStyle->applyFromArray($pStyles);
360
361 if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
362 // there is already such cell Xf in our collection
363 $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
364 } else {
365 // we don't have such a cell Xf, need to add
366 $workbook->addCellXf($newStyle);
367 $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
368 }
369 }
370
371 // Loop through columns, rows, or cells again and update the XF index
372 switch ($selectionType) {
373 case 'COLUMN':
374 for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) {
375 $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
376 $oldXfIndex = $columnDimension->getXfIndex();
377 $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
378 }
379
380 break;
381 case 'ROW':
382 for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) {
383 $rowDimension = $this->getActiveSheet()->getRowDimension($row);
384 // row without explicit style should be formatted based on default style
385 $oldXfIndex = $rowDimension->getXfIndex() ?? 0;
386 $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
387 }
388
389 break;
390 case 'CELL':
391 for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) {
392 for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) {
393 $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
394 $oldXfIndex = $cell->getXfIndex();
395 $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
396 }
397 }
398
399 break;
400 }
401 } else {
402 // not a supervisor, just apply the style array directly on style object
403 if (isset($pStyles['fill'])) {
404 $this->getFill()->applyFromArray($pStyles['fill']);
405 }
406 if (isset($pStyles['font'])) {
407 $this->getFont()->applyFromArray($pStyles['font']);
408 }
409 if (isset($pStyles['borders'])) {
410 $this->getBorders()->applyFromArray($pStyles['borders']);
411 }
412 if (isset($pStyles['alignment'])) {
413 $this->getAlignment()->applyFromArray($pStyles['alignment']);
414 }
415 if (isset($pStyles['numberFormat'])) {
416 $this->getNumberFormat()->applyFromArray($pStyles['numberFormat']);
417 }
418 if (isset($pStyles['protection'])) {
419 $this->getProtection()->applyFromArray($pStyles['protection']);
420 }
421 if (isset($pStyles['quotePrefix'])) {
422 $this->quotePrefix = $pStyles['quotePrefix'];
423 }
424 }
425
426 return $this;
427 }
428
429 private function getOldXfIndexes(string $selectionType, array $rangeStart, array $rangeEnd, string $columnStart, string $columnEnd, array $pStyles): array
430 {
431 $oldXfIndexes = [];
432 switch ($selectionType) {
433 case 'COLUMN':
434 for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
435 $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
436 }
437 foreach ($this->getActiveSheet()->getColumnIterator($columnStart, $columnEnd) as $columnIterator) {
438 $cellIterator = $columnIterator->getCellIterator();
439 $cellIterator->setIterateOnlyExistingCells(true);
440 foreach ($cellIterator as $columnCell) {
441 if ($columnCell !== null) {
442 $columnCell->getStyle()->applyFromArray($pStyles);
443 }
444 }
445 }
446
447 break;
448 case 'ROW':
449 for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
450 if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() === null) {
451 $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
452 } else {
453 $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
454 }
455 }
456 foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) {
457 $cellIterator = $rowIterator->getCellIterator();
458 $cellIterator->setIterateOnlyExistingCells(true);
459 foreach ($cellIterator as $rowCell) {
460 if ($rowCell !== null) {
461 $rowCell->getStyle()->applyFromArray($pStyles);
462 }
463 }
464 }
465
466 break;
467 case 'CELL':
468 for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
469 for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
470 $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
471 }
472 }
473
474 break;
475 }
476
477 return $oldXfIndexes;
478 }
479
485 public function getFill()
486 {
487 return $this->fill;
488 }
489
495 public function getFont()
496 {
497 return $this->font;
498 }
499
505 public function setFont(Font $font)
506 {
507 $this->font = $font;
508
509 return $this;
510 }
511
517 public function getBorders()
518 {
519 return $this->borders;
520 }
521
527 public function getAlignment()
528 {
529 return $this->alignment;
530 }
531
537 public function getNumberFormat()
538 {
539 return $this->numberFormat;
540 }
541
547 public function getConditionalStyles()
548 {
549 return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
550 }
551
559 public function setConditionalStyles(array $pValue)
560 {
561 $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
562
563 return $this;
564 }
565
571 public function getProtection()
572 {
573 return $this->protection;
574 }
575
581 public function getQuotePrefix()
582 {
583 if ($this->isSupervisor) {
584 return $this->getSharedComponent()->getQuotePrefix();
585 }
586
587 return $this->quotePrefix;
588 }
589
597 public function setQuotePrefix($pValue)
598 {
599 if ($pValue == '') {
600 $pValue = false;
601 }
602 if ($this->isSupervisor) {
603 $styleArray = ['quotePrefix' => $pValue];
604 $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
605 } else {
606 $this->quotePrefix = (bool) $pValue;
607 }
608
609 return $this;
610 }
611
617 public function getHashCode()
618 {
619 return md5(
620 $this->fill->getHashCode() .
621 $this->font->getHashCode() .
622 $this->borders->getHashCode() .
623 $this->alignment->getHashCode() .
624 $this->numberFormat->getHashCode() .
625 $this->protection->getHashCode() .
626 ($this->quotePrefix ? 't' : 'f') .
627 __CLASS__
628 );
629 }
630
636 public function getIndex()
637 {
638 return $this->index;
639 }
640
646 public function setIndex($pValue): void
647 {
648 $this->index = $pValue;
649 }
650
651 protected function exportArray1(): array
652 {
653 $exportedArray = [];
654 $this->exportArray2($exportedArray, 'alignment', $this->getAlignment());
655 $this->exportArray2($exportedArray, 'borders', $this->getBorders());
656 $this->exportArray2($exportedArray, 'fill', $this->getFill());
657 $this->exportArray2($exportedArray, 'font', $this->getFont());
658 $this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat());
659 $this->exportArray2($exportedArray, 'protection', $this->getProtection());
660 $this->exportArray2($exportedArray, 'quotePrefx', $this->getQuotePrefix());
661
662 return $exportedArray;
663 }
664}
An exception for terminatinating execution or to throw for unit testing.
Helper class to manipulate cell coordinates.
Definition: Coordinate.php:15
static coordinateFromString($pCoordinateString)
Coordinate from string.
Definition: Coordinate.php:32
static indexesFromString(string $coordinates)
Get indexes from a string coordinates.
Definition: Coordinate.php:52
static stringFromColumnIndex($columnIndex)
String from column index.
Definition: Coordinate.php:313
getSharedComponent()
Get the shared style component for the currently active cell in currently active sheet.
Definition: Style.php:105
getIndex()
Get own index in style collection.
Definition: Style.php:636
setQuotePrefix($pValue)
Set quote prefix.
Definition: Style.php:597
__construct($isSupervisor=false, $isConditional=false)
Create a new Style.
Definition: Style.php:76
applyFromArray(array $pStyles, $pAdvanced=true)
Apply styles from array.
Definition: Style.php:186
getNumberFormat()
Get Number Format.
Definition: Style.php:537
getConditionalStyles()
Get Conditional Styles.
Definition: Style.php:547
exportArray1()
Abstract method to be implemented in anything which extends this class.
Definition: Style.php:651
getOldXfIndexes(string $selectionType, array $rangeStart, array $rangeEnd, string $columnStart, string $columnEnd, array $pStyles)
Definition: Style.php:429
getProtection()
Get Protection.
Definition: Style.php:571
setConditionalStyles(array $pValue)
Set Conditional Styles.
Definition: Style.php:559
getQuotePrefix()
Get quote prefix.
Definition: Style.php:581
getStyleArray($array)
Build style array from subcomponents.
Definition: Style.php:136
setFont(Font $font)
Set font.
Definition: Style.php:505
setIndex($pValue)
Set own index in style collection.
Definition: Style.php:646
getActiveCell()
Get the currently active cell coordinate in currently active sheet.
Definition: Supervisor.php:98
getActiveSheet()
Get the currently active sheet.
Definition: Supervisor.php:76
getSelectedCells()
Get the currently active cell coordinate in currently active sheet.
Definition: Supervisor.php:87
exportArray2(array &$exportedArray, string $index, $objOrValue)
Populate array from exportArray1.
Definition: Supervisor.php:150
$x
Definition: complexTest.php:9
$y
Definition: example_007.php:83
$style
Definition: example_012.php:70
$row