ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Style.php
Go to the documentation of this file.
1 <?php
2 
4 
7 
8 class 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 }
setConditionalStyles(array $pValue)
Set Conditional Styles.
Definition: Style.php:559
$style
Definition: example_012.php:70
getStyleArray($array)
Build style array from subcomponents.
Definition: Style.php:136
setQuotePrefix($pValue)
Set quote prefix.
Definition: Style.php:597
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
applyFromArray(array $pStyles, $pAdvanced=true)
Apply styles from array.
Definition: Style.php:186
getIndex()
Get own index in style collection.
Definition: Style.php:636
getSelectedCells()
Get the currently active cell coordinate in currently active sheet.
Definition: Supervisor.php:87
$y
Definition: example_007.php:83
getQuotePrefix()
Get quote prefix.
Definition: Style.php:581
getProtection()
Get Protection.
Definition: Style.php:571
static indexesFromString(string $coordinates)
Get indexes from a string coordinates.
Definition: Coordinate.php:52
getOldXfIndexes(string $selectionType, array $rangeStart, array $rangeEnd, string $columnStart, string $columnEnd, array $pStyles)
Definition: Style.php:429
getNumberFormat()
Get Number Format.
Definition: Style.php:537
$row
static coordinateFromString($pCoordinateString)
Coordinate from string.
Definition: Coordinate.php:32
getActiveSheet()
Get the currently active sheet.
Definition: Supervisor.php:76
setFont(Font $font)
Set font.
Definition: Style.php:505
__construct($isSupervisor=false, $isConditional=false)
Create a new Style.
Definition: Style.php:76
getSharedComponent()
Get the shared style component for the currently active cell in currently active sheet.
Definition: Style.php:105
$x
Definition: complexTest.php:9
static stringFromColumnIndex($columnIndex)
String from column index.
Definition: Coordinate.php:313
exportArray2(array &$exportedArray, string $index, $objOrValue)
Populate array from exportArray1.
Definition: Supervisor.php:150
getConditionalStyles()
Get Conditional Styles.
Definition: Style.php:547