Style.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. class PHPExcel_Style extends PHPExcel_Style_Supervisor implements PHPExcel_IComparable
  8. {
  9. /**
  10. * Font
  11. *
  12. * @var PHPExcel_Style_Font
  13. */
  14. protected $font;
  15. /**
  16. * Fill
  17. *
  18. * @var PHPExcel_Style_Fill
  19. */
  20. protected $fill;
  21. /**
  22. * Borders
  23. *
  24. * @var PHPExcel_Style_Borders
  25. */
  26. protected $borders;
  27. /**
  28. * Alignment
  29. *
  30. * @var PHPExcel_Style_Alignment
  31. */
  32. protected $alignment;
  33. /**
  34. * Number Format
  35. *
  36. * @var PHPExcel_Style_NumberFormat
  37. */
  38. protected $numberFormat;
  39. /**
  40. * Conditional styles
  41. *
  42. * @var PHPExcel_Style_Conditional[]
  43. */
  44. protected $conditionalStyles;
  45. /**
  46. * Protection
  47. *
  48. * @var PHPExcel_Style_Protection
  49. */
  50. protected $protection;
  51. /**
  52. * Index of style in collection. Only used for real style.
  53. *
  54. * @var int
  55. */
  56. protected $index;
  57. /**
  58. * Use Quote Prefix when displaying in cell editor. Only used for real style.
  59. *
  60. * @var boolean
  61. */
  62. protected $quotePrefix = false;
  63. /**
  64. * Create a new PHPExcel_Style
  65. *
  66. * @param boolean $isSupervisor Flag indicating if this is a supervisor or not
  67. * Leave this value at default unless you understand exactly what
  68. * its ramifications are
  69. * @param boolean $isConditional Flag indicating if this is a conditional style or not
  70. * Leave this value at default unless you understand exactly what
  71. * its ramifications are
  72. */
  73. public function __construct($isSupervisor = false, $isConditional = false)
  74. {
  75. // Supervisor?
  76. $this->isSupervisor = $isSupervisor;
  77. // Initialise values
  78. $this->conditionalStyles = array();
  79. $this->font = new PHPExcel_Style_Font($isSupervisor, $isConditional);
  80. $this->fill = new PHPExcel_Style_Fill($isSupervisor, $isConditional);
  81. $this->borders = new PHPExcel_Style_Borders($isSupervisor, $isConditional);
  82. $this->alignment = new PHPExcel_Style_Alignment($isSupervisor, $isConditional);
  83. $this->numberFormat = new PHPExcel_Style_NumberFormat($isSupervisor, $isConditional);
  84. $this->protection = new PHPExcel_Style_Protection($isSupervisor, $isConditional);
  85. // bind parent if we are a supervisor
  86. if ($isSupervisor) {
  87. $this->font->bindParent($this);
  88. $this->fill->bindParent($this);
  89. $this->borders->bindParent($this);
  90. $this->alignment->bindParent($this);
  91. $this->numberFormat->bindParent($this);
  92. $this->protection->bindParent($this);
  93. }
  94. }
  95. /**
  96. * Get the shared style component for the currently active cell in currently active sheet.
  97. * Only used for style supervisor
  98. *
  99. * @return PHPExcel_Style
  100. */
  101. public function getSharedComponent()
  102. {
  103. $activeSheet = $this->getActiveSheet();
  104. $selectedCell = $this->getActiveCell(); // e.g. 'A1'
  105. if ($activeSheet->cellExists($selectedCell)) {
  106. $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
  107. } else {
  108. $xfIndex = 0;
  109. }
  110. return $this->parent->getCellXfByIndex($xfIndex);
  111. }
  112. /**
  113. * Get parent. Only used for style supervisor
  114. *
  115. * @return PHPExcel
  116. */
  117. public function getParent()
  118. {
  119. return $this->parent;
  120. }
  121. /**
  122. * Build style array from subcomponents
  123. *
  124. * @param array $array
  125. * @return array
  126. */
  127. public function getStyleArray($array)
  128. {
  129. return array('quotePrefix' => $array);
  130. }
  131. /**
  132. * Apply styles from array
  133. *
  134. * <code>
  135. * $objPHPExcel->getActiveSheet()->getStyle('B2')->applyFromArray(
  136. * array(
  137. * 'font' => array(
  138. * 'name' => 'Arial',
  139. * 'bold' => true,
  140. * 'italic' => false,
  141. * 'underline' => PHPExcel_Style_Font::UNDERLINE_DOUBLE,
  142. * 'strike' => false,
  143. * 'color' => array(
  144. * 'rgb' => '808080'
  145. * )
  146. * ),
  147. * 'borders' => array(
  148. * 'bottom' => array(
  149. * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
  150. * 'color' => array(
  151. * 'rgb' => '808080'
  152. * )
  153. * ),
  154. * 'top' => array(
  155. * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
  156. * 'color' => array(
  157. * 'rgb' => '808080'
  158. * )
  159. * )
  160. * ),
  161. * 'quotePrefix' => true
  162. * )
  163. * );
  164. * </code>
  165. *
  166. * @param array $pStyles Array containing style information
  167. * @param boolean $pAdvanced Advanced mode for setting borders.
  168. * @throws PHPExcel_Exception
  169. * @return PHPExcel_Style
  170. */
  171. public function applyFromArray($pStyles = null, $pAdvanced = true)
  172. {
  173. if (is_array($pStyles)) {
  174. if ($this->isSupervisor) {
  175. $pRange = $this->getSelectedCells();
  176. // Uppercase coordinate
  177. $pRange = strtoupper($pRange);
  178. // Is it a cell range or a single cell?
  179. if (strpos($pRange, ':') === false) {
  180. $rangeA = $pRange;
  181. $rangeB = $pRange;
  182. } else {
  183. list($rangeA, $rangeB) = explode(':', $pRange);
  184. }
  185. // Calculate range outer borders
  186. $rangeStart = PHPExcel_Cell::coordinateFromString($rangeA);
  187. $rangeEnd = PHPExcel_Cell::coordinateFromString($rangeB);
  188. // Translate column into index
  189. $rangeStart[0] = PHPExcel_Cell::columnIndexFromString($rangeStart[0]) - 1;
  190. $rangeEnd[0] = PHPExcel_Cell::columnIndexFromString($rangeEnd[0]) - 1;
  191. // Make sure we can loop upwards on rows and columns
  192. if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
  193. $tmp = $rangeStart;
  194. $rangeStart = $rangeEnd;
  195. $rangeEnd = $tmp;
  196. }
  197. // ADVANCED MODE:
  198. if ($pAdvanced && isset($pStyles['borders'])) {
  199. // 'allborders' is a shorthand property for 'outline' and 'inside' and
  200. // it applies to components that have not been set explicitly
  201. if (isset($pStyles['borders']['allborders'])) {
  202. foreach (array('outline', 'inside') as $component) {
  203. if (!isset($pStyles['borders'][$component])) {
  204. $pStyles['borders'][$component] = $pStyles['borders']['allborders'];
  205. }
  206. }
  207. unset($pStyles['borders']['allborders']); // not needed any more
  208. }
  209. // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
  210. // it applies to components that have not been set explicitly
  211. if (isset($pStyles['borders']['outline'])) {
  212. foreach (array('top', 'right', 'bottom', 'left') as $component) {
  213. if (!isset($pStyles['borders'][$component])) {
  214. $pStyles['borders'][$component] = $pStyles['borders']['outline'];
  215. }
  216. }
  217. unset($pStyles['borders']['outline']); // not needed any more
  218. }
  219. // 'inside' is a shorthand property for 'vertical' and 'horizontal'
  220. // it applies to components that have not been set explicitly
  221. if (isset($pStyles['borders']['inside'])) {
  222. foreach (array('vertical', 'horizontal') as $component) {
  223. if (!isset($pStyles['borders'][$component])) {
  224. $pStyles['borders'][$component] = $pStyles['borders']['inside'];
  225. }
  226. }
  227. unset($pStyles['borders']['inside']); // not needed any more
  228. }
  229. // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
  230. $xMax = min($rangeEnd[0] - $rangeStart[0] + 1, 3);
  231. $yMax = min($rangeEnd[1] - $rangeStart[1] + 1, 3);
  232. // loop through up to 3 x 3 = 9 regions
  233. for ($x = 1; $x <= $xMax; ++$x) {
  234. // start column index for region
  235. $colStart = ($x == 3) ?
  236. PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0])
  237. : PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] + $x - 1);
  238. // end column index for region
  239. $colEnd = ($x == 1) ?
  240. PHPExcel_Cell::stringFromColumnIndex($rangeStart[0])
  241. : PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
  242. for ($y = 1; $y <= $yMax; ++$y) {
  243. // which edges are touching the region
  244. $edges = array();
  245. if ($x == 1) {
  246. // are we at left edge
  247. $edges[] = 'left';
  248. }
  249. if ($x == $xMax) {
  250. // are we at right edge
  251. $edges[] = 'right';
  252. }
  253. if ($y == 1) {
  254. // are we at top edge?
  255. $edges[] = 'top';
  256. }
  257. if ($y == $yMax) {
  258. // are we at bottom edge?
  259. $edges[] = 'bottom';
  260. }
  261. // start row index for region
  262. $rowStart = ($y == 3) ?
  263. $rangeEnd[1] : $rangeStart[1] + $y - 1;
  264. // end row index for region
  265. $rowEnd = ($y == 1) ?
  266. $rangeStart[1] : $rangeEnd[1] - $yMax + $y;
  267. // build range for region
  268. $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
  269. // retrieve relevant style array for region
  270. $regionStyles = $pStyles;
  271. unset($regionStyles['borders']['inside']);
  272. // what are the inner edges of the region when looking at the selection
  273. $innerEdges = array_diff(array('top', 'right', 'bottom', 'left'), $edges);
  274. // inner edges that are not touching the region should take the 'inside' border properties if they have been set
  275. foreach ($innerEdges as $innerEdge) {
  276. switch ($innerEdge) {
  277. case 'top':
  278. case 'bottom':
  279. // should pick up 'horizontal' border property if set
  280. if (isset($pStyles['borders']['horizontal'])) {
  281. $regionStyles['borders'][$innerEdge] = $pStyles['borders']['horizontal'];
  282. } else {
  283. unset($regionStyles['borders'][$innerEdge]);
  284. }
  285. break;
  286. case 'left':
  287. case 'right':
  288. // should pick up 'vertical' border property if set
  289. if (isset($pStyles['borders']['vertical'])) {
  290. $regionStyles['borders'][$innerEdge] = $pStyles['borders']['vertical'];
  291. } else {
  292. unset($regionStyles['borders'][$innerEdge]);
  293. }
  294. break;
  295. }
  296. }
  297. // apply region style to region by calling applyFromArray() in simple mode
  298. $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
  299. }
  300. }
  301. return $this;
  302. }
  303. // SIMPLE MODE:
  304. // Selection type, inspect
  305. if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
  306. $selectionType = 'COLUMN';
  307. } elseif (preg_match('/^A[0-9]+:XFD[0-9]+$/', $pRange)) {
  308. $selectionType = 'ROW';
  309. } else {
  310. $selectionType = 'CELL';
  311. }
  312. // First loop through columns, rows, or cells to find out which styles are affected by this operation
  313. switch ($selectionType) {
  314. case 'COLUMN':
  315. $oldXfIndexes = array();
  316. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  317. $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
  318. }
  319. break;
  320. case 'ROW':
  321. $oldXfIndexes = array();
  322. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  323. if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
  324. $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
  325. } else {
  326. $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
  327. }
  328. }
  329. break;
  330. case 'CELL':
  331. $oldXfIndexes = array();
  332. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  333. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  334. $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
  335. }
  336. }
  337. break;
  338. }
  339. // clone each of the affected styles, apply the style array, and add the new styles to the workbook
  340. $workbook = $this->getActiveSheet()->getParent();
  341. foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
  342. $style = $workbook->getCellXfByIndex($oldXfIndex);
  343. $newStyle = clone $style;
  344. $newStyle->applyFromArray($pStyles);
  345. if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
  346. // there is already such cell Xf in our collection
  347. $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
  348. } else {
  349. // we don't have such a cell Xf, need to add
  350. $workbook->addCellXf($newStyle);
  351. $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
  352. }
  353. }
  354. // Loop through columns, rows, or cells again and update the XF index
  355. switch ($selectionType) {
  356. case 'COLUMN':
  357. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  358. $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
  359. $oldXfIndex = $columnDimension->getXfIndex();
  360. $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
  361. }
  362. break;
  363. case 'ROW':
  364. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  365. $rowDimension = $this->getActiveSheet()->getRowDimension($row);
  366. $oldXfIndex = $rowDimension->getXfIndex() === null ?
  367. 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
  368. $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
  369. }
  370. break;
  371. case 'CELL':
  372. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  373. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  374. $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
  375. $oldXfIndex = $cell->getXfIndex();
  376. $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
  377. }
  378. }
  379. break;
  380. }
  381. } else {
  382. // not a supervisor, just apply the style array directly on style object
  383. if (array_key_exists('fill', $pStyles)) {
  384. $this->getFill()->applyFromArray($pStyles['fill']);
  385. }
  386. if (array_key_exists('font', $pStyles)) {
  387. $this->getFont()->applyFromArray($pStyles['font']);
  388. }
  389. if (array_key_exists('borders', $pStyles)) {
  390. $this->getBorders()->applyFromArray($pStyles['borders']);
  391. }
  392. if (array_key_exists('alignment', $pStyles)) {
  393. $this->getAlignment()->applyFromArray($pStyles['alignment']);
  394. }
  395. if (array_key_exists('numberformat', $pStyles)) {
  396. $this->getNumberFormat()->applyFromArray($pStyles['numberformat']);
  397. }
  398. if (array_key_exists('protection', $pStyles)) {
  399. $this->getProtection()->applyFromArray($pStyles['protection']);
  400. }
  401. if (array_key_exists('quotePrefix', $pStyles)) {
  402. $this->quotePrefix = $pStyles['quotePrefix'];
  403. }
  404. }
  405. } else {
  406. throw new PHPExcel_Exception("Invalid style array passed.");
  407. }
  408. return $this;
  409. }
  410. /**
  411. * Get Fill
  412. *
  413. * @return PHPExcel_Style_Fill
  414. */
  415. public function getFill()
  416. {
  417. return $this->fill;
  418. }
  419. /**
  420. * Get Font
  421. *
  422. * @return PHPExcel_Style_Font
  423. */
  424. public function getFont()
  425. {
  426. return $this->font;
  427. }
  428. /**
  429. * Set font
  430. *
  431. * @param PHPExcel_Style_Font $font
  432. * @return PHPExcel_Style
  433. */
  434. public function setFont(PHPExcel_Style_Font $font)
  435. {
  436. $this->font = $font;
  437. return $this;
  438. }
  439. /**
  440. * Get Borders
  441. *
  442. * @return PHPExcel_Style_Borders
  443. */
  444. public function getBorders()
  445. {
  446. return $this->borders;
  447. }
  448. /**
  449. * Get Alignment
  450. *
  451. * @return PHPExcel_Style_Alignment
  452. */
  453. public function getAlignment()
  454. {
  455. return $this->alignment;
  456. }
  457. /**
  458. * Get Number Format
  459. *
  460. * @return PHPExcel_Style_NumberFormat
  461. */
  462. public function getNumberFormat()
  463. {
  464. return $this->numberFormat;
  465. }
  466. /**
  467. * Get Conditional Styles. Only used on supervisor.
  468. *
  469. * @return PHPExcel_Style_Conditional[]
  470. */
  471. public function getConditionalStyles()
  472. {
  473. return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
  474. }
  475. /**
  476. * Set Conditional Styles. Only used on supervisor.
  477. *
  478. * @param PHPExcel_Style_Conditional[] $pValue Array of condtional styles
  479. * @return PHPExcel_Style
  480. */
  481. public function setConditionalStyles($pValue = null)
  482. {
  483. if (is_array($pValue)) {
  484. $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
  485. }
  486. return $this;
  487. }
  488. /**
  489. * Get Protection
  490. *
  491. * @return PHPExcel_Style_Protection
  492. */
  493. public function getProtection()
  494. {
  495. return $this->protection;
  496. }
  497. /**
  498. * Get quote prefix
  499. *
  500. * @return boolean
  501. */
  502. public function getQuotePrefix()
  503. {
  504. if ($this->isSupervisor) {
  505. return $this->getSharedComponent()->getQuotePrefix();
  506. }
  507. return $this->quotePrefix;
  508. }
  509. /**
  510. * Set quote prefix
  511. *
  512. * @param boolean $pValue
  513. */
  514. public function setQuotePrefix($pValue)
  515. {
  516. if ($pValue == '') {
  517. $pValue = false;
  518. }
  519. if ($this->isSupervisor) {
  520. $styleArray = array('quotePrefix' => $pValue);
  521. $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
  522. } else {
  523. $this->quotePrefix = (boolean) $pValue;
  524. }
  525. return $this;
  526. }
  527. /**
  528. * Get hash code
  529. *
  530. * @return string Hash code
  531. */
  532. public function getHashCode()
  533. {
  534. $hashConditionals = '';
  535. foreach ($this->conditionalStyles as $conditional) {
  536. $hashConditionals .= $conditional->getHashCode();
  537. }
  538. return md5(
  539. $this->fill->getHashCode() .
  540. $this->font->getHashCode() .
  541. $this->borders->getHashCode() .
  542. $this->alignment->getHashCode() .
  543. $this->numberFormat->getHashCode() .
  544. $hashConditionals .
  545. $this->protection->getHashCode() .
  546. ($this->quotePrefix ? 't' : 'f') .
  547. __CLASS__
  548. );
  549. }
  550. /**
  551. * Get own index in style collection
  552. *
  553. * @return int
  554. */
  555. public function getIndex()
  556. {
  557. return $this->index;
  558. }
  559. /**
  560. * Set own index in style collection
  561. *
  562. * @param int $pValue
  563. */
  564. public function setIndex($pValue)
  565. {
  566. $this->index = $pValue;
  567. }
  568. }