diff --git a/docs/_pages/documentation.md b/docs/_pages/documentation.md
index 553cf67..d63ca03 100755
--- a/docs/_pages/documentation.md
+++ b/docs/_pages/documentation.md
@@ -116,16 +116,17 @@ $reader->setShouldPreserveEmptyRows(true);
For fonts and alignments, {{ site.spout_html }} does not support all the possible formatting options yet. But you can find the most important ones:
-| Category | Property | API
-|:----------|:--------------|:--------------------------------------
-| Font | Bold | `StyleBuilder::setFontBold()`
-| | Italic | `StyleBuilder::setFontItalic()`
-| | Underline | `StyleBuilder::setFontUnderline()`
-| | Strikethrough | `StyleBuilder::setFontStrikethrough()`
-| | Font name | `StyleBuilder::setFontName('Arial')`
-| | Font size | `StyleBuilder::setFontSize(14)`
-| | Font color | `StyleBuilder::setFontColor(Color::BLUE)`
`StyleBuilder::setFontColor(Color::rgb(0, 128, 255))`
-| Alignment | Wrap text | `StyleBuilder::setShouldWrapText(true|false)`
+| Category | Property | API
+|:----------|:---------------|:--------------------------------------
+| Font | Bold | `StyleBuilder::setFontBold()`
+| | Italic | `StyleBuilder::setFontItalic()`
+| | Underline | `StyleBuilder::setFontUnderline()`
+| | Strikethrough | `StyleBuilder::setFontStrikethrough()`
+| | Font name | `StyleBuilder::setFontName('Arial')`
+| | Font size | `StyleBuilder::setFontSize(14)`
+| | Font color | `StyleBuilder::setFontColor(Color::BLUE)`
`StyleBuilder::setFontColor(Color::rgb(0, 128, 255))`
+| Alignment | Cell alignment | `StyleBuilder::setCellAlignment(CellAlignment::CENTER)`
+| | Wrap text | `StyleBuilder::setShouldWrapText(true)`
### Styling rows
@@ -135,6 +136,7 @@ It is possible to apply some formatting options to a row. In this case, all cell
```php
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
+use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Common\Entity\Style\Color;
$writer = WriterEntityFactory::createXLSXWriter();
@@ -146,6 +148,7 @@ $style = (new StyleBuilder())
->setFontSize(15)
->setFontColor(Color::BLUE)
->setShouldWrapText()
+ ->setCellAlignment(CellAlignment::RIGHT)
->setBackgroundColor(Color::YELLOW)
->build();
diff --git a/src/Spout/Common/Entity/Style/CellAlignment.php b/src/Spout/Common/Entity/Style/CellAlignment.php
new file mode 100644
index 0000000..60fe833
--- /dev/null
+++ b/src/Spout/Common/Entity/Style/CellAlignment.php
@@ -0,0 +1,32 @@
+ 1,
+ self::RIGHT => 1,
+ self::CENTER => 1,
+ self::JUSTIFY => 1,
+ ];
+
+ /**
+ * @param string $cellAlignment
+ *
+ * @return bool Whether the given cell alignment is valid
+ */
+ public static function isValid($cellAlignment)
+ {
+ return isset(self::$VALID_ALIGNMENTS[$cellAlignment]);
+ }
+}
diff --git a/src/Spout/Common/Entity/Style/Color.php b/src/Spout/Common/Entity/Style/Color.php
index 36b3d26..ce903e5 100644
--- a/src/Spout/Common/Entity/Style/Color.php
+++ b/src/Spout/Common/Entity/Style/Color.php
@@ -8,7 +8,7 @@ use Box\Spout\Common\Exception\InvalidColorException;
* Class Color
* This class provides constants and functions to work with colors
*/
-class Color
+abstract class Color
{
/** Standard colors - based on Office Online */
const BLACK = '000000';
diff --git a/src/Spout/Common/Entity/Style/Style.php b/src/Spout/Common/Entity/Style/Style.php
index 0b5c0d7..7e989a4 100644
--- a/src/Spout/Common/Entity/Style/Style.php
+++ b/src/Spout/Common/Entity/Style/Style.php
@@ -8,7 +8,7 @@ namespace Box\Spout\Common\Entity\Style;
*/
class Style
{
- /** Default font values */
+ /** Default values */
const DEFAULT_FONT_SIZE = 11;
const DEFAULT_FONT_COLOR = Color::BLACK;
const DEFAULT_FONT_NAME = 'Arial';
@@ -54,6 +54,13 @@ class Style
/** @var bool Whether specific font properties should be applied */
private $shouldApplyFont = false;
+ /** @var bool Whether specific cell alignment should be applied */
+ private $shouldApplyCellAlignment = false;
+ /** @var string Cell alignment */
+ private $cellAlignment;
+ /** @var bool Whether the cell alignment property was set */
+ private $hasSetCellAlignment = false;
+
/** @var bool Whether the text should wrap in the cell (useful for long or multi-lines text) */
private $shouldWrapText = false;
/** @var bool Whether the wrap text property was set */
@@ -325,6 +332,44 @@ class Style
return $this->hasSetFontName;
}
+ /**
+ * @return string
+ */
+ public function getCellAlignment()
+ {
+ return $this->cellAlignment;
+ }
+
+ /**
+ * @param string $cellAlignment The cell alignment
+ *
+ * @return Style
+ */
+ public function setCellAlignment($cellAlignment)
+ {
+ $this->cellAlignment = $cellAlignment;
+ $this->hasSetCellAlignment = true;
+ $this->shouldApplyCellAlignment = true;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function hasSetCellAlignment()
+ {
+ return $this->hasSetCellAlignment;
+ }
+
+ /**
+ * @return bool Whether specific cell alignment should be applied
+ */
+ public function shouldApplyCellAlignment()
+ {
+ return $this->shouldApplyCellAlignment;
+ }
+
/**
* @return bool
*/
diff --git a/src/Spout/Writer/Common/Creator/Style/StyleBuilder.php b/src/Spout/Writer/Common/Creator/Style/StyleBuilder.php
index 65792eb..bc2d406 100644
--- a/src/Spout/Writer/Common/Creator/Style/StyleBuilder.php
+++ b/src/Spout/Writer/Common/Creator/Style/StyleBuilder.php
@@ -3,7 +3,9 @@
namespace Box\Spout\Writer\Common\Creator\Style;
use Box\Spout\Common\Entity\Style\Border;
+use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Common\Entity\Style\Style;
+use Box\Spout\Common\Exception\InvalidArgumentException;
/**
* Class StyleBuilder
@@ -122,6 +124,25 @@ class StyleBuilder
return $this;
}
+ /**
+ * Sets the cell alignment.
+ *
+ * @param string $cellAlignment The cell alignment
+ *
+ * @throws InvalidArgumentException If the given cell alignment is not valid
+ * @return StyleBuilder
+ */
+ public function setCellAlignment($cellAlignment)
+ {
+ if (!CellAlignment::isValid($cellAlignment)) {
+ throw new InvalidArgumentException('Invalid cell alignment value');
+ }
+
+ $this->style->setCellAlignment($cellAlignment);
+
+ return $this;
+ }
+
/**
* Set a border
*
diff --git a/src/Spout/Writer/Common/Manager/Style/StyleMerger.php b/src/Spout/Writer/Common/Manager/Style/StyleMerger.php
index 1628783..806c8d5 100644
--- a/src/Spout/Writer/Common/Manager/Style/StyleMerger.php
+++ b/src/Spout/Writer/Common/Manager/Style/StyleMerger.php
@@ -85,6 +85,9 @@ class StyleMerger
if (!$style->hasSetWrapText() && $baseStyle->shouldWrapText()) {
$styleToUpdate->setShouldWrapText();
}
+ if (!$style->hasSetCellAlignment() && $baseStyle->shouldApplyCellAlignment()) {
+ $styleToUpdate->setCellAlignment($baseStyle->getCellAlignment());
+ }
if (!$style->getBorder() && $baseStyle->shouldApplyBorder()) {
$styleToUpdate->setBorder($baseStyle->getBorder());
}
diff --git a/src/Spout/Writer/ODS/Manager/Style/StyleManager.php b/src/Spout/Writer/ODS/Manager/Style/StyleManager.php
index 5b3bb0d..4e163eb 100644
--- a/src/Spout/Writer/ODS/Manager/Style/StyleManager.php
+++ b/src/Spout/Writer/ODS/Manager/Style/StyleManager.php
@@ -3,6 +3,7 @@
namespace Box\Spout\Writer\ODS\Manager\Style;
use Box\Spout\Common\Entity\Style\BorderPart;
+use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Writer\Common\Entity\Worksheet;
use Box\Spout\Writer\ODS\Helper\BorderHelper;
@@ -199,6 +200,7 @@ EOD;
$content = '';
$content .= $this->getTextPropertiesSectionContent($style);
+ $content .= $this->getParagraphPropertiesSectionContent($style);
$content .= $this->getTableCellPropertiesSectionContent($style);
$content .= '';
@@ -214,26 +216,26 @@ EOD;
*/
private function getTextPropertiesSectionContent($style)
{
- $content = '';
-
- if ($style->shouldApplyFont()) {
- $content .= $this->getFontSectionContent($style);
+ if (!$style->shouldApplyFont()) {
+ return '';
}
- return $content;
+ return 'getFontSectionContent($style)
+ . '/>';
}
/**
- * Returns the contents of the "" section, inside "" section
+ * Returns the contents of the fonts definition section, inside "" section
*
* @param \Box\Spout\Common\Entity\Style\Style $style
+ *
* @return string
*/
private function getFontSectionContent($style)
{
$defaultStyle = $this->getDefaultStyle();
-
- $content = 'getFontColor();
if ($fontColor !== $defaultStyle->getFontColor()) {
@@ -263,11 +265,60 @@ EOD;
$content .= ' style:text-line-through-style="solid"';
}
- $content .= '/>';
-
return $content;
}
+ /**
+ * Returns the contents of the "" section, inside "" section
+ *
+ * @param \Box\Spout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getParagraphPropertiesSectionContent($style)
+ {
+ if (!$style->shouldApplyCellAlignment()) {
+ return '';
+ }
+
+ return 'getCellAlignmentSectionContent($style)
+ . '/>';
+ }
+
+ /**
+ * Returns the contents of the cell alignment definition for the "" section
+ *
+ * @param \Box\Spout\Common\Entity\Style\Style $style
+ *
+ * @return string
+ */
+ private function getCellAlignmentSectionContent($style)
+ {
+ return sprintf(
+ ' fo:text-align="%s" ',
+ $this->transformCellAlignment($style->getCellAlignment())
+ );
+ }
+
+ /**
+ * Even though "left" and "right" alignments are part of the spec, and interpreted
+ * respectively as "start" and "end", using the recommended values increase compatibility
+ * with software that will read the created ODS file.
+ *
+ * @param string $cellAlignment
+ *
+ * @return string
+ */
+ private function transformCellAlignment($cellAlignment)
+ {
+ switch ($cellAlignment) {
+ case CellAlignment::LEFT: return 'start';
+ case CellAlignment::RIGHT: return 'end';
+ default: return $cellAlignment;
+ }
+ }
+
/**
* Returns the contents of the "" section, inside "" section
*
@@ -276,7 +327,7 @@ EOD;
*/
private function getTableCellPropertiesSectionContent($style)
{
- $content = '';
+ $content = 'shouldWrapText()) {
$content .= $this->getWrapTextXMLContent();
@@ -290,6 +341,8 @@ EOD;
$content .= $this->getBackgroundColorXMLContent($style);
}
+ $content .= '/>';
+
return $content;
}
@@ -300,7 +353,7 @@ EOD;
*/
private function getWrapTextXMLContent()
{
- return '';
+ return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
}
/**
@@ -311,13 +364,11 @@ EOD;
*/
private function getBorderXMLContent($style)
{
- $borderProperty = '';
-
$borders = array_map(function (BorderPart $borderPart) {
return BorderHelper::serializeBorderPart($borderPart);
}, $style->getBorder()->getParts());
- return sprintf($borderProperty, implode(' ', $borders));
+ return sprintf(' %s ', implode(' ', $borders));
}
/**
@@ -328,9 +379,6 @@ EOD;
*/
private function getBackgroundColorXMLContent($style)
{
- return sprintf(
- '',
- $style->getBackgroundColor()
- );
+ return sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
}
}
diff --git a/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php b/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php
index b7491a9..5eaa606 100644
--- a/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php
+++ b/src/Spout/Writer/XLSX/Manager/Style/StyleManager.php
@@ -249,9 +249,16 @@ EOD;
$content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
- if ($style->shouldWrapText()) {
+ if ($style->shouldApplyCellAlignment() || $style->shouldWrapText()) {
$content .= ' applyAlignment="1">';
- $content .= '';
+ $content .= 'shouldApplyCellAlignment()) {
+ $content .= sprintf(' horizontal="%s"', $style->getCellAlignment());
+ }
+ if ($style->shouldWrapText()) {
+ $content .= ' wrapText="1"';
+ }
+ $content .= '/>';
$content .= '';
} else {
$content .= '/>';
diff --git a/tests/Spout/Writer/Common/Creator/StyleBuilderTest.php b/tests/Spout/Writer/Common/Creator/StyleBuilderTest.php
index 7790928..e1eb00f 100644
--- a/tests/Spout/Writer/Common/Creator/StyleBuilderTest.php
+++ b/tests/Spout/Writer/Common/Creator/StyleBuilderTest.php
@@ -3,7 +3,9 @@
namespace Box\Spout\Writer\Common\Creator\Style;
use Box\Spout\Common\Entity\Style\Border;
+use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Common\Entity\Style\Color;
+use Box\Spout\Common\Exception\InvalidArgumentException;
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
use PHPUnit\Framework\TestCase;
@@ -45,20 +47,18 @@ class StyleBuilderTest extends TestCase
/**
* @return void
*/
- public function testStyleBuilderShouldMergeFormats()
+ public function testStyleBuilderShouldApplyCellAlignment()
{
- $baseStyle = (new StyleBuilder())
- ->setFontBold()
- ->setFormat('0.00')
- ->build();
+ $style = (new StyleBuilder())->setCellAlignment(CellAlignment::CENTER)->build();
+ $this->assertTrue($style->shouldApplyCellAlignment());
+ }
- $currentStyle = (new StyleBuilder())->build();
-
- $styleMerger = new StyleMerger();
- $mergedStyle = $styleMerger->merge($currentStyle, $baseStyle);
-
- $this->assertNull($currentStyle->getFormat(), 'Current style has no border');
- $this->assertEquals('0.00', $baseStyle->getFormat(), 'Base style has a format 0.00');
- $this->assertEquals('0.00', $mergedStyle->getFormat(), 'Merged style has a format 0.00');
+ /**
+ * @return void
+ */
+ public function testStyleBuilderShouldThrowOnInvalidCellAlignment()
+ {
+ $this->expectException(InvalidArgumentException::class);
+ (new StyleBuilder())->setCellAlignment('invalid_cell_alignment')->build();
}
}
diff --git a/tests/Spout/Writer/Common/Manager/Style/StyleMergerTest.php b/tests/Spout/Writer/Common/Manager/Style/StyleMergerTest.php
index 125d676..46820c0 100644
--- a/tests/Spout/Writer/Common/Manager/Style/StyleMergerTest.php
+++ b/tests/Spout/Writer/Common/Manager/Style/StyleMergerTest.php
@@ -45,6 +45,7 @@ class StyleMergerTest extends TestCase
->setFontBold()
->setFontColor(Color::YELLOW)
->setBackgroundColor(Color::BLUE)
+ ->setFormat('0.00')
->build();
$currentStyle = (new StyleBuilder())->setFontName('Font')->setFontUnderline()->build();
$mergedStyle = $this->styleMerger->merge($currentStyle, $baseStyle);
@@ -60,6 +61,7 @@ class StyleMergerTest extends TestCase
$this->assertTrue($mergedStyle->isFontUnderline());
$this->assertEquals(Color::YELLOW, $mergedStyle->getFontColor());
$this->assertEquals(Color::BLUE, $mergedStyle->getBackgroundColor());
+ $this->assertEquals('0.00', $mergedStyle->getFormat());
}
/**
diff --git a/tests/Spout/Writer/ODS/WriterWithStyleTest.php b/tests/Spout/Writer/ODS/WriterWithStyleTest.php
index 5afd73b..dcdd364 100644
--- a/tests/Spout/Writer/ODS/WriterWithStyleTest.php
+++ b/tests/Spout/Writer/ODS/WriterWithStyleTest.php
@@ -4,6 +4,7 @@ namespace Box\Spout\Writer\ODS;
use Box\Spout\Common\Entity\Row;
use Box\Spout\Common\Entity\Style\Border;
+use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Common\Entity\Style\Color;
use Box\Spout\Common\Entity\Style\Style;
use Box\Spout\Reader\Wrapper\XMLReader;
@@ -204,6 +205,25 @@ class WriterWithStyleTest extends TestCase
$this->assertFirstChildHasAttributeEquals('wrap', $customStyleElement, 'table-cell-properties', 'fo:wrap-option');
}
+ /**
+ * @return void
+ */
+ public function testAddRowShouldApplyCellAlignment()
+ {
+ $fileName = 'test_add_row_should_apply_cell_alignment.xlsx';
+
+ $rightAlignedStyle = (new StyleBuilder())->setCellAlignment(CellAlignment::RIGHT)->build();
+ $dataRows = $this->createStyledRowsFromValues([['ods--11']], $rightAlignedStyle);
+
+ $this->writeToODSFile($dataRows, $fileName);
+
+ $styleElements = $this->getCellStyleElementsFromContentXmlFile($fileName);
+ $this->assertCount(2, $styleElements, 'There should be 2 styles (default and custom)');
+
+ $customStyleElement = $styleElements[1];
+ $this->assertFirstChildHasAttributeEquals('end', $customStyleElement, 'paragraph-properties', 'fo:text-align');
+ }
+
/**
* @return void
*/
diff --git a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php
index d556623..da0aec2 100644
--- a/tests/Spout/Writer/XLSX/WriterWithStyleTest.php
+++ b/tests/Spout/Writer/XLSX/WriterWithStyleTest.php
@@ -4,6 +4,7 @@ namespace Box\Spout\Writer\XLSX;
use Box\Spout\Common\Entity\Row;
use Box\Spout\Common\Entity\Style\Border;
+use Box\Spout\Common\Entity\Style\CellAlignment;
use Box\Spout\Common\Entity\Style\Color;
use Box\Spout\Common\Entity\Style\Style;
use Box\Spout\Reader\Wrapper\XMLReader;
@@ -287,6 +288,24 @@ class WriterWithStyleTest extends TestCase
$this->assertFirstChildHasAttributeEquals('1', $xfElement, 'alignment', 'wrapText');
}
+ /**
+ * @return void
+ */
+ public function testAddRowShouldApplyCellAlignment()
+ {
+ $fileName = 'test_add_row_should_apply_cell_alignment.xlsx';
+
+ $rightAlignedStyle = (new StyleBuilder())->setCellAlignment(CellAlignment::RIGHT)->build();
+ $dataRows = $this->createStyledRowsFromValues([['xlsx--11']], $rightAlignedStyle);
+
+ $this->writeToXLSXFile($dataRows, $fileName);
+
+ $cellXfsDomElement = $this->getXmlSectionFromStylesXmlFile($fileName, 'cellXfs');
+ $xfElement = $cellXfsDomElement->getElementsByTagName('xf')->item(1);
+ $this->assertEquals(1, $xfElement->getAttribute('applyAlignment'));
+ $this->assertFirstChildHasAttributeEquals(CellAlignment::RIGHT, $xfElement, 'alignment', 'horizontal');
+ }
+
/**
* @return void
*/