diff --git a/README.md b/README.md
index 7f97168..e48a176 100644
--- a/README.md
+++ b/README.md
@@ -133,7 +133,7 @@ $writer->setShouldAddBOM(false);
#### Row styling
-It is possible to apply some formatting options to a row. Spout supports fonts, borders as well as alignment styles.
+It is possible to apply some formatting options to a row. Spout supports fonts, background, borders as well as alignment styles.
```php
use Box\Spout\Common\Type;
@@ -146,6 +146,7 @@ $style = (new StyleBuilder())
->setFontSize(15)
->setFontColor(Color::BLUE)
->setShouldWrapText()
+ ->setBackgroundColor(Color::YELLOW)
->build();
$writer = WriterFactory::create(Type::XLSX);
diff --git a/src/Spout/Writer/ODS/Helper/StyleHelper.php b/src/Spout/Writer/ODS/Helper/StyleHelper.php
index 7dfb828..9a0eeee 100644
--- a/src/Spout/Writer/ODS/Helper/StyleHelper.php
+++ b/src/Spout/Writer/ODS/Helper/StyleHelper.php
@@ -265,6 +265,11 @@ EOD;
$content .= sprintf($borderProperty, implode(' ', $borders));
}
+ if ($style->shouldApplyBackgroundColor()) {
+ $content .= sprintf('
+ ', $style->getBackgroundColor());
+ }
+
$content .= '';
return $content;
diff --git a/src/Spout/Writer/Style/Style.php b/src/Spout/Writer/Style/Style.php
index b595b3a..a8813fb 100644
--- a/src/Spout/Writer/Style/Style.php
+++ b/src/Spout/Writer/Style/Style.php
@@ -71,6 +71,13 @@ class Style
*/
protected $shouldApplyBorder = false;
+ /** @var string Background color */
+ protected $backgroundColor = null;
+
+ /** @var bool */
+ protected $hasSetBackgroundColor = false;
+
+
/**
* @return int|null
*/
@@ -279,6 +286,35 @@ class Style
return $this->shouldApplyFont;
}
+ /**
+ * Sets the background color
+ * @param $color ARGB color (@see Color)
+ * @return Style
+ */
+ public function setBackgroundColor($color)
+ {
+ $this->hasSetBackgroundColor = true;
+ $this->backgroundColor = $color;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getBackgroundColor()
+ {
+ return $this->backgroundColor;
+ }
+
+ /**
+ *
+ * @return bool Whether the background color should be applied
+ */
+ public function shouldApplyBackgroundColor()
+ {
+ return $this->hasSetBackgroundColor;
+ }
+
/**
* Serializes the style for future comparison with other styles.
* The ID is excluded from the comparison, as we only care about
@@ -341,6 +377,9 @@ class Style
if (!$this->getBorder() && $baseStyle->shouldApplyBorder()) {
$mergedStyle->setBorder($baseStyle->getBorder());
}
+ if (!$this->hasSetBackgroundColor && $baseStyle->shouldApplyBackgroundColor()) {
+ $mergedStyle->setBackgroundColor($baseStyle->getBackgroundColor());
+ }
return $mergedStyle;
}
diff --git a/src/Spout/Writer/Style/StyleBuilder.php b/src/Spout/Writer/Style/StyleBuilder.php
index 6d6239c..d620de4 100644
--- a/src/Spout/Writer/Style/StyleBuilder.php
+++ b/src/Spout/Writer/Style/StyleBuilder.php
@@ -133,6 +133,19 @@ class StyleBuilder
return $this;
}
+ /**
+ * Sets a background color
+ *
+ * @api
+ * @param string $color ARGB color (@see Color)
+ * @return StyleBuilder
+ */
+ public function setBackgroundColor($color)
+ {
+ $this->style->setBackgroundColor($color);
+ return $this;
+ }
+
/**
* Returns the configured style. The style is cached and can be reused.
*
diff --git a/src/Spout/Writer/XLSX/Helper/StyleHelper.php b/src/Spout/Writer/XLSX/Helper/StyleHelper.php
index e13997e..abaa8d9 100644
--- a/src/Spout/Writer/XLSX/Helper/StyleHelper.php
+++ b/src/Spout/Writer/XLSX/Helper/StyleHelper.php
@@ -4,6 +4,7 @@ namespace Box\Spout\Writer\XLSX\Helper;
use Box\Spout\Writer\Common\Helper\AbstractStyleHelper;
use Box\Spout\Writer\Style\Color;
+use Box\Spout\Writer\Style\Style;
/**
* Class StyleHelper
@@ -13,6 +14,64 @@ use Box\Spout\Writer\Style\Color;
*/
class StyleHelper extends AbstractStyleHelper
{
+ /**
+ * @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;
+
+ /**
+ * XLSX specific operations on the registered styles
+ *
+ * @param \Box\Spout\Writer\Style\Style $style
+ * @return \Box\Spout\Writer\Style\Style
+ */
+ public function registerStyle($style)
+ {
+ $registeredStyle = parent::registerStyle($style);
+ $this->registerFill($registeredStyle);
+ return $registeredStyle;
+ }
+
+ /**
+ * Register a fill definition
+ *
+ * @param \Box\Spout\Writer\Style\Style $style
+ */
+ protected function registerFill($style)
+ {
+ $styleId = $style->getId();
+
+ // Currently - only solid backgrounds are supported
+ // so $backgroundColor is a scalar value (RGB Color)
+ $backgroundColor = $style->getBackgroundColor();
+
+ // We need to track the already registered background definitions
+ if (isset($backgroundColor) && !isset($this->registeredFills[$backgroundColor])) {
+ $this->registeredFills[$backgroundColor] = $styleId;
+ }
+
+ if (!isset($this->styleIdToFillMappingTable[$styleId])) {
+ // The fillId maps a style to a fill declaration
+ // When there is no background color definition - we default to 0
+ $fillId = $backgroundColor !== null ? $this->fillIndex++ : 0;
+ $this->styleIdToFillMappingTable[$styleId] = $fillId;
+ }
+ }
+
+
/**
* Returns the content of the "styles.xml" file, given a list of styles.
*
@@ -84,13 +143,29 @@ EOD;
*/
protected function getFillsSectionContent()
{
- return <<
-
-
-
-
-EOD;
+ // Excel reserves two default fills
+ $fillsCount = count($this->registeredFills) + 2;
+ $content = sprintf('', $fillsCount);
+
+ $content .= '';
+ $content .= '';
+
+ // The other fills are actually registered by setting a background color
+ foreach ($this->registeredFills as $styleId) {
+
+ /** @var Style $style */
+ $style = $this->styleIdToStyleMappingTable[$styleId];
+
+ $backgroundColor = $style->getBackgroundColor();
+ $content .= sprintf(
+ '',
+ $backgroundColor
+ );
+ }
+
+ $content .= '';
+
+ return $content;
}
/**
@@ -160,7 +235,11 @@ EOD;
$content = '';
foreach ($registeredStyles as $style) {
- $content .= 'getId();
+ $fillId = $this->styleIdToFillMappingTable[$styleId];
+
+ $content .= 'shouldApplyFont()) {
$content .= ' applyFont="1"';
diff --git a/tests/Spout/Writer/ODS/WriterWithStyleTest.php b/tests/Spout/Writer/ODS/WriterWithStyleTest.php
index 7a7c190..157b7f0 100644
--- a/tests/Spout/Writer/ODS/WriterWithStyleTest.php
+++ b/tests/Spout/Writer/ODS/WriterWithStyleTest.php
@@ -118,6 +118,7 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase
->setFontSize(15)
->setFontColor(Color::RED)
->setFontName('Cambria')
+ ->setBackgroundColor(Color::GREEN)
->build();
$this->writeToODSFileWithMultipleStyles($dataRows, $fileName, [$style, $style2]);
@@ -137,6 +138,7 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase
$this->assertFirstChildHasAttributeEquals('15pt', $customFont2Element, 'text-properties', 'fo:font-size');
$this->assertFirstChildHasAttributeEquals('#' . Color::RED, $customFont2Element, 'text-properties', 'fo:color');
$this->assertFirstChildHasAttributeEquals('Cambria', $customFont2Element, 'text-properties', 'style:font-name');
+ $this->assertFirstChildHasAttributeEquals('#' . Color::GREEN, $customFont2Element, 'table-cell-properties', 'fo:background-color');
}
/**
@@ -239,6 +241,26 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase
$this->assertFirstChildHasAttributeEquals('wrap', $customStyleElement, 'table-cell-properties', 'fo:wrap-option');
}
+ /**
+ * @return void
+ */
+ public function testAddBackgroundColor()
+ {
+ $fileName = 'test_default_background_style.ods';
+ $dataRows = [
+ ['defaultBgColor'],
+ ];
+
+ $style = (new StyleBuilder())->setBackgroundColor(Color::WHITE)->build();
+ $this->writeToODSFile($dataRows, $fileName, $style);
+
+ $styleElements = $this->getCellStyleElementsFromContentXmlFile($fileName);
+ $this->assertEquals(2, count($styleElements), 'There should be 2 styles (default and custom)');
+
+ $customStyleElement = $styleElements[1];
+ $this->assertFirstChildHasAttributeEquals('#' . Color::WHITE, $customStyleElement, 'table-cell-properties', 'fo:background-color');
+ }
+
/**
* @return void
*/
diff --git a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php
index 7596efa..19a11ad 100644
--- a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php
+++ b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php
@@ -126,7 +126,6 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase
$fontElements = $fontsDomElement->getElementsByTagName('font');
$this->assertEquals(3, $fontElements->length, 'There should be 3 associated "font" elements, including the default one.');
-
// First font should be the default one
$defaultFontElement = $fontElements->item(0);
$this->assertChildrenNumEquals(3, $defaultFontElement, 'The default font should only have 3 properties.');
@@ -234,6 +233,34 @@ class WriterWithStyleTest extends \PHPUnit_Framework_TestCase
$this->assertFirstChildHasAttributeEquals('1', $xfElement, 'alignment', 'wrapText');
}
+ /**
+ * @return void
+ */
+ public function testAddBackgroundColor()
+ {
+ $fileName = 'test_add_background_color.xlsx';
+ $dataRows = [
+ ["BgColor"],
+ ];
+ $style = (new StyleBuilder())->setBackgroundColor(Color::WHITE)->build();
+ $this->writeToXLSXFile($dataRows, $fileName, $style);
+ $fillsDomElement = $this->getXmlSectionFromStylesXmlFile($fileName, 'fills');
+ $this->assertEquals(3, $fillsDomElement->getAttribute('count'), 'There should be 3 fills, including the 2 default ones');
+
+ $fillsElements = $fillsDomElement->getElementsByTagName('fill');
+
+ $thirdFillElement = $fillsElements->item(2); // Zero based
+ $fgColor = $thirdFillElement->getElementsByTagName('fgColor')->item(0)->getAttribute('rgb');
+
+ $this->assertEquals(Color::WHITE, $fgColor, 'The foreground color should equal white');
+
+ $styleXfsElements = $this->getXmlSectionFromStylesXmlFile($fileName, 'cellXfs');
+ $this->assertEquals(2, $styleXfsElements->getAttribute('count'), '2 cell xfs present - a default one and a custom one');
+
+ $customFillId = $styleXfsElements->lastChild->getAttribute('fillId');
+ $this->assertEquals(2, (int)$customFillId, 'The custom fill id should have the index 2');
+ }
+
/**
* @return void
*/