Merge 887d6ef0339aaa04fc15691e3890f8234ccfb8c2 into 84596668410bea89d21aa9867b91e1550e359329

This commit is contained in:
Jonny Nott 2022-05-26 15:55:27 +02:00 committed by GitHub
commit bc54327cf4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 229 additions and 24 deletions

View File

@ -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();

View File

@ -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

View 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]);
}
}

View File

@ -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
*/

View File

@ -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
*

View File

@ -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());
}

View File

@ -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" ';
}
/**

View File

@ -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>';

View File

@ -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();
}
}

View File

@ -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
*/

View File

@ -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
*/