Style.php 27 KB

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