parent
1891c0b053
commit
b2c5797c72
27
README.md
27
README.md
@ -125,7 +125,7 @@ The writer always generate CSV files encoded in UTF-8, with a BOM.
|
||||
|
||||
#### Row styling
|
||||
|
||||
It is possible to apply some formatting options to a row. Spout supports fonts as well as alignment styles.
|
||||
It is possible to apply some formatting options to a row. Spout supports fonts, borders as well as alignment styles.
|
||||
|
||||
```php
|
||||
use Box\Spout\Common\Type;
|
||||
@ -150,6 +150,31 @@ $writer->addRowsWithStyle($multipleRows, $style); // style will be applied to al
|
||||
$writer->close();
|
||||
```
|
||||
|
||||
Adding borders to a row requires a ```Border``` object.
|
||||
|
||||
```php
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\Writer\WriterFactory;
|
||||
use Box\Spout\Writer\Style\StyleBuilder;
|
||||
use Box\Spout\Writer\Style\Border;
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
|
||||
$border = new Border();
|
||||
$border
|
||||
->addPart(new BorderPart(Border::BOTTOM, Border::STYLE_SOLID, Border::WIDTH_THICK, Color::ORANGE))
|
||||
->addPart(new BorderPart(Border::RIGHT, Border::STYLE_DASHED, BOrder::WIDTH_THIN, Color::GREEN));
|
||||
|
||||
$style = (new StyleBuilder())
|
||||
->setBorder($border)
|
||||
->build();
|
||||
|
||||
$writerXlsx = WriterFactory::create(Type::XLSX);
|
||||
$writerXlsx->openToFile('borders.xlsx');
|
||||
$writerXlsx->addRowsWithStyle($multipleRows, $style);
|
||||
$writerXlsx->close();
|
||||
|
||||
```
|
||||
|
||||
Unfortunately, Spout does not support all the possible formatting options yet. But you can find the most important ones:
|
||||
|
||||
Category | Property | API
|
||||
|
18
src/Spout/Writer/Exception/InvalidBorderNameException.php
Normal file
18
src/Spout/Writer/Exception/InvalidBorderNameException.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\Exception;
|
||||
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
|
||||
class InvalidBorderNameException extends WriterException
|
||||
{
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
|
||||
$msg = '%s is not a valid name identifier for a border. Valid identifiers are: %s.';
|
||||
|
||||
parent::__construct(sprintf($msg, $name, implode(',', BorderPart::getAllowedNames())));
|
||||
|
||||
}
|
||||
}
|
17
src/Spout/Writer/Exception/InvalidBorderStyleException.php
Normal file
17
src/Spout/Writer/Exception/InvalidBorderStyleException.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\Exception;
|
||||
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
|
||||
class InvalidBorderStyleException extends WriterException
|
||||
{
|
||||
public function __construct($name)
|
||||
{
|
||||
|
||||
$msg = '%s is not a valid style identifier for a border. Valid identifiers are: %s.';
|
||||
|
||||
parent::__construct(sprintf($msg, $name, implode(',', BorderPart::getAllowedStyles())));
|
||||
|
||||
}
|
||||
}
|
17
src/Spout/Writer/Exception/InvalidBorderWidthException.php
Normal file
17
src/Spout/Writer/Exception/InvalidBorderWidthException.php
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\Exception;
|
||||
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
|
||||
class InvalidBorderWidthException extends WriterException
|
||||
{
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$msg = '%s is not a valid width identifier for a border. Valid identifiers are: %s.';
|
||||
|
||||
parent::__construct(sprintf($msg, $name, implode(',', BorderPart::getAllowedWidth())));
|
||||
|
||||
}
|
||||
}
|
65
src/Spout/Writer/ODS/Helper/BorderHelper.php
Normal file
65
src/Spout/Writer/ODS/Helper/BorderHelper.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\ODS\Helper;
|
||||
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
use Box\Spout\Writer\Style\Border;
|
||||
|
||||
/**
|
||||
* Class BorderHelper
|
||||
*
|
||||
* The fo:border, fo:border-top, fo:border-bottom, fo:border-left and fo:border-right attributes
|
||||
* specify border properties
|
||||
* http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#__RefHeading__1419780_253892949
|
||||
*
|
||||
* Example table-cell-properties
|
||||
*
|
||||
* <style:table-cell-properties
|
||||
* fo:border-bottom="0.74pt solid #ffc000" style:diagonal-bl-tr="none"
|
||||
* style:diagonal-tl-br="none" fo:border-left="none" fo:border-right="none"
|
||||
* style:rotation-align="none" fo:border-top="none"/>
|
||||
*/
|
||||
class BorderHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* ODS border attributes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $odsStyleMap = [
|
||||
Border::STYLE_SOLID.'%'.Border::WIDTH_THIN => '0.75pt solid',
|
||||
Border::STYLE_SOLID.'%'.Border::WIDTH_MEDIUM => '1.75pt solid',
|
||||
Border::STYLE_SOLID.'%'.Border::WIDTH_THICK => '2.5pt solid',
|
||||
Border::STYLE_DOTTED.'%'.Border::WIDTH_THIN => '0.75pt dotted',
|
||||
Border::STYLE_DOTTED.'%'.Border::WIDTH_MEDIUM => '1.75pt dotted',
|
||||
Border::STYLE_DOTTED.'%'.Border::WIDTH_THICK => '2.5pt dotted',
|
||||
Border::STYLE_DASHED.'%'.Border::WIDTH_THIN => '0.75pt dashed',
|
||||
Border::STYLE_DASHED.'%'.Border::WIDTH_MEDIUM => '1.75pt dashed',
|
||||
Border::STYLE_DASHED.'%'.Border::WIDTH_THICK => '2.5pt dashed',
|
||||
Border::STYLE_DOUBLE.'%'.Border::WIDTH_THIN => '0.75pt double',
|
||||
Border::STYLE_DOUBLE.'%'.Border::WIDTH_MEDIUM => '1.75pt double',
|
||||
Border::STYLE_DOUBLE.'%'.Border::WIDTH_THICK => '2.5pt double',
|
||||
Border::STYLE_NONE.'%'.Border::WIDTH_THIN => 'none',
|
||||
Border::STYLE_NONE.'%'.Border::WIDTH_MEDIUM => 'none',
|
||||
Border::STYLE_NONE.'%'.Border::WIDTH_THICK => 'none',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param BorderPart $borderPart
|
||||
* @return string
|
||||
*/
|
||||
public static function serializeBorderPart(BorderPart $borderPart)
|
||||
{
|
||||
$styleDef = $borderPart->getStyle() .'%' . $borderPart->getWidth();
|
||||
$borderStyle = self::$odsStyleMap[$styleDef];
|
||||
$colorEl = ($borderPart->getColor() && $borderPart->getStyle() !== Border::STYLE_NONE)
|
||||
? '#' . $borderPart->getColor() : '';
|
||||
$partEl = sprintf(
|
||||
'fo:border-%s="%s"',
|
||||
$borderPart->getName(),
|
||||
$borderStyle . ' ' .$colorEl
|
||||
);
|
||||
return $partEl;
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace Box\Spout\Writer\ODS\Helper;
|
||||
|
||||
use Box\Spout\Writer\Common\Helper\AbstractStyleHelper;
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
|
||||
/**
|
||||
* Class StyleHelper
|
||||
@ -256,9 +257,17 @@ EOD;
|
||||
$content .= '<style:table-cell-properties fo:wrap-option="wrap" style:vertical-align="automatic"/>';
|
||||
}
|
||||
|
||||
if ($style->shouldApplyBorder()) {
|
||||
$el = '<style:table-cell-properties %s />';
|
||||
$borders = array_map(function (BorderPart $borderPart) {
|
||||
return BorderHelper::serializeBorderPart($borderPart);
|
||||
}, $style->getBorder()->getParts());
|
||||
$content .= sprintf($el, implode(' ', $borders));
|
||||
}
|
||||
|
||||
$content .= '</style:style>';
|
||||
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
}
|
||||
|
64
src/Spout/Writer/Style/Border.php
Normal file
64
src/Spout/Writer/Style/Border.php
Normal file
@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\Style;
|
||||
|
||||
class Border
|
||||
{
|
||||
const LEFT = 'left';
|
||||
const RIGHT = 'right';
|
||||
const TOP = 'top';
|
||||
const BOTTOM = 'bottom';
|
||||
|
||||
const STYLE_NONE = 'none';
|
||||
const STYLE_SOLID = 'solid';
|
||||
const STYLE_DASHED = 'dashed';
|
||||
const STYLE_DOTTED = 'dotted';
|
||||
const STYLE_DOUBLE = 'double';
|
||||
|
||||
const WIDTH_THIN = 'thin';
|
||||
const WIDTH_MEDIUM = 'medium';
|
||||
const WIDTH_THICK = 'thick';
|
||||
|
||||
/**
|
||||
* @var array A list of BorderPart objects for this border.
|
||||
*/
|
||||
protected $parts = [];
|
||||
|
||||
/**
|
||||
* @param array $borderParts
|
||||
*/
|
||||
public function __construct(array $borderParts = [])
|
||||
{
|
||||
$this->setParts($borderParts);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function getParts()
|
||||
{
|
||||
return $this->parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set BorderParts
|
||||
* @param array $parts
|
||||
*/
|
||||
public function setParts($parts)
|
||||
{
|
||||
unset($this->parts);
|
||||
foreach ($parts as $part) {
|
||||
$this->addPart($part);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BorderPart $borderPart
|
||||
* @return self
|
||||
*/
|
||||
public function addPart(BorderPart $borderPart)
|
||||
{
|
||||
$this->parts[$borderPart->getName()] = $borderPart;
|
||||
return $this;
|
||||
}
|
||||
}
|
174
src/Spout/Writer/Style/BorderPart.php
Normal file
174
src/Spout/Writer/Style/BorderPart.php
Normal file
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\Style;
|
||||
|
||||
use Box\Spout\Writer\Exception\InvalidBorderNameException;
|
||||
use Box\Spout\Writer\Exception\InvalidBorderStyleException;
|
||||
use Box\Spout\Writer\Exception\InvalidBorderWidthException;
|
||||
|
||||
class BorderPart
|
||||
{
|
||||
/**
|
||||
* @var string The style of this border part.
|
||||
*/
|
||||
protected $style;
|
||||
|
||||
/**
|
||||
* @var string The name of this border part.
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @var string The color of this border part.
|
||||
*/
|
||||
protected $color;
|
||||
|
||||
/**
|
||||
* @var string The width of this border part.
|
||||
*/
|
||||
protected $width;
|
||||
|
||||
/**
|
||||
* @var array Allowed style constants for parts.
|
||||
*/
|
||||
protected static $allowedStyles = [
|
||||
'none',
|
||||
'solid',
|
||||
'dashed',
|
||||
'dotted',
|
||||
'double'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Allowed names constants for border parts.
|
||||
*/
|
||||
protected static $allowedNames = [
|
||||
'left',
|
||||
'right',
|
||||
'top',
|
||||
'bottom',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array Allowed width constants for border parts.
|
||||
*/
|
||||
protected static $allowedWidth = [
|
||||
'thin',
|
||||
'medium',
|
||||
'thick',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param string $name @see BorderPart::allowedNames
|
||||
* @param string $style @see BorderPart::allowedStyles
|
||||
* @param string $width @see BorderPart::allowedWidth
|
||||
* @param string $color A RGB color code
|
||||
* @throws InvalidBorderNameException
|
||||
* @throws InvalidBorderStyleException
|
||||
* @throws InvalidBorderWidthException
|
||||
*/
|
||||
public function __construct($name, $style = Border::STYLE_SOLID, $width = Border::WIDTH_MEDIUM, $color = Color::BLACK)
|
||||
{
|
||||
$this->setName($name);
|
||||
$this->setStyle($style);
|
||||
$this->setWidth($width);
|
||||
$this->setColor($color);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setName($name)
|
||||
{
|
||||
if (!in_array($name, self::$allowedNames)) {
|
||||
throw new InvalidBorderNameException($name);
|
||||
}
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getStyle()
|
||||
{
|
||||
return $this->style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $style
|
||||
*/
|
||||
public function setStyle($style)
|
||||
{
|
||||
if (!in_array($style, self::$allowedStyles)) {
|
||||
throw new InvalidBorderStyleException($style);
|
||||
}
|
||||
$this->style = $style;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getColor()
|
||||
{
|
||||
return $this->color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $color
|
||||
*/
|
||||
public function setColor($color)
|
||||
{
|
||||
$this->color = $color;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getWidth()
|
||||
{
|
||||
return $this->width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $width
|
||||
*/
|
||||
public function setWidth($width)
|
||||
{
|
||||
if(!in_array($width, self::$allowedWidth)) {
|
||||
throw new InvalidBorderWidthException($width);
|
||||
}
|
||||
$this->width = $width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllowedStyles()
|
||||
{
|
||||
return self::$allowedStyles;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllowedNames()
|
||||
{
|
||||
return self::$allowedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getAllowedWidth()
|
||||
{
|
||||
return self::$allowedWidth;
|
||||
}
|
||||
}
|
@ -61,6 +61,16 @@ class Style
|
||||
/** @var bool Whether the wrap text property was set */
|
||||
protected $hasSetWrapText = false;
|
||||
|
||||
/**
|
||||
* @var Border
|
||||
*/
|
||||
protected $border = null;
|
||||
|
||||
/**
|
||||
* @var bool Whether border properties should be applied
|
||||
*/
|
||||
protected $shouldApplyBorder = false;
|
||||
|
||||
/**
|
||||
* @return int|null
|
||||
*/
|
||||
@ -79,6 +89,32 @@ class Style
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Border
|
||||
*/
|
||||
public function getBorder()
|
||||
{
|
||||
return $this->border;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Border $border
|
||||
*/
|
||||
public function setBorder(Border $border)
|
||||
{
|
||||
$this->shouldApplyBorder = true;
|
||||
$this->border = $border;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function shouldApplyBorder()
|
||||
{
|
||||
return $this->shouldApplyBorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
@ -302,6 +338,9 @@ class Style
|
||||
if (!$this->hasSetWrapText && $baseStyle->shouldWrapText()) {
|
||||
$mergedStyle->setShouldWrapText();
|
||||
}
|
||||
if (!$this->getBorder() && $baseStyle->shouldApplyBorder()) {
|
||||
$mergedStyle->setBorder($baseStyle->getBorder());
|
||||
}
|
||||
|
||||
return $mergedStyle;
|
||||
}
|
||||
|
@ -121,6 +121,18 @@ class StyleBuilder
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a border
|
||||
*
|
||||
* @param Border $border
|
||||
* @return $this
|
||||
*/
|
||||
public function setBorder(Border $border)
|
||||
{
|
||||
$this->style->setBorder($border);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the configured style. The style is cached and can be reused.
|
||||
*
|
||||
|
43
src/Spout/Writer/XLSX/Helper/BorderHelper.php
Normal file
43
src/Spout/Writer/XLSX/Helper/BorderHelper.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace Box\Spout\Writer\XLSX\Helper;
|
||||
|
||||
use Box\Spout\Writer\Style\BorderPart;
|
||||
use Box\Spout\Writer\Style\Border;
|
||||
|
||||
class BorderHelper
|
||||
{
|
||||
public static $xlsxStyleMap = [
|
||||
Border::STYLE_SOLID.'%'.Border::WIDTH_THIN => 'thin',
|
||||
Border::STYLE_SOLID.'%'.Border::WIDTH_MEDIUM => 'medium',
|
||||
Border::STYLE_SOLID.'%'.Border::WIDTH_THICK => 'thick',
|
||||
Border::STYLE_DOTTED.'%'.Border::WIDTH_THIN => 'dotted',
|
||||
Border::STYLE_DOTTED.'%'.Border::WIDTH_MEDIUM => 'dotted',
|
||||
Border::STYLE_DOTTED.'%'.Border::WIDTH_THICK => 'dotted',
|
||||
Border::STYLE_DASHED.'%'.Border::WIDTH_THIN => 'dashed',
|
||||
Border::STYLE_DASHED.'%'.Border::WIDTH_MEDIUM => 'mediumDashed',
|
||||
Border::STYLE_DASHED.'%'.Border::WIDTH_THICK => 'mediumDashed',
|
||||
Border::STYLE_DOUBLE.'%'.Border::WIDTH_THIN => 'double',
|
||||
Border::STYLE_DOUBLE.'%'.Border::WIDTH_MEDIUM => 'double',
|
||||
Border::STYLE_DOUBLE.'%'.Border::WIDTH_THICK => 'double',
|
||||
Border::STYLE_NONE.'%'.Border::WIDTH_THIN => 'none',
|
||||
Border::STYLE_NONE.'%'.Border::WIDTH_MEDIUM => 'none',
|
||||
Border::STYLE_NONE.'%'.Border::WIDTH_THICK => 'none',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param BorderPart $borderPart
|
||||
* @return string
|
||||
*/
|
||||
public static function serializeBorderPart(BorderPart $borderPart)
|
||||
{
|
||||
$styleDef = $borderPart->getStyle() .'%' . $borderPart->getWidth();
|
||||
$borderStyle = self::$xlsxStyleMap[$styleDef];
|
||||
|
||||
$colorEl = $borderPart->getColor() ? sprintf('<color rgb="%s"/>', $borderPart->getColor()) : '';
|
||||
$partEl = sprintf(
|
||||
'<%s style="%s">%s</%s>', $borderPart->getName(), $borderStyle, $colorEl, $borderPart->getName()
|
||||
);
|
||||
return $partEl.PHP_EOL;
|
||||
}
|
||||
}
|
@ -100,17 +100,23 @@ EOD;
|
||||
*/
|
||||
protected function getBordersSectionContent()
|
||||
{
|
||||
return <<<EOD
|
||||
<borders count="1">
|
||||
<border>
|
||||
<left/>
|
||||
<right/>
|
||||
<top/>
|
||||
<bottom/>
|
||||
<diagonal/>
|
||||
</border>
|
||||
</borders>
|
||||
EOD;
|
||||
$content = '<borders count="' . count($this->styleIdToStyleMappingTable) . '">';
|
||||
/** @var \Box\Spout\Writer\Style\Style $style */
|
||||
foreach ($this->getRegisteredStyles() as $style) {
|
||||
$border = $style->getBorder();
|
||||
if ($border) {
|
||||
$content .= '<border>';
|
||||
foreach ($border->getParts() as $part) {
|
||||
/** @var $part \Box\Spout\Writer\Style\BorderPart */
|
||||
$content .= BorderHelper::serializeBorderPart($part);
|
||||
}
|
||||
$content .= '</border>';
|
||||
} else {
|
||||
$content .= '<border><left/><right/><top/><bottom/><diagonal/></border>';
|
||||
}
|
||||
}
|
||||
$content .= '</borders>';
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -139,12 +145,16 @@ EOD;
|
||||
$content = '<cellXfs count="' . count($registeredStyles) . '">';
|
||||
|
||||
foreach ($registeredStyles as $style) {
|
||||
$content .= '<xf numFmtId="0" fontId="' . $style->getId() . '" fillId="0" borderId="0" xfId="0"';
|
||||
$content .= '<xf numFmtId="0" fontId="' . $style->getId() . '" fillId="0" borderId="' . $style->getId() . '" xfId="0"';
|
||||
|
||||
if ($style->shouldApplyFont()) {
|
||||
$content .= ' applyFont="1"';
|
||||
}
|
||||
|
||||
if ($style->shouldApplyBorder()) {
|
||||
$content .= ' applyBorder="1"';
|
||||
}
|
||||
|
||||
if ($style->shouldWrapText()) {
|
||||
$content .= ' applyAlignment="1">';
|
||||
$content .= '<alignment wrapText="1"/>';
|
||||
|
Loading…
x
Reference in New Issue
Block a user