Style.php 26 KB

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