Xf.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. <?php
  2. namespace PhpOffice\PhpSpreadsheet\Writer\Xls;
  3. use PhpOffice\PhpSpreadsheet\Style\Alignment;
  4. use PhpOffice\PhpSpreadsheet\Style\Border;
  5. use PhpOffice\PhpSpreadsheet\Style\Borders;
  6. use PhpOffice\PhpSpreadsheet\Style\Fill;
  7. use PhpOffice\PhpSpreadsheet\Style\Protection;
  8. use PhpOffice\PhpSpreadsheet\Style\Style;
  9. // Original file header of PEAR::Spreadsheet_Excel_Writer_Format (used as the base for this class):
  10. // -----------------------------------------------------------------------------------------
  11. // /*
  12. // * Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
  13. // *
  14. // * The majority of this is _NOT_ my code. I simply ported it from the
  15. // * PERL Spreadsheet::WriteExcel module.
  16. // *
  17. // * The author of the Spreadsheet::WriteExcel module is John McNamara
  18. // * <jmcnamara@cpan.org>
  19. // *
  20. // * I _DO_ maintain this code, and John McNamara has nothing to do with the
  21. // * porting of this code to PHP. Any questions directly related to this
  22. // * class library should be directed to me.
  23. // *
  24. // * License Information:
  25. // *
  26. // * Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
  27. // * Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
  28. // *
  29. // * This library is free software; you can redistribute it and/or
  30. // * modify it under the terms of the GNU Lesser General Public
  31. // * License as published by the Free Software Foundation; either
  32. // * version 2.1 of the License, or (at your option) any later version.
  33. // *
  34. // * This library is distributed in the hope that it will be useful,
  35. // * but WITHOUT ANY WARRANTY; without even the implied warranty of
  36. // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  37. // * Lesser General Public License for more details.
  38. // *
  39. // * You should have received a copy of the GNU Lesser General Public
  40. // * License along with this library; if not, write to the Free Software
  41. // * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  42. // */
  43. class Xf
  44. {
  45. /**
  46. * Style XF or a cell XF ?
  47. *
  48. * @var bool
  49. */
  50. private $isStyleXf;
  51. /**
  52. * Index to the FONT record. Index 4 does not exist.
  53. *
  54. * @var int
  55. */
  56. private $fontIndex;
  57. /**
  58. * An index (2 bytes) to a FORMAT record (number format).
  59. *
  60. * @var int
  61. */
  62. private $numberFormatIndex;
  63. /**
  64. * 1 bit, apparently not used.
  65. *
  66. * @var int
  67. */
  68. private $textJustLast;
  69. /**
  70. * The cell's foreground color.
  71. *
  72. * @var int
  73. */
  74. private $foregroundColor;
  75. /**
  76. * The cell's background color.
  77. *
  78. * @var int
  79. */
  80. private $backgroundColor;
  81. /**
  82. * Color of the bottom border of the cell.
  83. *
  84. * @var int
  85. */
  86. private $bottomBorderColor;
  87. /**
  88. * Color of the top border of the cell.
  89. *
  90. * @var int
  91. */
  92. private $topBorderColor;
  93. /**
  94. * Color of the left border of the cell.
  95. *
  96. * @var int
  97. */
  98. private $leftBorderColor;
  99. /**
  100. * Color of the right border of the cell.
  101. *
  102. * @var int
  103. */
  104. private $rightBorderColor;
  105. /**
  106. * Constructor.
  107. *
  108. * @param Style $style The XF format
  109. */
  110. public function __construct(Style $style)
  111. {
  112. $this->isStyleXf = false;
  113. $this->fontIndex = 0;
  114. $this->numberFormatIndex = 0;
  115. $this->textJustLast = 0;
  116. $this->foregroundColor = 0x40;
  117. $this->backgroundColor = 0x41;
  118. $this->_diag = 0;
  119. $this->bottomBorderColor = 0x40;
  120. $this->topBorderColor = 0x40;
  121. $this->leftBorderColor = 0x40;
  122. $this->rightBorderColor = 0x40;
  123. $this->_diag_color = 0x40;
  124. $this->_style = $style;
  125. }
  126. /**
  127. * Generate an Excel BIFF XF record (style or cell).
  128. *
  129. * @return string The XF record
  130. */
  131. public function writeXf()
  132. {
  133. // Set the type of the XF record and some of the attributes.
  134. if ($this->isStyleXf) {
  135. $style = 0xFFF5;
  136. } else {
  137. $style = self::mapLocked($this->_style->getProtection()->getLocked());
  138. $style |= self::mapHidden($this->_style->getProtection()->getHidden()) << 1;
  139. }
  140. // Flags to indicate if attributes have been set.
  141. $atr_num = ($this->numberFormatIndex != 0) ? 1 : 0;
  142. $atr_fnt = ($this->fontIndex != 0) ? 1 : 0;
  143. $atr_alc = ((int) $this->_style->getAlignment()->getWrapText()) ? 1 : 0;
  144. $atr_bdr = (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) ||
  145. self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) ||
  146. self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) ||
  147. self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle())) ? 1 : 0;
  148. $atr_pat = (($this->foregroundColor != 0x40) ||
  149. ($this->backgroundColor != 0x41) ||
  150. self::mapFillType($this->_style->getFill()->getFillType())) ? 1 : 0;
  151. $atr_prot = self::mapLocked($this->_style->getProtection()->getLocked())
  152. | self::mapHidden($this->_style->getProtection()->getHidden());
  153. // Zero the default border colour if the border has not been set.
  154. if (self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) == 0) {
  155. $this->bottomBorderColor = 0;
  156. }
  157. if (self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) == 0) {
  158. $this->topBorderColor = 0;
  159. }
  160. if (self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) == 0) {
  161. $this->rightBorderColor = 0;
  162. }
  163. if (self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()) == 0) {
  164. $this->leftBorderColor = 0;
  165. }
  166. if (self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) == 0) {
  167. $this->_diag_color = 0;
  168. }
  169. $record = 0x00E0; // Record identifier
  170. $length = 0x0014; // Number of bytes to follow
  171. $ifnt = $this->fontIndex; // Index to FONT record
  172. $ifmt = $this->numberFormatIndex; // Index to FORMAT record
  173. $align = $this->mapHAlign($this->_style->getAlignment()->getHorizontal()); // Alignment
  174. $align |= (int) $this->_style->getAlignment()->getWrapText() << 3;
  175. $align |= self::mapVAlign($this->_style->getAlignment()->getVertical()) << 4;
  176. $align |= $this->textJustLast << 7;
  177. $used_attrib = $atr_num << 2;
  178. $used_attrib |= $atr_fnt << 3;
  179. $used_attrib |= $atr_alc << 4;
  180. $used_attrib |= $atr_bdr << 5;
  181. $used_attrib |= $atr_pat << 6;
  182. $used_attrib |= $atr_prot << 7;
  183. $icv = $this->foregroundColor; // fg and bg pattern colors
  184. $icv |= $this->backgroundColor << 7;
  185. $border1 = self::mapBorderStyle($this->_style->getBorders()->getLeft()->getBorderStyle()); // Border line style and color
  186. $border1 |= self::mapBorderStyle($this->_style->getBorders()->getRight()->getBorderStyle()) << 4;
  187. $border1 |= self::mapBorderStyle($this->_style->getBorders()->getTop()->getBorderStyle()) << 8;
  188. $border1 |= self::mapBorderStyle($this->_style->getBorders()->getBottom()->getBorderStyle()) << 12;
  189. $border1 |= $this->leftBorderColor << 16;
  190. $border1 |= $this->rightBorderColor << 23;
  191. $diagonalDirection = $this->_style->getBorders()->getDiagonalDirection();
  192. $diag_tl_to_rb = $diagonalDirection == Borders::DIAGONAL_BOTH
  193. || $diagonalDirection == Borders::DIAGONAL_DOWN;
  194. $diag_tr_to_lb = $diagonalDirection == Borders::DIAGONAL_BOTH
  195. || $diagonalDirection == Borders::DIAGONAL_UP;
  196. $border1 |= $diag_tl_to_rb << 30;
  197. $border1 |= $diag_tr_to_lb << 31;
  198. $border2 = $this->topBorderColor; // Border color
  199. $border2 |= $this->bottomBorderColor << 7;
  200. $border2 |= $this->_diag_color << 14;
  201. $border2 |= self::mapBorderStyle($this->_style->getBorders()->getDiagonal()->getBorderStyle()) << 21;
  202. $border2 |= self::mapFillType($this->_style->getFill()->getFillType()) << 26;
  203. $header = pack('vv', $record, $length);
  204. //BIFF8 options: identation, shrinkToFit and text direction
  205. $biff8_options = $this->_style->getAlignment()->getIndent();
  206. $biff8_options |= (int) $this->_style->getAlignment()->getShrinkToFit() << 4;
  207. $data = pack('vvvC', $ifnt, $ifmt, $style, $align);
  208. $data .= pack('CCC', self::mapTextRotation($this->_style->getAlignment()->getTextRotation()), $biff8_options, $used_attrib);
  209. $data .= pack('VVv', $border1, $border2, $icv);
  210. return $header . $data;
  211. }
  212. /**
  213. * Is this a style XF ?
  214. *
  215. * @param bool $value
  216. */
  217. public function setIsStyleXf($value)
  218. {
  219. $this->isStyleXf = $value;
  220. }
  221. /**
  222. * Sets the cell's bottom border color.
  223. *
  224. * @param int $colorIndex Color index
  225. */
  226. public function setBottomColor($colorIndex)
  227. {
  228. $this->bottomBorderColor = $colorIndex;
  229. }
  230. /**
  231. * Sets the cell's top border color.
  232. *
  233. * @param int $colorIndex Color index
  234. */
  235. public function setTopColor($colorIndex)
  236. {
  237. $this->topBorderColor = $colorIndex;
  238. }
  239. /**
  240. * Sets the cell's left border color.
  241. *
  242. * @param int $colorIndex Color index
  243. */
  244. public function setLeftColor($colorIndex)
  245. {
  246. $this->leftBorderColor = $colorIndex;
  247. }
  248. /**
  249. * Sets the cell's right border color.
  250. *
  251. * @param int $colorIndex Color index
  252. */
  253. public function setRightColor($colorIndex)
  254. {
  255. $this->rightBorderColor = $colorIndex;
  256. }
  257. /**
  258. * Sets the cell's diagonal border color.
  259. *
  260. * @param int $colorIndex Color index
  261. */
  262. public function setDiagColor($colorIndex)
  263. {
  264. $this->_diag_color = $colorIndex;
  265. }
  266. /**
  267. * Sets the cell's foreground color.
  268. *
  269. * @param int $colorIndex Color index
  270. */
  271. public function setFgColor($colorIndex)
  272. {
  273. $this->foregroundColor = $colorIndex;
  274. }
  275. /**
  276. * Sets the cell's background color.
  277. *
  278. * @param int $colorIndex Color index
  279. */
  280. public function setBgColor($colorIndex)
  281. {
  282. $this->backgroundColor = $colorIndex;
  283. }
  284. /**
  285. * Sets the index to the number format record
  286. * It can be date, time, currency, etc...
  287. *
  288. * @param int $numberFormatIndex Index to format record
  289. */
  290. public function setNumberFormatIndex($numberFormatIndex)
  291. {
  292. $this->numberFormatIndex = $numberFormatIndex;
  293. }
  294. /**
  295. * Set the font index.
  296. *
  297. * @param int $value Font index, note that value 4 does not exist
  298. */
  299. public function setFontIndex($value)
  300. {
  301. $this->fontIndex = $value;
  302. }
  303. /**
  304. * Map of BIFF2-BIFF8 codes for border styles.
  305. *
  306. * @var array of int
  307. */
  308. private static $mapBorderStyles = [
  309. Border::BORDER_NONE => 0x00,
  310. Border::BORDER_THIN => 0x01,
  311. Border::BORDER_MEDIUM => 0x02,
  312. Border::BORDER_DASHED => 0x03,
  313. Border::BORDER_DOTTED => 0x04,
  314. Border::BORDER_THICK => 0x05,
  315. Border::BORDER_DOUBLE => 0x06,
  316. Border::BORDER_HAIR => 0x07,
  317. Border::BORDER_MEDIUMDASHED => 0x08,
  318. Border::BORDER_DASHDOT => 0x09,
  319. Border::BORDER_MEDIUMDASHDOT => 0x0A,
  320. Border::BORDER_DASHDOTDOT => 0x0B,
  321. Border::BORDER_MEDIUMDASHDOTDOT => 0x0C,
  322. Border::BORDER_SLANTDASHDOT => 0x0D,
  323. ];
  324. /**
  325. * Map border style.
  326. *
  327. * @param string $borderStyle
  328. *
  329. * @return int
  330. */
  331. private static function mapBorderStyle($borderStyle)
  332. {
  333. if (isset(self::$mapBorderStyles[$borderStyle])) {
  334. return self::$mapBorderStyles[$borderStyle];
  335. }
  336. return 0x00;
  337. }
  338. /**
  339. * Map of BIFF2-BIFF8 codes for fill types.
  340. *
  341. * @var array of int
  342. */
  343. private static $mapFillTypes = [
  344. Fill::FILL_NONE => 0x00,
  345. Fill::FILL_SOLID => 0x01,
  346. Fill::FILL_PATTERN_MEDIUMGRAY => 0x02,
  347. Fill::FILL_PATTERN_DARKGRAY => 0x03,
  348. Fill::FILL_PATTERN_LIGHTGRAY => 0x04,
  349. Fill::FILL_PATTERN_DARKHORIZONTAL => 0x05,
  350. Fill::FILL_PATTERN_DARKVERTICAL => 0x06,
  351. Fill::FILL_PATTERN_DARKDOWN => 0x07,
  352. Fill::FILL_PATTERN_DARKUP => 0x08,
  353. Fill::FILL_PATTERN_DARKGRID => 0x09,
  354. Fill::FILL_PATTERN_DARKTRELLIS => 0x0A,
  355. Fill::FILL_PATTERN_LIGHTHORIZONTAL => 0x0B,
  356. Fill::FILL_PATTERN_LIGHTVERTICAL => 0x0C,
  357. Fill::FILL_PATTERN_LIGHTDOWN => 0x0D,
  358. Fill::FILL_PATTERN_LIGHTUP => 0x0E,
  359. Fill::FILL_PATTERN_LIGHTGRID => 0x0F,
  360. Fill::FILL_PATTERN_LIGHTTRELLIS => 0x10,
  361. Fill::FILL_PATTERN_GRAY125 => 0x11,
  362. Fill::FILL_PATTERN_GRAY0625 => 0x12,
  363. Fill::FILL_GRADIENT_LINEAR => 0x00, // does not exist in BIFF8
  364. Fill::FILL_GRADIENT_PATH => 0x00, // does not exist in BIFF8
  365. ];
  366. /**
  367. * Map fill type.
  368. *
  369. * @param string $fillType
  370. *
  371. * @return int
  372. */
  373. private static function mapFillType($fillType)
  374. {
  375. if (isset(self::$mapFillTypes[$fillType])) {
  376. return self::$mapFillTypes[$fillType];
  377. }
  378. return 0x00;
  379. }
  380. /**
  381. * Map of BIFF2-BIFF8 codes for horizontal alignment.
  382. *
  383. * @var array of int
  384. */
  385. private static $mapHAlignments = [
  386. Alignment::HORIZONTAL_GENERAL => 0,
  387. Alignment::HORIZONTAL_LEFT => 1,
  388. Alignment::HORIZONTAL_CENTER => 2,
  389. Alignment::HORIZONTAL_RIGHT => 3,
  390. Alignment::HORIZONTAL_FILL => 4,
  391. Alignment::HORIZONTAL_JUSTIFY => 5,
  392. Alignment::HORIZONTAL_CENTER_CONTINUOUS => 6,
  393. ];
  394. /**
  395. * Map to BIFF2-BIFF8 codes for horizontal alignment.
  396. *
  397. * @param string $hAlign
  398. *
  399. * @return int
  400. */
  401. private function mapHAlign($hAlign)
  402. {
  403. if (isset(self::$mapHAlignments[$hAlign])) {
  404. return self::$mapHAlignments[$hAlign];
  405. }
  406. return 0;
  407. }
  408. /**
  409. * Map of BIFF2-BIFF8 codes for vertical alignment.
  410. *
  411. * @var array of int
  412. */
  413. private static $mapVAlignments = [
  414. Alignment::VERTICAL_TOP => 0,
  415. Alignment::VERTICAL_CENTER => 1,
  416. Alignment::VERTICAL_BOTTOM => 2,
  417. Alignment::VERTICAL_JUSTIFY => 3,
  418. ];
  419. /**
  420. * Map to BIFF2-BIFF8 codes for vertical alignment.
  421. *
  422. * @param string $vAlign
  423. *
  424. * @return int
  425. */
  426. private static function mapVAlign($vAlign)
  427. {
  428. if (isset(self::$mapVAlignments[$vAlign])) {
  429. return self::$mapVAlignments[$vAlign];
  430. }
  431. return 2;
  432. }
  433. /**
  434. * Map to BIFF8 codes for text rotation angle.
  435. *
  436. * @param int $textRotation
  437. *
  438. * @return int
  439. */
  440. private static function mapTextRotation($textRotation)
  441. {
  442. if ($textRotation >= 0) {
  443. return $textRotation;
  444. } elseif ($textRotation == -165) {
  445. return 255;
  446. } elseif ($textRotation < 0) {
  447. return 90 - $textRotation;
  448. }
  449. }
  450. /**
  451. * Map locked.
  452. *
  453. * @param string $locked
  454. *
  455. * @return int
  456. */
  457. private static function mapLocked($locked)
  458. {
  459. switch ($locked) {
  460. case Protection::PROTECTION_INHERIT:
  461. return 1;
  462. case Protection::PROTECTION_PROTECTED:
  463. return 1;
  464. case Protection::PROTECTION_UNPROTECTED:
  465. return 0;
  466. default:
  467. return 1;
  468. }
  469. }
  470. /**
  471. * Map hidden.
  472. *
  473. * @param string $hidden
  474. *
  475. * @return int
  476. */
  477. private static function mapHidden($hidden)
  478. {
  479. switch ($hidden) {
  480. case Protection::PROTECTION_INHERIT:
  481. return 0;
  482. case Protection::PROTECTION_PROTECTED:
  483. return 1;
  484. case Protection::PROTECTION_UNPROTECTED:
  485. return 0;
  486. default:
  487. return 0;
  488. }
  489. }
  490. }