Add setCellVerticalAlignment() & allow setShouldWrapText() to be explicitly set as false
- add Common\Entity\Style\CellVerticalAlignment - duplicate get/set/hasSet/shouldApply methods for CellAlignment in Common\Entity\Style\Style for CellVerticalAlignment instead, plus corresponding properties - add setCellVerticalAlignment method to Common\Creator\Style\StyleBuilder - add vertical alignment to StyleMerger:: mergeCellProperties() - adjust wrapText logic in mergeCellProperties() to fix issue https://github.com/box/spout/issues/829 - apply vertical cell styling for both XLSX and ODS, via corresponding StyleManager classes - transform vertical alignment ‘center’ to ‘middle’ for ODS - fix logic around wrapText such that the choice whether to include wrapping styles depends on hasSetWrapText() being true, and then use shouldWrapText() thereafter to either set wrapping or no wrapping (for XLSX, wrapText=“1” or wrapText=“0”, for ODS, wrap-option=wrap or wrap-option=no-wrap). previously there was no way to set wrapping to be OFF, only to set it to be ON. - add new tests to ensure shouldWrapText(false) results in the correct negated wrapText (XLSX) / wrap-option (ODS) styles - add new tests to StyleBuilderTest for vertical alignment - add vertical alignment to documentation.md
This commit is contained in:
parent
cc42c1d29f
commit
887d6ef033
@ -117,7 +117,7 @@ $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()`
|
||||
@ -126,6 +126,7 @@ For fonts and alignments, {{ site.spout_html }} does not support all the possibl
|
||||
| | Font size | `StyleBuilder::setFontSize(14)`
|
||||
| | Font color | `StyleBuilder::setFontColor(Color::BLUE)`<br>`StyleBuilder::setFontColor(Color::rgb(0, 128, 255))`
|
||||
| Alignment | Cell alignment | `StyleBuilder::setCellAlignment(CellAlignment::CENTER)`
|
||||
| | Cell vertical alignment | `StyleBuilder::setCellVerticalAlignment(CellVerticalAlignment::CENTER)`
|
||||
| | Wrap text | `StyleBuilder::setShouldWrapText(true)`
|
||||
| Format _(XLSX only)_ | Number format | `StyleBuilder::setFormat('0.000')`
|
||||
| | Date format | `StyleBuilder::setFormat('m/d/yy h:mm')`
|
||||
@ -150,6 +151,7 @@ $style = (new StyleBuilder())
|
||||
->setFontColor(Color::BLUE)
|
||||
->setShouldWrapText()
|
||||
->setCellAlignment(CellAlignment::RIGHT)
|
||||
->setCellVerticalAlignment(CellVerticalAlignment::BOTTOM)
|
||||
->setBackgroundColor(Color::YELLOW)
|
||||
->build();
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
namespace Box\Spout\Common\Entity\Style;
|
||||
|
||||
/**
|
||||
* Class Alignment
|
||||
* Class CellAlignment
|
||||
* This class provides constants to work with text alignment.
|
||||
*/
|
||||
abstract class CellAlignment
|
||||
|
38
src/Spout/Common/Entity/Style/CellVerticalAlignment.php
Normal file
38
src/Spout/Common/Entity/Style/CellVerticalAlignment.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Common\Entity\Style;
|
||||
|
||||
/**
|
||||
* Class CellVerticalAlignment
|
||||
* This class provides constants to work with text vertical alignment.
|
||||
*/
|
||||
abstract class CellVerticalAlignment
|
||||
{
|
||||
public const AUTO = 'auto';
|
||||
public const BASELINE = 'baseline';
|
||||
public const BOTTOM = 'bottom';
|
||||
public const CENTER = 'center';
|
||||
public const DISTRIBUTED = 'distributed';
|
||||
public const JUSTIFY = 'justify';
|
||||
public const TOP = 'top';
|
||||
|
||||
private static $VALID_ALIGNMENTS = [
|
||||
self::AUTO => 1,
|
||||
self::BASELINE => 1,
|
||||
self::BOTTOM => 1,
|
||||
self::CENTER => 1,
|
||||
self::DISTRIBUTED => 1,
|
||||
self::JUSTIFY => 1,
|
||||
self::TOP => 1,
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $cellVerticalAlignment
|
||||
*
|
||||
* @return bool Whether the given cell vertical alignment is valid
|
||||
*/
|
||||
public static function isValid($cellVerticalAlignment)
|
||||
{
|
||||
return isset(self::$VALID_ALIGNMENTS[$cellVerticalAlignment]);
|
||||
}
|
||||
}
|
@ -61,6 +61,13 @@ class Style
|
||||
/** @var bool Whether the cell alignment property was set */
|
||||
private $hasSetCellAlignment = false;
|
||||
|
||||
/** @var bool Whether specific cell vertical alignment should be applied */
|
||||
private $shouldApplyCellVerticalAlignment = false;
|
||||
/** @var string Cell vertical alignment */
|
||||
private $cellVerticalAlignment;
|
||||
/** @var bool Whether the cell vertical alignment property was set */
|
||||
private $hasSetCellVerticalAlignment = 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 */
|
||||
@ -354,6 +361,14 @@ class Style
|
||||
return $this->cellAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getCellVerticalAlignment()
|
||||
{
|
||||
return $this->cellVerticalAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cellAlignment The cell alignment
|
||||
*
|
||||
@ -369,6 +384,21 @@ class Style
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cellVerticalAlignment The cell vertical alignment
|
||||
*
|
||||
* @return Style
|
||||
*/
|
||||
public function setCellVerticalAlignment($cellVerticalAlignment)
|
||||
{
|
||||
$this->cellVerticalAlignment = $cellVerticalAlignment;
|
||||
$this->hasSetCellVerticalAlignment = true;
|
||||
$this->shouldApplyCellVerticalAlignment = true;
|
||||
$this->isEmpty = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
@ -377,6 +407,14 @@ class Style
|
||||
return $this->hasSetCellAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function hasSetCellVerticalAlignment()
|
||||
{
|
||||
return $this->hasSetCellVerticalAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether specific cell alignment should be applied
|
||||
*/
|
||||
@ -385,6 +423,14 @@ class Style
|
||||
return $this->shouldApplyCellAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool Whether specific cell alignment should be applied
|
||||
*/
|
||||
public function shouldApplyCellVerticalAlignment()
|
||||
{
|
||||
return $this->shouldApplyCellVerticalAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
@ -4,6 +4,7 @@ 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\CellVerticalAlignment;
|
||||
use Box\Spout\Common\Entity\Style\Style;
|
||||
use Box\Spout\Common\Exception\InvalidArgumentException;
|
||||
|
||||
@ -143,6 +144,25 @@ class StyleBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cell vertical alignment.
|
||||
*
|
||||
* @param string $cellVerticalAlignment The cell vertical alignment
|
||||
*
|
||||
* @throws InvalidArgumentException If the given cell vertical alignment is not valid
|
||||
* @return StyleBuilder
|
||||
*/
|
||||
public function setCellVerticalAlignment($cellVerticalAlignment)
|
||||
{
|
||||
if (!CellVerticalAlignment::isValid($cellVerticalAlignment)) {
|
||||
throw new InvalidArgumentException('Invalid cell vertical alignment value');
|
||||
}
|
||||
|
||||
$this->style->setCellVerticalAlignment($cellVerticalAlignment);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a border
|
||||
*
|
||||
|
@ -82,12 +82,15 @@ class StyleMerger
|
||||
*/
|
||||
private function mergeCellProperties(Style $styleToUpdate, Style $style, Style $baseStyle)
|
||||
{
|
||||
if (!$style->hasSetWrapText() && $baseStyle->shouldWrapText()) {
|
||||
$styleToUpdate->setShouldWrapText();
|
||||
if (!$style->hasSetWrapText() && $baseStyle->hasSetWrapText()) {
|
||||
$styleToUpdate->setShouldWrapText($baseStyle->shouldWrapText());
|
||||
}
|
||||
if (!$style->hasSetCellAlignment() && $baseStyle->shouldApplyCellAlignment()) {
|
||||
$styleToUpdate->setCellAlignment($baseStyle->getCellAlignment());
|
||||
}
|
||||
if (!$style->hasSetCellVerticalAlignment() && $baseStyle->shouldApplyCellVerticalAlignment()) {
|
||||
$styleToUpdate->setCellVerticalAlignment($baseStyle->getCellVerticalAlignment());
|
||||
}
|
||||
if ($style->getBorder() === null && $baseStyle->shouldApplyBorder()) {
|
||||
$styleToUpdate->setBorder($baseStyle->getBorder());
|
||||
}
|
||||
|
@ -4,6 +4,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\Common\Entity\Style\CellVerticalAlignment;
|
||||
use Box\Spout\Writer\Common\Entity\Worksheet;
|
||||
use Box\Spout\Writer\ODS\Helper\BorderHelper;
|
||||
|
||||
@ -277,12 +278,13 @@ EOD;
|
||||
*/
|
||||
private function getParagraphPropertiesSectionContent($style)
|
||||
{
|
||||
if (!$style->shouldApplyCellAlignment()) {
|
||||
if (!$style->shouldApplyCellAlignment() && !$style->shouldApplyCellVerticalAlignment()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return '<style:paragraph-properties '
|
||||
. $this->getCellAlignmentSectionContent($style)
|
||||
. $this->getCellVerticalAlignmentSectionContent($style)
|
||||
. '/>';
|
||||
}
|
||||
|
||||
@ -301,6 +303,21 @@ EOD;
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the cell vertical alignment definition for the "<style:paragraph-properties>" section
|
||||
*
|
||||
* @param \Box\Spout\Common\Entity\Style\Style $style
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function getCellVerticalAlignmentSectionContent($style)
|
||||
{
|
||||
return \sprintf(
|
||||
' fo:vertical-align="%s" ',
|
||||
$this->transformCellVerticalAlignment($style->getCellVerticalAlignment())
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Even though "left" and "right" alignments are part of the spec, and interpreted
|
||||
* respectively as "start" and "end", using the recommended values increase compatibility
|
||||
@ -319,6 +336,21 @@ EOD;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spec uses 'middle' rather than 'center'
|
||||
* http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1420236_253892949
|
||||
*
|
||||
* @param string $cellAlignment
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
private function transformCellVerticalAlignment($cellVerticalAlignment)
|
||||
{
|
||||
return ($cellVerticalAlignment === CellVerticalAlignment::CENTER)
|
||||
? 'middle'
|
||||
: $cellVerticalAlignment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the contents of the "<style:table-cell-properties>" section, inside "<style:style>" section
|
||||
*
|
||||
@ -329,8 +361,8 @@ EOD;
|
||||
{
|
||||
$content = '<style:table-cell-properties ';
|
||||
|
||||
if ($style->shouldWrapText()) {
|
||||
$content .= $this->getWrapTextXMLContent();
|
||||
if ($style->hasSetWrapText()) {
|
||||
$content .= $this->getWrapTextXMLContent($style->shouldWrapText());
|
||||
}
|
||||
|
||||
if ($style->shouldApplyBorder()) {
|
||||
@ -349,11 +381,12 @@ EOD;
|
||||
/**
|
||||
* Returns the contents of the wrap text definition for the "<style:table-cell-properties>" section
|
||||
*
|
||||
* @param boolean $shouldWrapText
|
||||
* @return string
|
||||
*/
|
||||
private function getWrapTextXMLContent()
|
||||
private function getWrapTextXMLContent($shouldWrapText)
|
||||
{
|
||||
return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
|
||||
return ' fo:wrap-option="' . ($shouldWrapText ? '' : 'no-') . 'wrap" style:vertical-align="automatic" ';
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -250,14 +250,17 @@ EOD;
|
||||
|
||||
$content .= \sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
|
||||
|
||||
if ($style->shouldApplyCellAlignment() || $style->shouldWrapText()) {
|
||||
if ($style->shouldApplyCellAlignment() || $style->shouldApplyCellVerticalAlignment() || $style->hasSetWrapText()) {
|
||||
$content .= ' applyAlignment="1">';
|
||||
$content .= '<alignment';
|
||||
if ($style->shouldApplyCellAlignment()) {
|
||||
$content .= \sprintf(' horizontal="%s"', $style->getCellAlignment());
|
||||
}
|
||||
if ($style->shouldWrapText()) {
|
||||
$content .= ' wrapText="1"';
|
||||
if ($style->shouldApplyCellVerticalAlignment()) {
|
||||
$content .= \sprintf(' vertical="%s"', $style->getCellVerticalAlignment());
|
||||
}
|
||||
if ($style->hasSetWrapText()) {
|
||||
$content .= ' wrapText="' . ($style->shouldWrapText() ? '1' : '0') . '"';
|
||||
}
|
||||
$content .= '/>';
|
||||
$content .= '</xf>';
|
||||
|
@ -4,6 +4,7 @@ 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\CellVerticalAlignment;
|
||||
use Box\Spout\Common\Entity\Style\Color;
|
||||
use Box\Spout\Common\Exception\InvalidArgumentException;
|
||||
use Box\Spout\Writer\Common\Manager\Style\StyleMerger;
|
||||
@ -53,6 +54,15 @@ class StyleBuilderTest extends TestCase
|
||||
$this->assertTrue($style->shouldApplyCellAlignment());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testStyleBuilderShouldApplyCellVerticalAlignment()
|
||||
{
|
||||
$style = (new StyleBuilder())->setCellVerticalAlignment(CellVerticalAlignment::CENTER)->build();
|
||||
$this->assertTrue($style->shouldApplyCellVerticalAlignment());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
@ -61,4 +71,13 @@ class StyleBuilderTest extends TestCase
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
(new StyleBuilder())->setCellAlignment('invalid_cell_alignment')->build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testStyleBuilderShouldThrowOnInvalidCellVerticalAlignment()
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
(new StyleBuilder())->setCellVerticalAlignment('invalid_cell_alignment')->build();
|
||||
}
|
||||
}
|
||||
|
@ -188,6 +188,27 @@ class WriterWithStyleTest extends TestCase
|
||||
$this->assertFirstChildHasAttributeEquals('wrap', $customStyleElement, 'table-cell-properties', 'fo:wrap-option');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldAddNegatedWrapTextAlignmentInfoInStylesXmlFileIfSpecified()
|
||||
{
|
||||
$fileName = 'test_add_row_should_add_negated_wrap_text_alignment.ods';
|
||||
|
||||
$style = (new StyleBuilder())->setShouldWrapText(false)->build();
|
||||
$dataRows = $this->createStyledRowsFromValues([
|
||||
['ods--11', 'ods--12'],
|
||||
], $style);
|
||||
|
||||
$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('no-wrap', $customStyleElement, 'table-cell-properties', 'fo:wrap-option');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
@ -271,6 +271,26 @@ class WriterWithStyleTest extends TestCase
|
||||
$this->assertFirstChildHasAttributeEquals('1', $xfElement, 'alignment', 'wrapText');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testAddRowShouldAddNegatedWrapTextAlignmentInfoInStylesXmlFileIfSpecified()
|
||||
{
|
||||
$fileName = 'test_add_row_should_add_negated_wrap_text_alignment.xlsx';
|
||||
|
||||
$style = (new StyleBuilder())->setShouldWrapText(false)->build();
|
||||
$dataRows = $this->createStyledRowsFromValues([
|
||||
['xlsx--11', 'xlsx--12'],
|
||||
], $style);
|
||||
|
||||
$this->writeToXLSXFile($dataRows, $fileName);
|
||||
|
||||
$cellXfsDomElement = $this->getXmlSectionFromStylesXmlFile($fileName, 'cellXfs');
|
||||
$xfElement = $cellXfsDomElement->getElementsByTagName('xf')->item(1);
|
||||
$this->assertEquals(1, $xfElement->getAttribute('applyAlignment'));
|
||||
$this->assertFirstChildHasAttributeEquals('0', $xfElement, 'alignment', 'wrapText');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user