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.

751 lines
28 KiB

  1. <?php
  2. /**
  3. * PHPExcel_Style_NumberFormat
  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_NumberFormat extends PHPExcel_Style_Supervisor implements PHPExcel_IComparable
  28. {
  29. /* Pre-defined formats */
  30. const FORMAT_GENERAL = 'General';
  31. const FORMAT_TEXT = '@';
  32. const FORMAT_NUMBER = '0';
  33. const FORMAT_NUMBER_00 = '0.00';
  34. const FORMAT_NUMBER_COMMA_SEPARATED1 = '#,##0.00';
  35. const FORMAT_NUMBER_COMMA_SEPARATED2 = '#,##0.00_-';
  36. const FORMAT_PERCENTAGE = '0%';
  37. const FORMAT_PERCENTAGE_00 = '0.00%';
  38. const FORMAT_DATE_YYYYMMDD2 = 'yyyy-mm-dd';
  39. const FORMAT_DATE_YYYYMMDD = 'yy-mm-dd';
  40. const FORMAT_DATE_DDMMYYYY = 'dd/mm/yy';
  41. const FORMAT_DATE_DMYSLASH = 'd/m/y';
  42. const FORMAT_DATE_DMYMINUS = 'd-m-y';
  43. const FORMAT_DATE_DMMINUS = 'd-m';
  44. const FORMAT_DATE_MYMINUS = 'm-y';
  45. const FORMAT_DATE_XLSX14 = 'mm-dd-yy';
  46. const FORMAT_DATE_XLSX15 = 'd-mmm-yy';
  47. const FORMAT_DATE_XLSX16 = 'd-mmm';
  48. const FORMAT_DATE_XLSX17 = 'mmm-yy';
  49. const FORMAT_DATE_XLSX22 = 'm/d/yy h:mm';
  50. const FORMAT_DATE_DATETIME = 'd/m/y h:mm';
  51. const FORMAT_DATE_TIME1 = 'h:mm AM/PM';
  52. const FORMAT_DATE_TIME2 = 'h:mm:ss AM/PM';
  53. const FORMAT_DATE_TIME3 = 'h:mm';
  54. const FORMAT_DATE_TIME4 = 'h:mm:ss';
  55. const FORMAT_DATE_TIME5 = 'mm:ss';
  56. const FORMAT_DATE_TIME6 = 'h:mm:ss';
  57. const FORMAT_DATE_TIME7 = 'i:s.S';
  58. const FORMAT_DATE_TIME8 = 'h:mm:ss;@';
  59. const FORMAT_DATE_YYYYMMDDSLASH = 'yy/mm/dd;@';
  60. const FORMAT_CURRENCY_USD_SIMPLE = '"$"#,##0.00_-';
  61. const FORMAT_CURRENCY_USD = '$#,##0_-';
  62. const FORMAT_CURRENCY_EUR_SIMPLE = '[$EUR ]#,##0.00_-';
  63. /**
  64. * Excel built-in number formats
  65. *
  66. * @var array
  67. */
  68. protected static $builtInFormats;
  69. /**
  70. * Excel built-in number formats (flipped, for faster lookups)
  71. *
  72. * @var array
  73. */
  74. protected static $flippedBuiltInFormats;
  75. /**
  76. * Format Code
  77. *
  78. * @var string
  79. */
  80. protected $formatCode = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
  81. /**
  82. * Built-in format Code
  83. *
  84. * @var string
  85. */
  86. protected $builtInFormatCode = 0;
  87. /**
  88. * Create a new PHPExcel_Style_NumberFormat
  89. *
  90. * @param boolean $isSupervisor Flag indicating if this is a supervisor or not
  91. * Leave this value at default unless you understand exactly what
  92. * its ramifications are
  93. * @param boolean $isConditional Flag indicating if this is a conditional style or not
  94. * Leave this value at default unless you understand exactly what
  95. * its ramifications are
  96. */
  97. public function __construct($isSupervisor = false, $isConditional = false)
  98. {
  99. // Supervisor?
  100. parent::__construct($isSupervisor);
  101. if ($isConditional) {
  102. $this->formatCode = null;
  103. $this->builtInFormatCode = false;
  104. }
  105. }
  106. /**
  107. * Get the shared style component for the currently active cell in currently active sheet.
  108. * Only used for style supervisor
  109. *
  110. * @return PHPExcel_Style_NumberFormat
  111. */
  112. public function getSharedComponent()
  113. {
  114. return $this->parent->getSharedComponent()->getNumberFormat();
  115. }
  116. /**
  117. * Build style array from subcomponents
  118. *
  119. * @param array $array
  120. * @return array
  121. */
  122. public function getStyleArray($array)
  123. {
  124. return array('numberformat' => $array);
  125. }
  126. /**
  127. * Apply styles from array
  128. *
  129. * <code>
  130. * $objPHPExcel->getActiveSheet()->getStyle('B2')->getNumberFormat()->applyFromArray(
  131. * array(
  132. * 'code' => PHPExcel_Style_NumberFormat::FORMAT_CURRENCY_EUR_SIMPLE
  133. * )
  134. * );
  135. * </code>
  136. *
  137. * @param array $pStyles Array containing style information
  138. * @throws PHPExcel_Exception
  139. * @return PHPExcel_Style_NumberFormat
  140. */
  141. public function applyFromArray($pStyles = null)
  142. {
  143. if (is_array($pStyles)) {
  144. if ($this->isSupervisor) {
  145. $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($this->getStyleArray($pStyles));
  146. } else {
  147. if (array_key_exists('code', $pStyles)) {
  148. $this->setFormatCode($pStyles['code']);
  149. }
  150. }
  151. } else {
  152. throw new PHPExcel_Exception("Invalid style array passed.");
  153. }
  154. return $this;
  155. }
  156. /**
  157. * Get Format Code
  158. *
  159. * @return string
  160. */
  161. public function getFormatCode()
  162. {
  163. if ($this->isSupervisor) {
  164. return $this->getSharedComponent()->getFormatCode();
  165. }
  166. if ($this->builtInFormatCode !== false) {
  167. return self::builtInFormatCode($this->builtInFormatCode);
  168. }
  169. return $this->formatCode;
  170. }
  171. /**
  172. * Set Format Code
  173. *
  174. * @param string $pValue
  175. * @return PHPExcel_Style_NumberFormat
  176. */
  177. public function setFormatCode($pValue = PHPExcel_Style_NumberFormat::FORMAT_GENERAL)
  178. {
  179. if ($pValue == '') {
  180. $pValue = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
  181. }
  182. if ($this->isSupervisor) {
  183. $styleArray = $this->getStyleArray(array('code' => $pValue));
  184. $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
  185. } else {
  186. $this->formatCode = $pValue;
  187. $this->builtInFormatCode = self::builtInFormatCodeIndex($pValue);
  188. }
  189. return $this;
  190. }
  191. /**
  192. * Get Built-In Format Code
  193. *
  194. * @return int
  195. */
  196. public function getBuiltInFormatCode()
  197. {
  198. if ($this->isSupervisor) {
  199. return $this->getSharedComponent()->getBuiltInFormatCode();
  200. }
  201. return $this->builtInFormatCode;
  202. }
  203. /**
  204. * Set Built-In Format Code
  205. *
  206. * @param int $pValue
  207. * @return PHPExcel_Style_NumberFormat
  208. */
  209. public function setBuiltInFormatCode($pValue = 0)
  210. {
  211. if ($this->isSupervisor) {
  212. $styleArray = $this->getStyleArray(array('code' => self::builtInFormatCode($pValue)));
  213. $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
  214. } else {
  215. $this->builtInFormatCode = $pValue;
  216. $this->formatCode = self::builtInFormatCode($pValue);
  217. }
  218. return $this;
  219. }
  220. /**
  221. * Fill built-in format codes
  222. */
  223. private static function fillBuiltInFormatCodes()
  224. {
  225. // [MS-OI29500: Microsoft Office Implementation Information for ISO/IEC-29500 Standard Compliance]
  226. // 18.8.30. numFmt (Number Format)
  227. //
  228. // The ECMA standard defines built-in format IDs
  229. // 14: "mm-dd-yy"
  230. // 22: "m/d/yy h:mm"
  231. // 37: "#,##0 ;(#,##0)"
  232. // 38: "#,##0 ;[Red](#,##0)"
  233. // 39: "#,##0.00;(#,##0.00)"
  234. // 40: "#,##0.00;[Red](#,##0.00)"
  235. // 47: "mmss.0"
  236. // KOR fmt 55: "yyyy-mm-dd"
  237. // Excel defines built-in format IDs
  238. // 14: "m/d/yyyy"
  239. // 22: "m/d/yyyy h:mm"
  240. // 37: "#,##0_);(#,##0)"
  241. // 38: "#,##0_);[Red](#,##0)"
  242. // 39: "#,##0.00_);(#,##0.00)"
  243. // 40: "#,##0.00_);[Red](#,##0.00)"
  244. // 47: "mm:ss.0"
  245. // KOR fmt 55: "yyyy/mm/dd"
  246. // Built-in format codes
  247. if (is_null(self::$builtInFormats)) {
  248. self::$builtInFormats = array();
  249. // General
  250. self::$builtInFormats[0] = PHPExcel_Style_NumberFormat::FORMAT_GENERAL;
  251. self::$builtInFormats[1] = '0';
  252. self::$builtInFormats[2] = '0.00';
  253. self::$builtInFormats[3] = '#,##0';
  254. self::$builtInFormats[4] = '#,##0.00';
  255. self::$builtInFormats[9] = '0%';
  256. self::$builtInFormats[10] = '0.00%';
  257. self::$builtInFormats[11] = '0.00E+00';
  258. self::$builtInFormats[12] = '# ?/?';
  259. self::$builtInFormats[13] = '# ??/??';
  260. self::$builtInFormats[14] = 'm/d/yyyy'; // Despite ECMA 'mm-dd-yy';
  261. self::$builtInFormats[15] = 'd-mmm-yy';
  262. self::$builtInFormats[16] = 'd-mmm';
  263. self::$builtInFormats[17] = 'mmm-yy';
  264. self::$builtInFormats[18] = 'h:mm AM/PM';
  265. self::$builtInFormats[19] = 'h:mm:ss AM/PM';
  266. self::$builtInFormats[20] = 'h:mm';
  267. self::$builtInFormats[21] = 'h:mm:ss';
  268. self::$builtInFormats[22] = 'm/d/yyyy h:mm'; // Despite ECMA 'm/d/yy h:mm';
  269. self::$builtInFormats[37] = '#,##0_);(#,##0)'; // Despite ECMA '#,##0 ;(#,##0)';
  270. self::$builtInFormats[38] = '#,##0_);[Red](#,##0)'; // Despite ECMA '#,##0 ;[Red](#,##0)';
  271. self::$builtInFormats[39] = '#,##0.00_);(#,##0.00)'; // Despite ECMA '#,##0.00;(#,##0.00)';
  272. self::$builtInFormats[40] = '#,##0.00_);[Red](#,##0.00)'; // Despite ECMA '#,##0.00;[Red](#,##0.00)';
  273. self::$builtInFormats[44] = '_("$"* #,##0.00_);_("$"* \(#,##0.00\);_("$"* "-"??_);_(@_)';
  274. self::$builtInFormats[45] = 'mm:ss';
  275. self::$builtInFormats[46] = '[h]:mm:ss';
  276. self::$builtInFormats[47] = 'mm:ss.0'; // Despite ECMA 'mmss.0';
  277. self::$builtInFormats[48] = '##0.0E+0';
  278. self::$builtInFormats[49] = '@';
  279. // CHT
  280. self::$builtInFormats[27] = '[$-404]e/m/d';
  281. self::$builtInFormats[30] = 'm/d/yy';
  282. self::$builtInFormats[36] = '[$-404]e/m/d';
  283. self::$builtInFormats[50] = '[$-404]e/m/d';
  284. self::$builtInFormats[57] = '[$-404]e/m/d';
  285. // THA
  286. self::$builtInFormats[59] = 't0';
  287. self::$builtInFormats[60] = 't0.00';
  288. self::$builtInFormats[61] = 't#,##0';
  289. self::$builtInFormats[62] = 't#,##0.00';
  290. self::$builtInFormats[67] = 't0%';
  291. self::$builtInFormats[68] = 't0.00%';
  292. self::$builtInFormats[69] = 't# ?/?';
  293. self::$builtInFormats[70] = 't# ??/??';
  294. // Flip array (for faster lookups)
  295. self::$flippedBuiltInFormats = array_flip(self::$builtInFormats);
  296. }
  297. }
  298. /**
  299. * Get built-in format code
  300. *
  301. * @param int $pIndex
  302. * @return string
  303. */
  304. public static function builtInFormatCode($pIndex)
  305. {
  306. // Clean parameter
  307. $pIndex = intval($pIndex);
  308. // Ensure built-in format codes are available
  309. self::fillBuiltInFormatCodes();
  310. // Lookup format code
  311. if (isset(self::$builtInFormats[$pIndex])) {
  312. return self::$builtInFormats[$pIndex];
  313. }
  314. return '';
  315. }
  316. /**
  317. * Get built-in format code index
  318. *
  319. * @param string $formatCode
  320. * @return int|boolean
  321. */
  322. public static function builtInFormatCodeIndex($formatCode)
  323. {
  324. // Ensure built-in format codes are available
  325. self::fillBuiltInFormatCodes();
  326. // Lookup format code
  327. if (isset(self::$flippedBuiltInFormats[$formatCode])) {
  328. return self::$flippedBuiltInFormats[$formatCode];
  329. }
  330. return false;
  331. }
  332. /**
  333. * Get hash code
  334. *
  335. * @return string Hash code
  336. */
  337. public function getHashCode()
  338. {
  339. if ($this->isSupervisor) {
  340. return $this->getSharedComponent()->getHashCode();
  341. }
  342. return md5(
  343. $this->formatCode .
  344. $this->builtInFormatCode .
  345. __CLASS__
  346. );
  347. }
  348. /**
  349. * Search/replace values to convert Excel date/time format masks to PHP format masks
  350. *
  351. * @var array
  352. */
  353. private static $dateFormatReplacements = array(
  354. // first remove escapes related to non-format characters
  355. '\\' => '',
  356. // 12-hour suffix
  357. 'am/pm' => 'A',
  358. // 4-digit year
  359. 'e' => 'Y',
  360. 'yyyy' => 'Y',
  361. // 2-digit year
  362. 'yy' => 'y',
  363. // first letter of month - no php equivalent
  364. 'mmmmm' => 'M',
  365. // full month name
  366. 'mmmm' => 'F',
  367. // short month name
  368. 'mmm' => 'M',
  369. // mm is minutes if time, but can also be month w/leading zero
  370. // so we try to identify times be the inclusion of a : separator in the mask
  371. // It isn't perfect, but the best way I know how
  372. ':mm' => ':i',
  373. 'mm:' => 'i:',
  374. // month leading zero
  375. 'mm' => 'm',
  376. // month no leading zero
  377. 'm' => 'n',
  378. // full day of week name
  379. 'dddd' => 'l',
  380. // short day of week name
  381. 'ddd' => 'D',
  382. // days leading zero
  383. 'dd' => 'd',
  384. // days no leading zero
  385. 'd' => 'j',
  386. // seconds
  387. 'ss' => 's',
  388. // fractional seconds - no php equivalent
  389. '.s' => ''
  390. );
  391. /**
  392. * Search/replace values to convert Excel date/time format masks hours to PHP format masks (24 hr clock)
  393. *
  394. * @var array
  395. */
  396. private static $dateFormatReplacements24 = array(
  397. 'hh' => 'H',
  398. 'h' => 'G'
  399. );
  400. /**
  401. * Search/replace values to convert Excel date/time format masks hours to PHP format masks (12 hr clock)
  402. *
  403. * @var array
  404. */
  405. private static $dateFormatReplacements12 = array(
  406. 'hh' => 'h',
  407. 'h' => 'g'
  408. );
  409. private static function setLowercaseCallback($matches) {
  410. return mb_strtolower($matches[0]);
  411. }
  412. private static function escapeQuotesCallback($matches) {
  413. return '\\' . implode('\\', str_split($matches[1]));
  414. }
  415. private static function formatAsDate(&$value, &$format)
  416. {
  417. // strip off first part containing e.g. [$-F800] or [$USD-409]
  418. // general syntax: [$<Currency string>-<language info>]
  419. // language info is in hexadecimal
  420. $format = preg_replace('/^(\[\$[A-Z]*-[0-9A-F]*\])/i', '', $format);
  421. // OpenOffice.org uses upper-case number formats, e.g. 'YYYY', convert to lower-case;
  422. // but we don't want to change any quoted strings
  423. $format = preg_replace_callback('/(?:^|")([^"]*)(?:$|")/', array('self', 'setLowercaseCallback'), $format);
  424. // Only process the non-quoted blocks for date format characters
  425. $blocks = explode('"', $format);
  426. foreach($blocks as $key => &$block) {
  427. if ($key % 2 == 0) {
  428. $block = strtr($block, self::$dateFormatReplacements);
  429. if (!strpos($block, 'A')) {
  430. // 24-hour time format
  431. $block = strtr($block, self::$dateFormatReplacements24);
  432. } else {
  433. // 12-hour time format
  434. $block = strtr($block, self::$dateFormatReplacements12);
  435. }
  436. }
  437. }
  438. $format = implode('"', $blocks);
  439. // escape any quoted characters so that DateTime format() will render them correctly
  440. $format = preg_replace_callback('/"(.*)"/U', array('self', 'escapeQuotesCallback'), $format);
  441. $dateObj = PHPExcel_Shared_Date::ExcelToPHPObject($value);
  442. $value = $dateObj->format($format);
  443. }
  444. private static function formatAsPercentage(&$value, &$format)
  445. {
  446. if ($format === self::FORMAT_PERCENTAGE) {
  447. $value = round((100 * $value), 0) . '%';
  448. } else {
  449. if (preg_match('/\.[#0]+/i', $format, $m)) {
  450. $s = substr($m[0], 0, 1) . (strlen($m[0]) - 1);
  451. $format = str_replace($m[0], $s, $format);
  452. }
  453. if (preg_match('/^[#0]+/', $format, $m)) {
  454. $format = str_replace($m[0], strlen($m[0]), $format);
  455. }
  456. $format = '%' . str_replace('%', 'f%%', $format);
  457. $value = sprintf($format, 100 * $value);
  458. }
  459. }
  460. private static function formatAsFraction(&$value, &$format)
  461. {
  462. $sign = ($value < 0) ? '-' : '';
  463. $integerPart = floor(abs($value));
  464. $decimalPart = trim(fmod(abs($value), 1), '0.');
  465. $decimalLength = strlen($decimalPart);
  466. $decimalDivisor = pow(10, $decimalLength);
  467. $GCD = PHPExcel_Calculation_MathTrig::GCD($decimalPart, $decimalDivisor);
  468. $adjustedDecimalPart = $decimalPart/$GCD;
  469. $adjustedDecimalDivisor = $decimalDivisor/$GCD;
  470. if ((strpos($format, '0') !== false) || (strpos($format, '#') !== false) || (substr($format, 0, 3) == '? ?')) {
  471. if ($integerPart == 0) {
  472. $integerPart = '';
  473. }
  474. $value = "$sign$integerPart $adjustedDecimalPart/$adjustedDecimalDivisor";
  475. } else {
  476. $adjustedDecimalPart += $integerPart * $adjustedDecimalDivisor;
  477. $value = "$sign$adjustedDecimalPart/$adjustedDecimalDivisor";
  478. }
  479. }
  480. private static function complexNumberFormatMask($number, $mask, $level = 0)
  481. {
  482. $sign = ($number < 0.0);
  483. $number = abs($number);
  484. if (strpos($mask, '.') !== false) {
  485. $numbers = explode('.', $number . '.0');
  486. $masks = explode('.', $mask . '.0');
  487. $result1 = self::complexNumberFormatMask($numbers[0], $masks[0], 1);
  488. $result2 = strrev(self::complexNumberFormatMask(strrev($numbers[1]), strrev($masks[1]), 1));
  489. return (($sign) ? '-' : '') . $result1 . '.' . $result2;
  490. }
  491. $r = preg_match_all('/0+/', $mask, $result, PREG_OFFSET_CAPTURE);
  492. if ($r > 1) {
  493. $result = array_reverse($result[0]);
  494. foreach ($result as $block) {
  495. $divisor = 1 . $block[0];
  496. $size = strlen($block[0]);
  497. $offset = $block[1];
  498. $blockValue = sprintf(
  499. '%0' . $size . 'd',
  500. fmod($number, $divisor)
  501. );
  502. $number = floor($number / $divisor);
  503. $mask = substr_replace($mask, $blockValue, $offset, $size);
  504. }
  505. if ($number > 0) {
  506. $mask = substr_replace($mask, $number, $offset, 0);
  507. }
  508. $result = $mask;
  509. } else {
  510. $result = $number;
  511. }
  512. return (($sign) ? '-' : '') . $result;
  513. }
  514. /**
  515. * Convert a value in a pre-defined format to a PHP string
  516. *
  517. * @param mixed $value Value to format
  518. * @param string $format Format code
  519. * @param array $callBack Callback function for additional formatting of string
  520. * @return string Formatted string
  521. */
  522. public static function toFormattedString($value = '0', $format = PHPExcel_Style_NumberFormat::FORMAT_GENERAL, $callBack = null)
  523. {
  524. // For now we do not treat strings although section 4 of a format code affects strings
  525. if (!is_numeric($value)) {
  526. return $value;
  527. }
  528. // For 'General' format code, we just pass the value although this is not entirely the way Excel does it,
  529. // it seems to round numbers to a total of 10 digits.
  530. if (($format === PHPExcel_Style_NumberFormat::FORMAT_GENERAL) || ($format === PHPExcel_Style_NumberFormat::FORMAT_TEXT)) {
  531. return $value;
  532. }
  533. // Convert any other escaped characters to quoted strings, e.g. (\T to "T")
  534. $format = preg_replace('/(\\\(.))(?=(?:[^"]|"[^"]*")*$)/u', '"${2}"', $format);
  535. // Get the sections, there can be up to four sections, separated with a semi-colon (but only if not a quoted literal)
  536. $sections = preg_split('/(;)(?=(?:[^"]|"[^"]*")*$)/u', $format);
  537. // Extract the relevant section depending on whether number is positive, negative, or zero?
  538. // Text not supported yet.
  539. // Here is how the sections apply to various values in Excel:
  540. // 1 section: [POSITIVE/NEGATIVE/ZERO/TEXT]
  541. // 2 sections: [POSITIVE/ZERO/TEXT] [NEGATIVE]
  542. // 3 sections: [POSITIVE/TEXT] [NEGATIVE] [ZERO]
  543. // 4 sections: [POSITIVE] [NEGATIVE] [ZERO] [TEXT]
  544. switch (count($sections)) {
  545. case 1:
  546. $format = $sections[0];
  547. break;
  548. case 2:
  549. $format = ($value >= 0) ? $sections[0] : $sections[1];
  550. $value = abs($value); // Use the absolute value
  551. break;
  552. case 3:
  553. $format = ($value > 0) ?
  554. $sections[0] : ( ($value < 0) ?
  555. $sections[1] : $sections[2]);
  556. $value = abs($value); // Use the absolute value
  557. break;
  558. case 4:
  559. $format = ($value > 0) ?
  560. $sections[0] : ( ($value < 0) ?
  561. $sections[1] : $sections[2]);
  562. $value = abs($value); // Use the absolute value
  563. break;
  564. default:
  565. // something is wrong, just use first section
  566. $format = $sections[0];
  567. break;
  568. }
  569. // In Excel formats, "_" is used to add spacing,
  570. // The following character indicates the size of the spacing, which we can't do in HTML, so we just use a standard space
  571. $format = preg_replace('/_./', ' ', $format);
  572. // Save format with color information for later use below
  573. $formatColor = $format;
  574. // Strip color information
  575. $color_regex = '/^\\[[a-zA-Z]+\\]/';
  576. $format = preg_replace($color_regex, '', $format);
  577. // Let's begin inspecting the format and converting the value to a formatted string
  578. // Check for date/time characters (not inside quotes)
  579. if (preg_match('/(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy](?=(?:[^"]|"[^"]*")*$)/miu', $format, $matches)) {
  580. // datetime format
  581. self::formatAsDate($value, $format);
  582. } elseif (preg_match('/%$/', $format)) {
  583. // % number format
  584. self::formatAsPercentage($value, $format);
  585. } else {
  586. if ($format === self::FORMAT_CURRENCY_EUR_SIMPLE) {
  587. $value = 'EUR ' . sprintf('%1.2f', $value);
  588. } else {
  589. // Some non-number strings are quoted, so we'll get rid of the quotes, likewise any positional * symbols
  590. $format = str_replace(array('"', '*'), '', $format);
  591. // Find out if we need thousands separator
  592. // This is indicated by a comma enclosed by a digit placeholder:
  593. // #,# or 0,0
  594. $useThousands = preg_match('/(#,#|0,0)/', $format);
  595. if ($useThousands) {
  596. $format = preg_replace('/0,0/', '00', $format);
  597. $format = preg_replace('/#,#/', '##', $format);
  598. }
  599. // Scale thousands, millions,...
  600. // This is indicated by a number of commas after a digit placeholder:
  601. // #, or 0.0,,
  602. $scale = 1; // same as no scale
  603. $matches = array();
  604. if (preg_match('/(#|0)(,+)/', $format, $matches)) {
  605. $scale = pow(1000, strlen($matches[2]));
  606. // strip the commas
  607. $format = preg_replace('/0,+/', '0', $format);
  608. $format = preg_replace('/#,+/', '#', $format);
  609. }
  610. if (preg_match('/#?.*\?\/\?/', $format, $m)) {
  611. //echo 'Format mask is fractional '.$format.' <br />';
  612. if ($value != (int)$value) {
  613. self::formatAsFraction($value, $format);
  614. }
  615. } else {
  616. // Handle the number itself
  617. // scale number
  618. $value = $value / $scale;
  619. // Strip #
  620. $format = preg_replace('/\\#/', '0', $format);
  621. $n = "/\[[^\]]+\]/";
  622. $m = preg_replace($n, '', $format);
  623. $number_regex = "/(0+)(\.?)(0*)/";
  624. if (preg_match($number_regex, $m, $matches)) {
  625. $left = $matches[1];
  626. $dec = $matches[2];
  627. $right = $matches[3];
  628. // minimun width of formatted number (including dot)
  629. $minWidth = strlen($left) + strlen($dec) + strlen($right);
  630. if ($useThousands) {
  631. $value = number_format(
  632. $value,
  633. strlen($right),
  634. PHPExcel_Shared_String::getDecimalSeparator(),
  635. PHPExcel_Shared_String::getThousandsSeparator()
  636. );
  637. $value = preg_replace($number_regex, $value, $format);
  638. } else {
  639. if (preg_match('/[0#]E[+-]0/i', $format)) {
  640. // Scientific format
  641. $value = sprintf('%5.2E', $value);
  642. } elseif (preg_match('/0([^\d\.]+)0/', $format)) {
  643. $value = self::complexNumberFormatMask($value, $format);
  644. } else {
  645. $sprintf_pattern = "%0$minWidth." . strlen($right) . "f";
  646. $value = sprintf($sprintf_pattern, $value);
  647. $value = preg_replace($number_regex, $value, $format);
  648. }
  649. }
  650. }
  651. }
  652. if (preg_match('/\[\$(.*)\]/u', $format, $m)) {
  653. // Currency or Accounting
  654. $currencyFormat = $m[0];
  655. $currencyCode = $m[1];
  656. list($currencyCode) = explode('-', $currencyCode);
  657. if ($currencyCode == '') {
  658. $currencyCode = PHPExcel_Shared_String::getCurrencyCode();
  659. }
  660. $value = preg_replace('/\[\$([^\]]*)\]/u', $currencyCode, $value);
  661. }
  662. }
  663. }
  664. // Escape any escaped slashes to a single slash
  665. $format = preg_replace("/\\\\/u", '\\', $format);
  666. // Additional formatting provided by callback function
  667. if ($callBack !== null) {
  668. list($writerInstance, $function) = $callBack;
  669. $value = $writerInstance->$function($value, $formatColor);
  670. }
  671. return $value;
  672. }
  673. }