Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]
1 <?php 2 3 namespace PhpOffice\PhpSpreadsheet\Style; 4 5 use PhpOffice\PhpSpreadsheet\Calculation\Functions; 6 use PhpOffice\PhpSpreadsheet\Cell\Coordinate; 7 use PhpOffice\PhpSpreadsheet\Exception; 8 use PhpOffice\PhpSpreadsheet\Shared\StringHelper; 9 use PhpOffice\PhpSpreadsheet\Spreadsheet; 10 11 class Style extends Supervisor 12 { 13 /** 14 * Font. 15 * 16 * @var Font 17 */ 18 protected $font; 19 20 /** 21 * Fill. 22 * 23 * @var Fill 24 */ 25 protected $fill; 26 27 /** 28 * Borders. 29 * 30 * @var Borders 31 */ 32 protected $borders; 33 34 /** 35 * Alignment. 36 * 37 * @var Alignment 38 */ 39 protected $alignment; 40 41 /** 42 * Number Format. 43 * 44 * @var NumberFormat 45 */ 46 protected $numberFormat; 47 48 /** 49 * Protection. 50 * 51 * @var Protection 52 */ 53 protected $protection; 54 55 /** 56 * Index of style in collection. Only used for real style. 57 * 58 * @var int 59 */ 60 protected $index; 61 62 /** 63 * Use Quote Prefix when displaying in cell editor. Only used for real style. 64 * 65 * @var bool 66 */ 67 protected $quotePrefix = false; 68 69 /** 70 * Internal cache for styles 71 * Used when applying style on range of cells (column or row) and cleared when 72 * all cells in range is styled. 73 * 74 * PhpSpreadsheet will always minimize the amount of styles used. So cells with 75 * same styles will reference the same Style instance. To check if two styles 76 * are similar Style::getHashCode() is used. This call is expensive. To minimize 77 * the need to call this method we can cache the internal PHP object id of the 78 * Style in the range. Style::getHashCode() will then only be called when we 79 * encounter a unique style. 80 * 81 * @see Style::applyFromArray() 82 * @see Style::getHashCode() 83 * 84 * @var null|array<string, array> 85 */ 86 private static $cachedStyles; 87 88 /** 89 * Create a new Style. 90 * 91 * @param bool $isSupervisor Flag indicating if this is a supervisor or not 92 * Leave this value at default unless you understand exactly what 93 * its ramifications are 94 * @param bool $isConditional Flag indicating if this is a conditional style or not 95 * Leave this value at default unless you understand exactly what 96 * its ramifications are 97 */ 98 public function __construct($isSupervisor = false, $isConditional = false) 99 { 100 parent::__construct($isSupervisor); 101 102 // Initialise values 103 $this->font = new Font($isSupervisor, $isConditional); 104 $this->fill = new Fill($isSupervisor, $isConditional); 105 $this->borders = new Borders($isSupervisor, $isConditional); 106 $this->alignment = new Alignment($isSupervisor, $isConditional); 107 $this->numberFormat = new NumberFormat($isSupervisor, $isConditional); 108 $this->protection = new Protection($isSupervisor, $isConditional); 109 110 // bind parent if we are a supervisor 111 if ($isSupervisor) { 112 $this->font->bindParent($this); 113 $this->fill->bindParent($this); 114 $this->borders->bindParent($this); 115 $this->alignment->bindParent($this); 116 $this->numberFormat->bindParent($this); 117 $this->protection->bindParent($this); 118 } 119 } 120 121 /** 122 * Get the shared style component for the currently active cell in currently active sheet. 123 * Only used for style supervisor. 124 */ 125 public function getSharedComponent(): self 126 { 127 $activeSheet = $this->getActiveSheet(); 128 $selectedCell = Functions::trimSheetFromCellReference($this->getActiveCell()); // e.g. 'A1' 129 130 if ($activeSheet->cellExists($selectedCell)) { 131 $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex(); 132 } else { 133 $xfIndex = 0; 134 } 135 136 return $activeSheet->getParentOrThrow()->getCellXfByIndex($xfIndex); 137 } 138 139 /** 140 * Get parent. Only used for style supervisor. 141 */ 142 public function getParent(): Spreadsheet 143 { 144 return $this->getActiveSheet()->getParentOrThrow(); 145 } 146 147 /** 148 * Build style array from subcomponents. 149 * 150 * @param array $array 151 * 152 * @return array 153 */ 154 public function getStyleArray($array) 155 { 156 return ['quotePrefix' => $array]; 157 } 158 159 /** 160 * Apply styles from array. 161 * 162 * <code> 163 * $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray( 164 * [ 165 * 'font' => [ 166 * 'name' => 'Arial', 167 * 'bold' => true, 168 * 'italic' => false, 169 * 'underline' => Font::UNDERLINE_DOUBLE, 170 * 'strikethrough' => false, 171 * 'color' => [ 172 * 'rgb' => '808080' 173 * ] 174 * ], 175 * 'borders' => [ 176 * 'bottom' => [ 177 * 'borderStyle' => Border::BORDER_DASHDOT, 178 * 'color' => [ 179 * 'rgb' => '808080' 180 * ] 181 * ], 182 * 'top' => [ 183 * 'borderStyle' => Border::BORDER_DASHDOT, 184 * 'color' => [ 185 * 'rgb' => '808080' 186 * ] 187 * ] 188 * ], 189 * 'alignment' => [ 190 * 'horizontal' => Alignment::HORIZONTAL_CENTER, 191 * 'vertical' => Alignment::VERTICAL_CENTER, 192 * 'wrapText' => true, 193 * ], 194 * 'quotePrefix' => true 195 * ] 196 * ); 197 * </code> 198 * 199 * @param array $styleArray Array containing style information 200 * @param bool $advancedBorders advanced mode for setting borders 201 * 202 * @return $this 203 */ 204 public function applyFromArray(array $styleArray, $advancedBorders = true) 205 { 206 if ($this->isSupervisor) { 207 $pRange = $this->getSelectedCells(); 208 209 // Uppercase coordinate and strip any Worksheet reference from the selected range 210 $pRange = strtoupper($pRange); 211 if (strpos($pRange, '!') !== false) { 212 $pRangeWorksheet = StringHelper::strToUpper(trim(substr($pRange, 0, (int) strrpos($pRange, '!')), "'")); 213 if ($pRangeWorksheet !== '' && StringHelper::strToUpper($this->getActiveSheet()->getTitle()) !== $pRangeWorksheet) { 214 throw new Exception('Invalid Worksheet for specified Range'); 215 } 216 $pRange = strtoupper(Functions::trimSheetFromCellReference($pRange)); 217 } 218 219 // Is it a cell range or a single cell? 220 if (strpos($pRange, ':') === false) { 221 $rangeA = $pRange; 222 $rangeB = $pRange; 223 } else { 224 [$rangeA, $rangeB] = explode(':', $pRange); 225 } 226 227 // Calculate range outer borders 228 $rangeStart = Coordinate::coordinateFromString($rangeA); 229 $rangeEnd = Coordinate::coordinateFromString($rangeB); 230 $rangeStartIndexes = Coordinate::indexesFromString($rangeA); 231 $rangeEndIndexes = Coordinate::indexesFromString($rangeB); 232 233 $columnStart = $rangeStart[0]; 234 $columnEnd = $rangeEnd[0]; 235 236 // Make sure we can loop upwards on rows and columns 237 if ($rangeStartIndexes[0] > $rangeEndIndexes[0] && $rangeStartIndexes[1] > $rangeEndIndexes[1]) { 238 $tmp = $rangeStartIndexes; 239 $rangeStartIndexes = $rangeEndIndexes; 240 $rangeEndIndexes = $tmp; 241 } 242 243 // ADVANCED MODE: 244 if ($advancedBorders && isset($styleArray['borders'])) { 245 // 'allBorders' is a shorthand property for 'outline' and 'inside' and 246 // it applies to components that have not been set explicitly 247 if (isset($styleArray['borders']['allBorders'])) { 248 foreach (['outline', 'inside'] as $component) { 249 if (!isset($styleArray['borders'][$component])) { 250 $styleArray['borders'][$component] = $styleArray['borders']['allBorders']; 251 } 252 } 253 unset($styleArray['borders']['allBorders']); // not needed any more 254 } 255 // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left' 256 // it applies to components that have not been set explicitly 257 if (isset($styleArray['borders']['outline'])) { 258 foreach (['top', 'right', 'bottom', 'left'] as $component) { 259 if (!isset($styleArray['borders'][$component])) { 260 $styleArray['borders'][$component] = $styleArray['borders']['outline']; 261 } 262 } 263 unset($styleArray['borders']['outline']); // not needed any more 264 } 265 // 'inside' is a shorthand property for 'vertical' and 'horizontal' 266 // it applies to components that have not been set explicitly 267 if (isset($styleArray['borders']['inside'])) { 268 foreach (['vertical', 'horizontal'] as $component) { 269 if (!isset($styleArray['borders'][$component])) { 270 $styleArray['borders'][$component] = $styleArray['borders']['inside']; 271 } 272 } 273 unset($styleArray['borders']['inside']); // not needed any more 274 } 275 // width and height characteristics of selection, 1, 2, or 3 (for 3 or more) 276 $xMax = min($rangeEndIndexes[0] - $rangeStartIndexes[0] + 1, 3); 277 $yMax = min($rangeEndIndexes[1] - $rangeStartIndexes[1] + 1, 3); 278 279 // loop through up to 3 x 3 = 9 regions 280 for ($x = 1; $x <= $xMax; ++$x) { 281 // start column index for region 282 $colStart = ($x == 3) ? 283 Coordinate::stringFromColumnIndex($rangeEndIndexes[0]) 284 : Coordinate::stringFromColumnIndex($rangeStartIndexes[0] + $x - 1); 285 // end column index for region 286 $colEnd = ($x == 1) ? 287 Coordinate::stringFromColumnIndex($rangeStartIndexes[0]) 288 : Coordinate::stringFromColumnIndex($rangeEndIndexes[0] - $xMax + $x); 289 290 for ($y = 1; $y <= $yMax; ++$y) { 291 // which edges are touching the region 292 $edges = []; 293 if ($x == 1) { 294 // are we at left edge 295 $edges[] = 'left'; 296 } 297 if ($x == $xMax) { 298 // are we at right edge 299 $edges[] = 'right'; 300 } 301 if ($y == 1) { 302 // are we at top edge? 303 $edges[] = 'top'; 304 } 305 if ($y == $yMax) { 306 // are we at bottom edge? 307 $edges[] = 'bottom'; 308 } 309 310 // start row index for region 311 $rowStart = ($y == 3) ? 312 $rangeEndIndexes[1] : $rangeStartIndexes[1] + $y - 1; 313 314 // end row index for region 315 $rowEnd = ($y == 1) ? 316 $rangeStartIndexes[1] : $rangeEndIndexes[1] - $yMax + $y; 317 318 // build range for region 319 $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd; 320 321 // retrieve relevant style array for region 322 $regionStyles = $styleArray; 323 unset($regionStyles['borders']['inside']); 324 325 // what are the inner edges of the region when looking at the selection 326 $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges); 327 328 // inner edges that are not touching the region should take the 'inside' border properties if they have been set 329 foreach ($innerEdges as $innerEdge) { 330 switch ($innerEdge) { 331 case 'top': 332 case 'bottom': 333 // should pick up 'horizontal' border property if set 334 if (isset($styleArray['borders']['horizontal'])) { 335 $regionStyles['borders'][$innerEdge] = $styleArray['borders']['horizontal']; 336 } else { 337 unset($regionStyles['borders'][$innerEdge]); 338 } 339 340 break; 341 case 'left': 342 case 'right': 343 // should pick up 'vertical' border property if set 344 if (isset($styleArray['borders']['vertical'])) { 345 $regionStyles['borders'][$innerEdge] = $styleArray['borders']['vertical']; 346 } else { 347 unset($regionStyles['borders'][$innerEdge]); 348 } 349 350 break; 351 } 352 } 353 354 // apply region style to region by calling applyFromArray() in simple mode 355 $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false); 356 } 357 } 358 359 // restore initial cell selection range 360 $this->getActiveSheet()->getStyle($pRange); 361 362 return $this; 363 } 364 365 // SIMPLE MODE: 366 // Selection type, inspect 367 if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) { 368 $selectionType = 'COLUMN'; 369 370 // Enable caching of styles 371 self::$cachedStyles = ['hashByObjId' => [], 'styleByHash' => []]; 372 } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) { 373 $selectionType = 'ROW'; 374 375 // Enable caching of styles 376 self::$cachedStyles = ['hashByObjId' => [], 'styleByHash' => []]; 377 } else { 378 $selectionType = 'CELL'; 379 } 380 381 // First loop through columns, rows, or cells to find out which styles are affected by this operation 382 $oldXfIndexes = $this->getOldXfIndexes($selectionType, $rangeStartIndexes, $rangeEndIndexes, $columnStart, $columnEnd, $styleArray); 383 384 // clone each of the affected styles, apply the style array, and add the new styles to the workbook 385 $workbook = $this->getActiveSheet()->getParentOrThrow(); 386 $newXfIndexes = []; 387 foreach ($oldXfIndexes as $oldXfIndex => $dummy) { 388 $style = $workbook->getCellXfByIndex($oldXfIndex); 389 390 // $cachedStyles is set when applying style for a range of cells, either column or row 391 if (self::$cachedStyles === null) { 392 // Clone the old style and apply style-array 393 $newStyle = clone $style; 394 $newStyle->applyFromArray($styleArray); 395 396 // Look for existing style we can use instead (reduce memory usage) 397 $existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode()); 398 } else { 399 // Style cache is stored by Style::getHashCode(). But calling this method is 400 // expensive. So we cache the php obj id -> hash. 401 $objId = spl_object_id($style); 402 403 // Look for the original HashCode 404 $styleHash = self::$cachedStyles['hashByObjId'][$objId] ?? null; 405 if ($styleHash === null) { 406 // This object_id is not cached, store the hashcode in case encounter again 407 $styleHash = self::$cachedStyles['hashByObjId'][$objId] = $style->getHashCode(); 408 } 409 410 // Find existing style by hash. 411 $existingStyle = self::$cachedStyles['styleByHash'][$styleHash] ?? null; 412 413 if (!$existingStyle) { 414 // The old style combined with the new style array is not cached, so we create it now 415 $newStyle = clone $style; 416 $newStyle->applyFromArray($styleArray); 417 418 // Look for similar style in workbook to reduce memory usage 419 $existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode()); 420 421 // Cache the new style by original hashcode 422 self::$cachedStyles['styleByHash'][$styleHash] = $existingStyle instanceof self ? $existingStyle : $newStyle; 423 } 424 } 425 426 if ($existingStyle) { 427 // there is already such cell Xf in our collection 428 $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex(); 429 } else { 430 if (!isset($newStyle)) { 431 // Handle bug in PHPStan, see https://github.com/phpstan/phpstan/issues/5805 432 // $newStyle should always be defined. 433 // This block might not be needed in the future 434 // @codeCoverageIgnoreStart 435 $newStyle = clone $style; 436 $newStyle->applyFromArray($styleArray); 437 // @codeCoverageIgnoreEnd 438 } 439 440 // we don't have such a cell Xf, need to add 441 $workbook->addCellXf($newStyle); 442 $newXfIndexes[$oldXfIndex] = $newStyle->getIndex(); 443 } 444 } 445 446 // Loop through columns, rows, or cells again and update the XF index 447 switch ($selectionType) { 448 case 'COLUMN': 449 for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) { 450 $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col); 451 $oldXfIndex = $columnDimension->getXfIndex(); 452 $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]); 453 } 454 455 // Disable caching of styles 456 self::$cachedStyles = null; 457 458 break; 459 case 'ROW': 460 for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) { 461 $rowDimension = $this->getActiveSheet()->getRowDimension($row); 462 // row without explicit style should be formatted based on default style 463 $oldXfIndex = $rowDimension->getXfIndex() ?? 0; 464 $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]); 465 } 466 467 // Disable caching of styles 468 self::$cachedStyles = null; 469 470 break; 471 case 'CELL': 472 for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) { 473 for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) { 474 $cell = $this->getActiveSheet()->getCell([$col, $row]); 475 $oldXfIndex = $cell->getXfIndex(); 476 $cell->setXfIndex($newXfIndexes[$oldXfIndex]); 477 } 478 } 479 480 break; 481 } 482 } else { 483 // not a supervisor, just apply the style array directly on style object 484 if (isset($styleArray['fill'])) { 485 $this->getFill()->applyFromArray($styleArray['fill']); 486 } 487 if (isset($styleArray['font'])) { 488 $this->getFont()->applyFromArray($styleArray['font']); 489 } 490 if (isset($styleArray['borders'])) { 491 $this->getBorders()->applyFromArray($styleArray['borders']); 492 } 493 if (isset($styleArray['alignment'])) { 494 $this->getAlignment()->applyFromArray($styleArray['alignment']); 495 } 496 if (isset($styleArray['numberFormat'])) { 497 $this->getNumberFormat()->applyFromArray($styleArray['numberFormat']); 498 } 499 if (isset($styleArray['protection'])) { 500 $this->getProtection()->applyFromArray($styleArray['protection']); 501 } 502 if (isset($styleArray['quotePrefix'])) { 503 $this->quotePrefix = $styleArray['quotePrefix']; 504 } 505 } 506 507 return $this; 508 } 509 510 private function getOldXfIndexes(string $selectionType, array $rangeStart, array $rangeEnd, string $columnStart, string $columnEnd, array $styleArray): array 511 { 512 $oldXfIndexes = []; 513 switch ($selectionType) { 514 case 'COLUMN': 515 for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { 516 $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true; 517 } 518 foreach ($this->getActiveSheet()->getColumnIterator($columnStart, $columnEnd) as $columnIterator) { 519 $cellIterator = $columnIterator->getCellIterator(); 520 $cellIterator->setIterateOnlyExistingCells(true); 521 foreach ($cellIterator as $columnCell) { 522 if ($columnCell !== null) { 523 $columnCell->getStyle()->applyFromArray($styleArray); 524 } 525 } 526 } 527 528 break; 529 case 'ROW': 530 for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { 531 if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() === null) { 532 $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style 533 } else { 534 $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true; 535 } 536 } 537 foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) { 538 $cellIterator = $rowIterator->getCellIterator(); 539 $cellIterator->setIterateOnlyExistingCells(true); 540 foreach ($cellIterator as $rowCell) { 541 if ($rowCell !== null) { 542 $rowCell->getStyle()->applyFromArray($styleArray); 543 } 544 } 545 } 546 547 break; 548 case 'CELL': 549 for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) { 550 for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) { 551 $oldXfIndexes[$this->getActiveSheet()->getCell([$col, $row])->getXfIndex()] = true; 552 } 553 } 554 555 break; 556 } 557 558 return $oldXfIndexes; 559 } 560 561 /** 562 * Get Fill. 563 * 564 * @return Fill 565 */ 566 public function getFill() 567 { 568 return $this->fill; 569 } 570 571 /** 572 * Get Font. 573 * 574 * @return Font 575 */ 576 public function getFont() 577 { 578 return $this->font; 579 } 580 581 /** 582 * Set font. 583 * 584 * @return $this 585 */ 586 public function setFont(Font $font) 587 { 588 $this->font = $font; 589 590 return $this; 591 } 592 593 /** 594 * Get Borders. 595 * 596 * @return Borders 597 */ 598 public function getBorders() 599 { 600 return $this->borders; 601 } 602 603 /** 604 * Get Alignment. 605 * 606 * @return Alignment 607 */ 608 public function getAlignment() 609 { 610 return $this->alignment; 611 } 612 613 /** 614 * Get Number Format. 615 * 616 * @return NumberFormat 617 */ 618 public function getNumberFormat() 619 { 620 return $this->numberFormat; 621 } 622 623 /** 624 * Get Conditional Styles. Only used on supervisor. 625 * 626 * @return Conditional[] 627 */ 628 public function getConditionalStyles() 629 { 630 return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell()); 631 } 632 633 /** 634 * Set Conditional Styles. Only used on supervisor. 635 * 636 * @param Conditional[] $conditionalStyleArray Array of conditional styles 637 * 638 * @return $this 639 */ 640 public function setConditionalStyles(array $conditionalStyleArray) 641 { 642 $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $conditionalStyleArray); 643 644 return $this; 645 } 646 647 /** 648 * Get Protection. 649 * 650 * @return Protection 651 */ 652 public function getProtection() 653 { 654 return $this->protection; 655 } 656 657 /** 658 * Get quote prefix. 659 * 660 * @return bool 661 */ 662 public function getQuotePrefix() 663 { 664 if ($this->isSupervisor) { 665 return $this->getSharedComponent()->getQuotePrefix(); 666 } 667 668 return $this->quotePrefix; 669 } 670 671 /** 672 * Set quote prefix. 673 * 674 * @param bool $quotePrefix 675 * 676 * @return $this 677 */ 678 public function setQuotePrefix($quotePrefix) 679 { 680 if ($quotePrefix == '') { 681 $quotePrefix = false; 682 } 683 if ($this->isSupervisor) { 684 $styleArray = ['quotePrefix' => $quotePrefix]; 685 $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray); 686 } else { 687 $this->quotePrefix = (bool) $quotePrefix; 688 } 689 690 return $this; 691 } 692 693 /** 694 * Get hash code. 695 * 696 * @return string Hash code 697 */ 698 public function getHashCode() 699 { 700 return md5( 701 $this->fill->getHashCode() . 702 $this->font->getHashCode() . 703 $this->borders->getHashCode() . 704 $this->alignment->getHashCode() . 705 $this->numberFormat->getHashCode() . 706 $this->protection->getHashCode() . 707 ($this->quotePrefix ? 't' : 'f') . 708 __CLASS__ 709 ); 710 } 711 712 /** 713 * Get own index in style collection. 714 * 715 * @return int 716 */ 717 public function getIndex() 718 { 719 return $this->index; 720 } 721 722 /** 723 * Set own index in style collection. 724 * 725 * @param int $index 726 */ 727 public function setIndex($index): void 728 { 729 $this->index = $index; 730 } 731 732 protected function exportArray1(): array 733 { 734 $exportedArray = []; 735 $this->exportArray2($exportedArray, 'alignment', $this->getAlignment()); 736 $this->exportArray2($exportedArray, 'borders', $this->getBorders()); 737 $this->exportArray2($exportedArray, 'fill', $this->getFill()); 738 $this->exportArray2($exportedArray, 'font', $this->getFont()); 739 $this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat()); 740 $this->exportArray2($exportedArray, 'protection', $this->getProtection()); 741 $this->exportArray2($exportedArray, 'quotePrefx', $this->getQuotePrefix()); 742 743 return $exportedArray; 744 } 745 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body