Refactory Writer Styles to match new code organization (#433)

Decomposed old StyleHelper into StyleManager, StyleRegistry and StyleMerger.
This commit is contained in:
Adrien Loison authored and GitHub committed 2017-05-30 13:05:18 +02:00
1 parent 238756ab6e
commit cc9a0b526b
34 files changed
+1005 -762

No files matched your search

@@ -0,0 +1,248 @@
<?php
namespace Box\Spout\Writer\XLSX\Manager\Style;
use Box\Spout\Writer\Common\Entity\Style\Color;
use Box\Spout\Writer\Common\Entity\Style\Style;
use Box\Spout\Writer\XLSX\Helper\BorderHelper;
/**
* Class StyleManager
* Manages styles to be applied to a cell
*
* @package Box\Spout\Writer\XLSX\Manager\Style
*/
class StyleManager extends \Box\Spout\Writer\Common\Manager\Style\StyleManager
{
/** @var StyleRegistry */
protected $styleRegistry;
/**
* For empty cells, we can specify a style or not. If no style are specified,
* then the software default will be applied. But sometimes, it may be useful
* to override this default style, for instance if the cell should have a
* background color different than the default one or some borders
* (fonts property don't really matter here).
*
* @param int $styleId
* @return bool Whether the cell should define a custom style
*/
public function shouldApplyStyleOnEmptyCell($styleId)
{
$associatedFillId = $this->styleRegistry->getFillIdForStyleId($styleId);
$hasStyleCustomFill = ($associatedFillId !== null && $associatedFillId !== 0);
$associatedBorderId = $this->styleRegistry->getBorderIdForStyleId($styleId);
$hasStyleCustomBorders = ($associatedBorderId !== null && $associatedBorderId !== 0);
return ($hasStyleCustomFill || $hasStyleCustomBorders);
}
/**
* Returns the content of the "styles.xml" file, given a list of styles.
*
* @return string
*/
public function getStylesXMLFileContent()
{
$content = <<<EOD
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
EOD;
$content .= $this->getFontsSectionContent();
$content .= $this->getFillsSectionContent();
$content .= $this->getBordersSectionContent();
$content .= $this->getCellStyleXfsSectionContent();
$content .= $this->getCellXfsSectionContent();
$content .= $this->getCellStylesSectionContent();
$content .= <<<EOD
</styleSheet>
EOD;
return $content;
}
/**
* Returns the content of the "<fonts>" section.
*
* @return string
*/
protected function getFontsSectionContent()
{
$registeredStyles = $this->styleRegistry->getRegisteredStyles();
$content = '<fonts count="' . count($registeredStyles) . '">';
/** @var Style $style */
foreach ($registeredStyles as $style) {
$content .= '<font>';
$content .= '<sz val="' . $style->getFontSize() . '"/>';
$content .= '<color rgb="' . Color::toARGB($style->getFontColor()) . '"/>';
$content .= '<name val="' . $style->getFontName() . '"/>';
if ($style->isFontBold()) {
$content .= '<b/>';
}
if ($style->isFontItalic()) {
$content .= '<i/>';
}
if ($style->isFontUnderline()) {
$content .= '<u/>';
}
if ($style->isFontStrikethrough()) {
$content .= '<strike/>';
}
$content .= '</font>';
}
$content .= '</fonts>';
return $content;
}
/**
* Returns the content of the "<fills>" section.
*
* @return string
*/
protected function getFillsSectionContent()
{
$registeredFills = $this->styleRegistry->getRegisteredFills();
// Excel reserves two default fills
$fillsCount = count($registeredFills) + 2;
$content = sprintf('<fills count="%d">', $fillsCount);
$content .= '<fill><patternFill patternType="none"/></fill>';
$content .= '<fill><patternFill patternType="gray125"/></fill>';
// The other fills are actually registered by setting a background color
foreach ($registeredFills as $styleId) {
/** @var Style $style */
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$backgroundColor = $style->getBackgroundColor();
$content .= sprintf(
'<fill><patternFill patternType="solid"><fgColor rgb="%s"/></patternFill></fill>',
$backgroundColor
);
}
$content .= '</fills>';
return $content;
}
/**
* Returns the content of the "<borders>" section.
*
* @return string
*/
protected function getBordersSectionContent()
{
$registeredBorders = $this->styleRegistry->getRegisteredBorders();
// There is one default border with index 0
$borderCount = count($registeredBorders) + 1;
$content = '<borders count="' . $borderCount . '">';
// Default border starting at index 0
$content .= '<border><left/><right/><top/><bottom/></border>';
foreach ($registeredBorders as $styleId) {
/** @var \Box\Spout\Writer\Common\Entity\Style\Style $style */
$style = $this->styleRegistry->getStyleFromStyleId($styleId);
$border = $style->getBorder();
$content .= '<border>';
// @link https://github.com/box/spout/issues/271
$sortOrder = ['left', 'right', 'top', 'bottom'];
foreach ($sortOrder as $partName) {
if ($border->hasPart($partName)) {
/** @var $part \Box\Spout\Writer\Common\Entity\Style\BorderPart */
$part = $border->getPart($partName);
$content .= BorderHelper::serializeBorderPart($part);
}
}
$content .= '</border>';
}
$content .= '</borders>';
return $content;
}
/**
* Returns the content of the "<cellStyleXfs>" section.
*
* @return string
*/
protected function getCellStyleXfsSectionContent()
{
return <<<EOD
<cellStyleXfs count="1">
<xf borderId="0" fillId="0" fontId="0" numFmtId="0"/>
</cellStyleXfs>
EOD;
}
/**
* Returns the content of the "<cellXfs>" section.
*
* @return string
*/
protected function getCellXfsSectionContent()
{
$registeredStyles = $this->styleRegistry->getRegisteredStyles();
$content = '<cellXfs count="' . count($registeredStyles) . '">';
foreach ($registeredStyles as $style) {
$styleId = $style->getId();
$fillId = $this->styleRegistry->getFillIdForStyleId($styleId);
$borderId = $this->styleRegistry->getBorderIdForStyleId($styleId);
$content .= '<xf numFmtId="0" fontId="' . $styleId . '" fillId="' . $fillId . '" borderId="' . $borderId . '" xfId="0"';
if ($style->shouldApplyFont()) {
$content .= ' applyFont="1"';
}
$content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
if ($style->shouldWrapText()) {
$content .= ' applyAlignment="1">';
$content .= '<alignment wrapText="1"/>';
$content .= '</xf>';
} else {
$content .= '/>';
}
}
$content .= '</cellXfs>';
return $content;
}
/**
* Returns the content of the "<cellStyles>" section.
*
* @return string
*/
protected function getCellStylesSectionContent()
{
return <<<EOD
<cellStyles count="1">
<cellStyle builtinId="0" name="Normal" xfId="0"/>
</cellStyles>
EOD;
}
}
@@ -0,0 +1,158 @@
<?php
namespace Box\Spout\Writer\XLSX\Manager\Style;
use Box\Spout\Writer\Common\Entity\Style\Style;
/**
* Class StyleRegistry
* Registry for all used styles
*
* @package Box\Spout\Writer\XLSX\Manager\Style
*/
class StyleRegistry extends \Box\Spout\Writer\Common\Manager\Style\StyleRegistry
{
/**
* @var array
*/
protected $registeredFills = [];
/**
* @var array [STYLE_ID] => [FILL_ID] maps a style to a fill declaration
*/
protected $styleIdToFillMappingTable = [];
/**
* Excel preserves two default fills with index 0 and 1
* Since Excel is the dominant vendor - we play along here
*
* @var int The fill index counter for custom fills.
*/
protected $fillIndex = 2;
/**
* @var array
*/
protected $registeredBorders = [];
/**
* @var array [STYLE_ID] => [BORDER_ID] maps a style to a border declaration
*/
protected $styleIdToBorderMappingTable = [];
/**
* XLSX specific operations on the registered styles
*
* @param Style $style
* @return Style
*/
public function registerStyle(Style $style)
{
$registeredStyle = parent::registerStyle($style);
$this->registerFill($registeredStyle);
$this->registerBorder($registeredStyle);
return $registeredStyle;
}
/**
* Register a fill definition
*
* @param Style $style
*/
private function registerFill(Style $style)
{
$styleId = $style->getId();
// Currently - only solid backgrounds are supported
// so $backgroundColor is a scalar value (RGB Color)
$backgroundColor = $style->getBackgroundColor();
if ($backgroundColor) {
$isBackgroundColorRegistered = isset($this->registeredFills[$backgroundColor]);
// We need to track the already registered background definitions
if ($isBackgroundColorRegistered) {
$registeredStyleId = $this->registeredFills[$backgroundColor];
$registeredFillId = $this->styleIdToFillMappingTable[$registeredStyleId];
$this->styleIdToFillMappingTable[$styleId] = $registeredFillId;
} else {
$this->registeredFills[$backgroundColor] = $styleId;
$this->styleIdToFillMappingTable[$styleId] = $this->fillIndex++;
}
} else {
// The fillId maps a style to a fill declaration
// When there is no background color definition - we default to 0
$this->styleIdToFillMappingTable[$styleId] = 0;
}
}
/**
* @param int $styleId
* @return int|null Fill ID associated to the given style ID
*/
public function getFillIdForStyleId($styleId)
{
return (isset($this->styleIdToFillMappingTable[$styleId])) ?
$this->styleIdToFillMappingTable[$styleId] :
null;
}
/**
* Register a border definition
*
* @param Style $style
*/
private function registerBorder(Style $style)
{
$styleId = $style->getId();
if ($style->shouldApplyBorder()) {
$border = $style->getBorder();
$serializedBorder = serialize($border);
$isBorderAlreadyRegistered = isset($this->registeredBorders[$serializedBorder]);
if ($isBorderAlreadyRegistered) {
$registeredStyleId = $this->registeredBorders[$serializedBorder];
$registeredBorderId = $this->styleIdToBorderMappingTable[$registeredStyleId];
$this->styleIdToBorderMappingTable[$styleId] = $registeredBorderId;
} else {
$this->registeredBorders[$serializedBorder] = $styleId;
$this->styleIdToBorderMappingTable[$styleId] = count($this->registeredBorders);
}
} else {
// If no border should be applied - the mapping is the default border: 0
$this->styleIdToBorderMappingTable[$styleId] = 0;
}
}
/**
* @param int $styleId
* @return int|null Fill ID associated to the given style ID
*/
public function getBorderIdForStyleId($styleId)
{
return (isset($this->styleIdToBorderMappingTable[$styleId])) ?
$this->styleIdToBorderMappingTable[$styleId] :
null;
}
/**
* @return array
*/
public function getRegisteredFills()
{
return $this->registeredFills;
}
/**
* @return array
*/
public function getRegisteredBorders()
{
return $this->registeredBorders;
}
}
@@ -5,7 +5,7 @@ namespace Box\Spout\Writer\XLSX\Manager;
use Box\Spout\Writer\Common\Sheet;
use Box\Spout\Writer\Common\Manager\WorkbookManagerAbstract;
use Box\Spout\Writer\XLSX\Helper\FileSystemHelper;
use Box\Spout\Writer\XLSX\Helper\StyleHelper;
use Box\Spout\Writer\XLSX\Manager\Style\StyleManager;
/**
* Class WorkbookManager
@@ -24,12 +24,12 @@ class WorkbookManager extends WorkbookManagerAbstract
/** @var WorksheetManager Object used to manage worksheets */
protected $worksheetManager;
/** @var StyleManager Manages styles */
protected $styleManager;
/** @var FileSystemHelper Helper to perform file system operations */
protected $fileSystemHelper;
/** @var StyleHelper Helper to apply styles */
protected $styleHelper;
/**
* @return int Maximum number of rows/columns a sheet can contain
*/
@@ -72,7 +72,7 @@ class WorkbookManager extends WorkbookManagerAbstract
->createContentTypesFile($worksheets)
->createWorkbookFile($worksheets)
->createWorkbookRelsFile($worksheets)
->createStylesFile($this->styleHelper)
->createStylesFile($this->styleManager)
->zipRootFolderAndCopyToStream($finalFilePointer);
}
}
@@ -13,7 +13,7 @@ use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\Common\Manager\WorksheetManagerInterface;
use Box\Spout\Writer\Common\Entity\Style\Style;
use Box\Spout\Writer\XLSX\Helper\SharedStringsHelper;
use Box\Spout\Writer\XLSX\Helper\StyleHelper;
use Box\Spout\Writer\XLSX\Manager\Style\StyleManager;
/**
* Class WorksheetManager
@@ -39,12 +39,12 @@ EOD;
/** @var bool Whether inline or shared strings should be used */
protected $shouldUseInlineStrings;
/** @var StyleManager Manages styles */
private $styleManager;
/** @var SharedStringsHelper Helper to write shared strings */
private $sharedStringsHelper;
/** @var StyleHelper Helper to work with styles */
private $styleHelper;
/** @var \Box\Spout\Common\Escaper\XLSX Strings escaper */
private $stringsEscaper;
@@ -55,21 +55,21 @@ EOD;
* WorksheetManager constructor.
*
* @param OptionsManagerInterface $optionsManager
* @param StyleManager $styleManager
* @param SharedStringsHelper $sharedStringsHelper
* @param StyleHelper $styleHelper
* @param \Box\Spout\Common\Escaper\XLSX $stringsEscaper
* @param StringHelper $stringHelper
*/
public function __construct(
OptionsManagerInterface $optionsManager,
StyleManager $styleManager,
SharedStringsHelper $sharedStringsHelper,
StyleHelper $styleHelper,
\Box\Spout\Common\Escaper\XLSX $stringsEscaper,
StringHelper $stringHelper)
{
$this->shouldUseInlineStrings = $optionsManager->getOption(Options::SHOULD_USE_INLINE_STRINGS);
$this->styleManager = $styleManager;
$this->sharedStringsHelper = $sharedStringsHelper;
$this->styleHelper = $styleHelper;
$this->stringsEscaper = $stringsEscaper;
$this->stringHelper = $stringHelper;
}
@@ -211,7 +211,7 @@ EOD;
} else if ($cell->isNumeric()) {
$cellXML .= '><v>' . $cell->getValue() . '</v></c>';
} else if ($cell->isEmpty()) {
if ($this->styleHelper->shouldApplyStyleOnEmptyCell($styleId)) {
if ($this->styleManager->shouldApplyStyleOnEmptyCell($styleId)) {
$cellXML .= '/>';
} else {
// don't write empty cells that do no need styling