add support for number formats

This commit is contained in:
willkensonh 2018-10-12 16:43:01 +11:00
parent 39d16b5bbf
commit a8b3568719
4 changed files with 231 additions and 2 deletions

View File

@ -0,0 +1,114 @@
<?php
namespace Box\Spout\Common\Entity\Style;
/**
* Class Style
* Represents a style to be applied to a cell
*/
class NumberFormat
{
const TYPE_CURRENCY = 1;
const TYPE_PERCENTAGE = 2;
const TYPE_NUMERIC = 3;
const TYPES = [
self::TYPE_CURRENCY,
self::TYPE_PERCENTAGE,
self::TYPE_NUMERIC,
];
private $id;
private $type;
private $minDecimalPlaces;
private $maxDecimalPlaces;
private $currencySymbol;
private $commas;
/**
* @param int $id
* @return NumberFormat
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* @return int|null
*/
public function getId()
{
return $this->id;
}
/**
* @param int $type
* @return NumberFormat
*/
public function setType($type)
{
if (!in_array($type,self::TYPES)) {
return $this;
//todo throw some excpection or something
}
$this->type = $type;
if ($type == self::TYPE_CURRENCY) {
if (($this->minDecimalPlaces === null) && ($this->maxDecimalPlaces === null)) {
$this->setDecimalPlaces(2,2);
}
if ($this->currencySymbol === null) {
$this->setCurrencySymbol('$');
}
}
return $this;
}
/**
* @param int $minDecimalPlaces
* @param int $maxDecimalPlaces
* @return NumberFormat
*/
public function setDecimalPlaces(int $minDecimalPlaces = null, int $maxDecimalPlaces = null)
{
$this->minDecimalPlaces = $minDecimalPlaces;
$this->maxDecimalPlaces = $maxDecimalPlaces;
return $this;
}
/**
* @param int $currencySymbol
* @return NumberFormat
*/
public function setCurrencySymbol(string $currencySymbol)
{
$this->currencySymbol = $currencySymbol;
return $this;
}
/**
* @return string
*/
public function getFormatCode()
{
//todo spit out xml tag
$formatString = $this->type == self::TYPE_CURRENCY ? '$_' : '';
$formatString .= ($this->commas ? '#,##0' : '#0');
$formatString .= '.';
for ($i = 0; $i < $this->minDecimalPlaces; $i++) {
$formatString .= '0';
}
for ($i = $this->minDecimalPlaces; $i < $this->maxDecimalPlaces; $i++) {
$formatString .= '#';
}
if ($this->type == self::TYPE_PERCENTAGE) {
$formatString .= '%';
}
return $formatString;
}
}

View File

@ -71,6 +71,8 @@ class Style
/** @var bool */
private $hasSetBackgroundColor = false;
private $numberFormat;
/**
* @return int|null
*/
@ -110,6 +112,25 @@ class Style
return $this;
}
/**
* @return NumberFormat
*/
public function getNumberFormat()
{
return $this->numberFormat ?? null;
}
/**
* @param NumberFormat $format
* @return Style
*/
public function setNumberFormat(NumberFormat $format)
{
$this->numberFormat = $format;
return $this;
}
/**
* @return bool
*/

View File

@ -2,6 +2,7 @@
namespace Box\Spout\Writer\Common\Manager\Style;
use Box\Spout\Common\Entity\Style\NumberFormat;
use Box\Spout\Common\Entity\Style\Style;
/**
@ -12,10 +13,13 @@ class StyleRegistry
{
/** @var array [SERIALIZED_STYLE] => [STYLE_ID] mapping table, keeping track of the registered styles */
protected $serializedStyleToStyleIdMappingTable = [];
protected $serializedNumberFormatToFormatIdMappingTable = [];
/** @var array [STYLE_ID] => [STYLE] mapping table, keeping track of the registered styles */
protected $styleIdToStyleMappingTable = [];
protected $numberFormats = [];
/**
* @param Style $defaultStyle
*/
@ -34,6 +38,11 @@ class StyleRegistry
*/
public function registerStyle(Style $style)
{
$format = $style->getNumberFormat();
if (!empty($format)) {
$registeredFormat = $this->registerNumberFormat($format);
$style->setNumberFormat($registeredFormat);
}
$serializedStyle = $this->serialize($style);
if (!$this->hasStyleAlreadyBeenRegistered($style)) {
@ -47,6 +56,19 @@ class StyleRegistry
return $this->getStyleFromSerializedStyle($serializedStyle);
}
public function registerNumberFormat(NumberFormat $format)
{
$serializedFormat = $this->serializeFormat($format);
if (!$this->hasFormatAlreadyBeenRegistered($format)) {
$nextFormatId = count($this->serializedNumberFormatToFormatIdMappingTable);
$format->setId($nextFormatId);
$this->serializedNumberFormatToFormatIdMappingTable[$serializedFormat] = $nextFormatId;
$this->numberFormats[$nextFormatId] = $format;
}
return $this->getFormatFromSerializedFormat($serializedFormat);
}
/**
* Returns whether the given style has already been registered.
*
@ -61,6 +83,20 @@ class StyleRegistry
return isset($this->serializedStyleToStyleIdMappingTable[$serializedStyle]);
}
/**
* Returns whether the given number format has already been registered.
*
* @param NumberFormat $format
* @return bool
*/
protected function hasFormatAlreadyBeenRegistered(NumberFormat $format)
{
$serializedFormat = $this->serializeFormat($format);
// Using isset here because it is way faster than array_key_exists...
return isset($this->serializedNumberFormatToFormatIdMappingTable[$serializedFormat]);
}
/**
* Returns the registered style associated to the given serialization.
*
@ -74,6 +110,19 @@ class StyleRegistry
return $this->styleIdToStyleMappingTable[$styleId];
}
/**
* Returns the registered number format associated to the given serialization.
*
* @param string $serializedFormat The serialized number format from which the actual format should be fetched from
* @return NumberFormat
*/
protected function getFormatFromSerializedFormat($serializedFormat)
{
$formatId = $this->serializedNumberFormatToFormatIdMappingTable[$serializedFormat];
return $this->numberFormats[$formatId];
}
/**
* @return Style[] List of registered styles
*/
@ -82,6 +131,14 @@ class StyleRegistry
return array_values($this->styleIdToStyleMappingTable);
}
/**
* @return NumberFormat[] List of registered number formats
*/
public function getRegisteredNumberFormats()
{
return array_values($this->numberFormats);
}
/**
* @param int $styleId
* @return Style
@ -111,4 +168,25 @@ class StyleRegistry
return $serializedStyle;
}
/**
* Serializes the number format for future comparison with other formats.
* The ID is excluded from the comparison, as we only care about
* actual number format properties.
*
* @param Style $style
* @return string The serialized style
*/
public function serializeFormat(NumberFormat $format)
{
// In order to be able to properly compare style, set static ID value
$currentId = $format->getId();
$format->setId(0);
$serializedFormat = serialize($format);
$format->setId($currentId);
return $serializedFormat;
}
}

View File

@ -47,7 +47,7 @@ class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
EOD;
$content .= $this->getNumberFormatSectionContent();
$content .= $this->getFontsSectionContent();
$content .= $this->getFillsSectionContent();
$content .= $this->getBordersSectionContent();
@ -62,6 +62,22 @@ EOD;
return $content;
}
protected function getNumberFormatSectionContent()
{
$registeredFormats = $this->styleRegistry->getRegisteredNumberFormats();
if (empty($registeredFormats)) {
return '<numFmts count="0"/>';
}
$content = '<numFmts count="'.count($registeredFormats).'">';
foreach ($registeredFormats as $format) {
$content .= '<numFmt numFmtId="'.$format->getId().'" formatCode="'.$format->getFormatCode().'"/>';
}
$content .= '</numFmts>';
return $content;
}
/**
* Returns the content of the "<fonts>" section.
*
@ -207,7 +223,7 @@ EOD;
$fillId = $this->getFillIdForStyleId($styleId);
$borderId = $this->getBorderIdForStyleId($styleId);
$content .= '<xf numFmtId="0" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
$content .= '<xf numFmtId="'.$style->getNumberFormat()->getId().'" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
if ($style->shouldApplyFont()) {
$content .= ' applyFont="1"';