Style.php 22 KB

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