For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

644 lines
23 KiB

  1. <?php
  2. /**
  3. * PHPExcel_Style
  4. *
  5. * Copyright (c) 2006 - 2015 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Style
  23. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version ##VERSION##, ##DATE##
  26. */
  27. class PHPExcel_Style extends PHPExcel_Style_Supervisor implements PHPExcel_IComparable
  28. {
  29. /**
  30. * Font
  31. *
  32. * @var PHPExcel_Style_Font
  33. */
  34. protected $font;
  35. /**
  36. * Fill
  37. *
  38. * @var PHPExcel_Style_Fill
  39. */
  40. protected $fill;
  41. /**
  42. * Borders
  43. *
  44. * @var PHPExcel_Style_Borders
  45. */
  46. protected $borders;
  47. /**
  48. * Alignment
  49. *
  50. * @var PHPExcel_Style_Alignment
  51. */
  52. protected $alignment;
  53. /**
  54. * Number Format
  55. *
  56. * @var PHPExcel_Style_NumberFormat
  57. */
  58. protected $numberFormat;
  59. /**
  60. * Conditional styles
  61. *
  62. * @var PHPExcel_Style_Conditional[]
  63. */
  64. protected $conditionalStyles;
  65. /**
  66. * Protection
  67. *
  68. * @var PHPExcel_Style_Protection
  69. */
  70. protected $protection;
  71. /**
  72. * Index of style in collection. Only used for real style.
  73. *
  74. * @var int
  75. */
  76. protected $index;
  77. /**
  78. * Use Quote Prefix when displaying in cell editor. Only used for real style.
  79. *
  80. * @var boolean
  81. */
  82. protected $quotePrefix = false;
  83. /**
  84. * Create a new PHPExcel_Style
  85. *
  86. * @param boolean $isSupervisor Flag indicating if this is a supervisor or not
  87. * Leave this value at default unless you understand exactly what
  88. * its ramifications are
  89. * @param boolean $isConditional Flag indicating if this is a conditional style or not
  90. * Leave this value at default unless you understand exactly what
  91. * its ramifications are
  92. */
  93. public function __construct($isSupervisor = false, $isConditional = false)
  94. {
  95. // Supervisor?
  96. $this->isSupervisor = $isSupervisor;
  97. // Initialise values
  98. $this->conditionalStyles = array();
  99. $this->font = new PHPExcel_Style_Font($isSupervisor, $isConditional);
  100. $this->fill = new PHPExcel_Style_Fill($isSupervisor, $isConditional);
  101. $this->borders = new PHPExcel_Style_Borders($isSupervisor, $isConditional);
  102. $this->alignment = new PHPExcel_Style_Alignment($isSupervisor, $isConditional);
  103. $this->numberFormat = new PHPExcel_Style_NumberFormat($isSupervisor, $isConditional);
  104. $this->protection = new PHPExcel_Style_Protection($isSupervisor, $isConditional);
  105. // bind parent if we are a supervisor
  106. if ($isSupervisor) {
  107. $this->font->bindParent($this);
  108. $this->fill->bindParent($this);
  109. $this->borders->bindParent($this);
  110. $this->alignment->bindParent($this);
  111. $this->numberFormat->bindParent($this);
  112. $this->protection->bindParent($this);
  113. }
  114. }
  115. /**
  116. * Get the shared style component for the currently active cell in currently active sheet.
  117. * Only used for style supervisor
  118. *
  119. * @return PHPExcel_Style
  120. */
  121. public function getSharedComponent()
  122. {
  123. $activeSheet = $this->getActiveSheet();
  124. $selectedCell = $this->getActiveCell(); // e.g. 'A1'
  125. if ($activeSheet->cellExists($selectedCell)) {
  126. $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
  127. } else {
  128. $xfIndex = 0;
  129. }
  130. return $this->parent->getCellXfByIndex($xfIndex);
  131. }
  132. /**
  133. * Get parent. Only used for style supervisor
  134. *
  135. * @return PHPExcel
  136. */
  137. public function getParent()
  138. {
  139. return $this->parent;
  140. }
  141. /**
  142. * Build style array from subcomponents
  143. *
  144. * @param array $array
  145. * @return array
  146. */
  147. public function getStyleArray($array)
  148. {
  149. return array('quotePrefix' => $array);
  150. }
  151. /**
  152. * Apply styles from array
  153. *
  154. * <code>
  155. * $objPHPExcel->getActiveSheet()->getStyle('B2')->applyFromArray(
  156. * array(
  157. * 'font' => array(
  158. * 'name' => 'Arial',
  159. * 'bold' => true,
  160. * 'italic' => false,
  161. * 'underline' => PHPExcel_Style_Font::UNDERLINE_DOUBLE,
  162. * 'strike' => false,
  163. * 'color' => array(
  164. * 'rgb' => '808080'
  165. * )
  166. * ),
  167. * 'borders' => array(
  168. * 'bottom' => array(
  169. * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
  170. * 'color' => array(
  171. * 'rgb' => '808080'
  172. * )
  173. * ),
  174. * 'top' => array(
  175. * 'style' => PHPExcel_Style_Border::BORDER_DASHDOT,
  176. * 'color' => array(
  177. * 'rgb' => '808080'
  178. * )
  179. * )
  180. * ),
  181. * 'quotePrefix' => true
  182. * )
  183. * );
  184. * </code>
  185. *
  186. * @param array $pStyles Array containing style information
  187. * @param boolean $pAdvanced Advanced mode for setting borders.
  188. * @throws PHPExcel_Exception
  189. * @return PHPExcel_Style
  190. */
  191. public function applyFromArray($pStyles = null, $pAdvanced = true)
  192. {
  193. if (is_array($pStyles)) {
  194. if ($this->isSupervisor) {
  195. $pRange = $this->getSelectedCells();
  196. // Uppercase coordinate
  197. $pRange = strtoupper($pRange);
  198. // Is it a cell range or a single cell?
  199. if (strpos($pRange, ':') === false) {
  200. $rangeA = $pRange;
  201. $rangeB = $pRange;
  202. } else {
  203. list($rangeA, $rangeB) = explode(':', $pRange);
  204. }
  205. // Calculate range outer borders
  206. $rangeStart = PHPExcel_Cell::coordinateFromString($rangeA);
  207. $rangeEnd = PHPExcel_Cell::coordinateFromString($rangeB);
  208. // Translate column into index
  209. $rangeStart[0] = PHPExcel_Cell::columnIndexFromString($rangeStart[0]) - 1;
  210. $rangeEnd[0] = PHPExcel_Cell::columnIndexFromString($rangeEnd[0]) - 1;
  211. // Make sure we can loop upwards on rows and columns
  212. if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
  213. $tmp = $rangeStart;
  214. $rangeStart = $rangeEnd;
  215. $rangeEnd = $tmp;
  216. }
  217. // ADVANCED MODE:
  218. if ($pAdvanced && isset($pStyles['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($pStyles['borders']['allborders'])) {
  222. foreach (array('outline', 'inside') as $component) {
  223. if (!isset($pStyles['borders'][$component])) {
  224. $pStyles['borders'][$component] = $pStyles['borders']['allborders'];
  225. }
  226. }
  227. unset($pStyles['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($pStyles['borders']['outline'])) {
  232. foreach (array('top', 'right', 'bottom', 'left') as $component) {
  233. if (!isset($pStyles['borders'][$component])) {
  234. $pStyles['borders'][$component] = $pStyles['borders']['outline'];
  235. }
  236. }
  237. unset($pStyles['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($pStyles['borders']['inside'])) {
  242. foreach (array('vertical', 'horizontal') as $component) {
  243. if (!isset($pStyles['borders'][$component])) {
  244. $pStyles['borders'][$component] = $pStyles['borders']['inside'];
  245. }
  246. }
  247. unset($pStyles['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($rangeEnd[0] - $rangeStart[0] + 1, 3);
  251. $yMax = min($rangeEnd[1] - $rangeStart[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. PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0])
  257. : PHPExcel_Cell::stringFromColumnIndex($rangeStart[0] + $x - 1);
  258. // end column index for region
  259. $colEnd = ($x == 1) ?
  260. PHPExcel_Cell::stringFromColumnIndex($rangeStart[0])
  261. : PHPExcel_Cell::stringFromColumnIndex($rangeEnd[0] - $xMax + $x);
  262. for ($y = 1; $y <= $yMax; ++$y) {
  263. // which edges are touching the region
  264. $edges = array();
  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. $rangeEnd[1] : $rangeStart[1] + $y - 1;
  284. // end row index for region
  285. $rowEnd = ($y == 1) ?
  286. $rangeStart[1] : $rangeEnd[1] - $yMax + $y;
  287. // build range for region
  288. $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
  289. // retrieve relevant style array for region
  290. $regionStyles = $pStyles;
  291. unset($regionStyles['borders']['inside']);
  292. // what are the inner edges of the region when looking at the selection
  293. $innerEdges = array_diff(array('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($pStyles['borders']['horizontal'])) {
  301. $regionStyles['borders'][$innerEdge] = $pStyles['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($pStyles['borders']['vertical'])) {
  310. $regionStyles['borders'][$innerEdge] = $pStyles['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. return $this;
  322. }
  323. // SIMPLE MODE:
  324. // Selection type, inspect
  325. if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
  326. $selectionType = 'COLUMN';
  327. } elseif (preg_match('/^A[0-9]+:XFD[0-9]+$/', $pRange)) {
  328. $selectionType = 'ROW';
  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. switch ($selectionType) {
  334. case 'COLUMN':
  335. $oldXfIndexes = array();
  336. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  337. $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
  338. }
  339. break;
  340. case 'ROW':
  341. $oldXfIndexes = array();
  342. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  343. if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() == null) {
  344. $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
  345. } else {
  346. $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
  347. }
  348. }
  349. break;
  350. case 'CELL':
  351. $oldXfIndexes = array();
  352. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  353. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  354. $oldXfIndexes[$this->getActiveSheet()->getCellByColumnAndRow($col, $row)->getXfIndex()] = true;
  355. }
  356. }
  357. break;
  358. }
  359. // clone each of the affected styles, apply the style array, and add the new styles to the workbook
  360. $workbook = $this->getActiveSheet()->getParent();
  361. foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
  362. $style = $workbook->getCellXfByIndex($oldXfIndex);
  363. $newStyle = clone $style;
  364. $newStyle->applyFromArray($pStyles);
  365. if ($existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode())) {
  366. // there is already such cell Xf in our collection
  367. $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
  368. } else {
  369. // we don't have such a cell Xf, need to add
  370. $workbook->addCellXf($newStyle);
  371. $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
  372. }
  373. }
  374. // Loop through columns, rows, or cells again and update the XF index
  375. switch ($selectionType) {
  376. case 'COLUMN':
  377. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  378. $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
  379. $oldXfIndex = $columnDimension->getXfIndex();
  380. $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
  381. }
  382. break;
  383. case 'ROW':
  384. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  385. $rowDimension = $this->getActiveSheet()->getRowDimension($row);
  386. $oldXfIndex = $rowDimension->getXfIndex() === null ?
  387. 0 : $rowDimension->getXfIndex(); // row without explicit style should be formatted based on default style
  388. $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
  389. }
  390. break;
  391. case 'CELL':
  392. for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
  393. for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
  394. $cell = $this->getActiveSheet()->getCellByColumnAndRow($col, $row);
  395. $oldXfIndex = $cell->getXfIndex();
  396. $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
  397. }
  398. }
  399. break;
  400. }
  401. } else {
  402. // not a supervisor, just apply the style array directly on style object
  403. if (array_key_exists('fill', $pStyles)) {
  404. $this->getFill()->applyFromArray($pStyles['fill']);
  405. }
  406. if (array_key_exists('font', $pStyles)) {
  407. $this->getFont()->applyFromArray($pStyles['font']);
  408. }
  409. if (array_key_exists('borders', $pStyles)) {
  410. $this->getBorders()->applyFromArray($pStyles['borders']);
  411. }
  412. if (array_key_exists('alignment', $pStyles)) {
  413. $this->getAlignment()->applyFromArray($pStyles['alignment']);
  414. }
  415. if (array_key_exists('numberformat', $pStyles)) {
  416. $this->getNumberFormat()->applyFromArray($pStyles['numberformat']);
  417. }
  418. if (array_key_exists('protection', $pStyles)) {
  419. $this->getProtection()->applyFromArray($pStyles['protection']);
  420. }
  421. if (array_key_exists('quotePrefix', $pStyles)) {
  422. $this->quotePrefix = $pStyles['quotePrefix'];
  423. }
  424. }
  425. } else {
  426. throw new PHPExcel_Exception("Invalid style array passed.");
  427. }
  428. return $this;
  429. }
  430. /**
  431. * Get Fill
  432. *
  433. * @return PHPExcel_Style_Fill
  434. */
  435. public function getFill()
  436. {
  437. return $this->fill;
  438. }
  439. /**
  440. * Get Font
  441. *
  442. * @return PHPExcel_Style_Font
  443. */
  444. public function getFont()
  445. {
  446. return $this->font;
  447. }
  448. /**
  449. * Set font
  450. *
  451. * @param PHPExcel_Style_Font $font
  452. * @return PHPExcel_Style
  453. */
  454. public function setFont(PHPExcel_Style_Font $font)
  455. {
  456. $this->font = $font;
  457. return $this;
  458. }
  459. /**
  460. * Get Borders
  461. *
  462. * @return PHPExcel_Style_Borders
  463. */
  464. public function getBorders()
  465. {
  466. return $this->borders;
  467. }
  468. /**
  469. * Get Alignment
  470. *
  471. * @return PHPExcel_Style_Alignment
  472. */
  473. public function getAlignment()
  474. {
  475. return $this->alignment;
  476. }
  477. /**
  478. * Get Number Format
  479. *
  480. * @return PHPExcel_Style_NumberFormat
  481. */
  482. public function getNumberFormat()
  483. {
  484. return $this->numberFormat;
  485. }
  486. /**
  487. * Get Conditional Styles. Only used on supervisor.
  488. *
  489. * @return PHPExcel_Style_Conditional[]
  490. */
  491. public function getConditionalStyles()
  492. {
  493. return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
  494. }
  495. /**
  496. * Set Conditional Styles. Only used on supervisor.
  497. *
  498. * @param PHPExcel_Style_Conditional[] $pValue Array of condtional styles
  499. * @return PHPExcel_Style
  500. */
  501. public function setConditionalStyles($pValue = null)
  502. {
  503. if (is_array($pValue)) {
  504. $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $pValue);
  505. }
  506. return $this;
  507. }
  508. /**
  509. * Get Protection
  510. *
  511. * @return PHPExcel_Style_Protection
  512. */
  513. public function getProtection()
  514. {
  515. return $this->protection;
  516. }
  517. /**
  518. * Get quote prefix
  519. *
  520. * @return boolean
  521. */
  522. public function getQuotePrefix()
  523. {
  524. if ($this->isSupervisor) {
  525. return $this->getSharedComponent()->getQuotePrefix();
  526. }
  527. return $this->quotePrefix;
  528. }
  529. /**
  530. * Set quote prefix
  531. *
  532. * @param boolean $pValue
  533. */
  534. public function setQuotePrefix($pValue)
  535. {
  536. if ($pValue == '') {
  537. $pValue = false;
  538. }
  539. if ($this->isSupervisor) {
  540. $styleArray = array('quotePrefix' => $pValue);
  541. $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
  542. } else {
  543. $this->quotePrefix = (boolean) $pValue;
  544. }
  545. return $this;
  546. }
  547. /**
  548. * Get hash code
  549. *
  550. * @return string Hash code
  551. */
  552. public function getHashCode()
  553. {
  554. $hashConditionals = '';
  555. foreach ($this->conditionalStyles as $conditional) {
  556. $hashConditionals .= $conditional->getHashCode();
  557. }
  558. return md5(
  559. $this->fill->getHashCode() .
  560. $this->font->getHashCode() .
  561. $this->borders->getHashCode() .
  562. $this->alignment->getHashCode() .
  563. $this->numberFormat->getHashCode() .
  564. $hashConditionals .
  565. $this->protection->getHashCode() .
  566. ($this->quotePrefix ? 't' : 'f') .
  567. __CLASS__
  568. );
  569. }
  570. /**
  571. * Get own index in style collection
  572. *
  573. * @return int
  574. */
  575. public function getIndex()
  576. {
  577. return $this->index;
  578. }
  579. /**
  580. * Set own index in style collection
  581. *
  582. * @param int $pValue
  583. */
  584. public function setIndex($pValue)
  585. {
  586. $this->index = $pValue;
  587. }
  588. }