Skip to content
This repository was archived by the owner on May 26, 2022. It is now read-only.

Commit 9f4c094

Browse files
committed
Cell alignment
This PR adds support for cell alignment for XLSX and ODS files. You can now align the content of the cells this way: ``` use Box\Spout\Common\Entity\Style\CellAlignment; use Box\Spout\Writer\Common\Creator\Style\StyleBuilder; $style = (new StyleBuilder()) ->setCellAlignment(CellAlignment::RIGHT) ->build(); ... ``` Possible cell alignments are: LEFT, RIGHT, CENTER and JUSTIFY.
1 parent 0a0b1f7 commit 9f4c094

File tree

12 files changed

+246
-46
lines changed

12 files changed

+246
-46
lines changed

docs/_pages/documentation.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,17 @@ $reader->setShouldPreserveEmptyRows(true);
116116

117117
For fonts and alignments, {{ site.spout_html }} does not support all the possible formatting options yet. But you can find the most important ones:
118118

119-
| Category | Property | API
120-
|:----------|:--------------|:--------------------------------------
121-
| Font | Bold | `StyleBuilder::setFontBold()`
122-
| | Italic | `StyleBuilder::setFontItalic()`
123-
| | Underline | `StyleBuilder::setFontUnderline()`
124-
| | Strikethrough | `StyleBuilder::setFontStrikethrough()`
125-
| | Font name | `StyleBuilder::setFontName('Arial')`
126-
| | Font size | `StyleBuilder::setFontSize(14)`
127-
| | Font color | `StyleBuilder::setFontColor(Color::BLUE)`<br>`StyleBuilder::setFontColor(Color::rgb(0, 128, 255))`
128-
| Alignment | Wrap text | `StyleBuilder::setShouldWrapText(true|false)`
119+
| Category | Property | API
120+
|:----------|:---------------|:--------------------------------------
121+
| Font | Bold | `StyleBuilder::setFontBold()`
122+
| | Italic | `StyleBuilder::setFontItalic()`
123+
| | Underline | `StyleBuilder::setFontUnderline()`
124+
| | Strikethrough | `StyleBuilder::setFontStrikethrough()`
125+
| | Font name | `StyleBuilder::setFontName('Arial')`
126+
| | Font size | `StyleBuilder::setFontSize(14)`
127+
| | Font color | `StyleBuilder::setFontColor(Color::BLUE)`<br>`StyleBuilder::setFontColor(Color::rgb(0, 128, 255))`
128+
| Alignment | Cell alignment | `StyleBuilder::setCellAlignment(CellAlignment::CENTER)`
129+
| | Wrap text | `StyleBuilder::setShouldWrapText(true)`
129130

130131

131132
### Styling rows
@@ -135,6 +136,7 @@ It is possible to apply some formatting options to a row. In this case, all cell
135136
```php
136137
use Box\Spout\Writer\Common\Creator\WriterEntityFactory;
137138
use Box\Spout\Writer\Common\Creator\Style\StyleBuilder;
139+
use Box\Spout\Common\Entity\Style\CellAlignment;
138140
use Box\Spout\Common\Entity\Style\Color;
139141

140142
$writer = WriterEntityFactory::createXLSXWriter();
@@ -146,6 +148,7 @@ $style = (new StyleBuilder())
146148
->setFontSize(15)
147149
->setFontColor(Color::BLUE)
148150
->setShouldWrapText()
151+
->setCellAlignment(CellAlignment::RIGHT)
149152
->setBackgroundColor(Color::YELLOW)
150153
->build();
151154

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
namespace Box\Spout\Common\Entity\Style;
4+
5+
/**
6+
* Class Alignment
7+
* This class provides constants to work with text alignment.
8+
*/
9+
abstract class CellAlignment
10+
{
11+
const LEFT = 'left';
12+
const RIGHT = 'right';
13+
const CENTER = 'center';
14+
const JUSTIFY = 'justify';
15+
16+
private static $VALID_ALIGNMENTS = [
17+
self::LEFT => 1,
18+
self::RIGHT => 1,
19+
self::CENTER => 1,
20+
self::JUSTIFY => 1,
21+
];
22+
23+
/**
24+
* @param string $cellAlignment
25+
*
26+
* @return bool Whether the given cell alignment is valid
27+
*/
28+
public static function isValid($cellAlignment)
29+
{
30+
return isset(self::$VALID_ALIGNMENTS[$cellAlignment]);
31+
}
32+
}

src/Spout/Common/Entity/Style/Color.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Class Color
99
* This class provides constants and functions to work with colors
1010
*/
11-
class Color
11+
abstract class Color
1212
{
1313
/** Standard colors - based on Office Online */
1414
const BLACK = '000000';

src/Spout/Common/Entity/Style/Style.php

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
class Style
1010
{
11-
/** Default font values */
11+
/** Default values */
1212
const DEFAULT_FONT_SIZE = 11;
1313
const DEFAULT_FONT_COLOR = Color::BLACK;
1414
const DEFAULT_FONT_NAME = 'Arial';
@@ -54,6 +54,13 @@ class Style
5454
/** @var bool Whether specific font properties should be applied */
5555
private $shouldApplyFont = false;
5656

57+
/** @var bool Whether specific cell alignment should be applied */
58+
private $shouldApplyCellAlignment = false;
59+
/** @var string Cell alignment */
60+
private $cellAlignment;
61+
/** @var bool Whether the cell alignment property was set */
62+
private $hasSetCellAlignment = false;
63+
5764
/** @var bool Whether the text should wrap in the cell (useful for long or multi-lines text) */
5865
private $shouldWrapText = false;
5966
/** @var bool Whether the wrap text property was set */
@@ -325,6 +332,44 @@ public function hasSetFontName()
325332
return $this->hasSetFontName;
326333
}
327334

335+
/**
336+
* @return string
337+
*/
338+
public function getCellAlignment()
339+
{
340+
return $this->cellAlignment;
341+
}
342+
343+
/**
344+
* @param string $cellAlignment The cell alignment
345+
*
346+
* @return Style
347+
*/
348+
public function setCellAlignment($cellAlignment)
349+
{
350+
$this->cellAlignment = $cellAlignment;
351+
$this->hasSetCellAlignment = true;
352+
$this->shouldApplyCellAlignment = true;
353+
354+
return $this;
355+
}
356+
357+
/**
358+
* @return bool
359+
*/
360+
public function hasSetCellAlignment()
361+
{
362+
return $this->hasSetCellAlignment;
363+
}
364+
365+
/**
366+
* @return bool Whether specific cell alignment should be applied
367+
*/
368+
public function shouldApplyCellAlignment()
369+
{
370+
return $this->shouldApplyCellAlignment;
371+
}
372+
328373
/**
329374
* @return bool
330375
*/

src/Spout/Writer/Common/Creator/Style/StyleBuilder.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace Box\Spout\Writer\Common\Creator\Style;
44

55
use Box\Spout\Common\Entity\Style\Border;
6+
use Box\Spout\Common\Entity\Style\CellAlignment;
67
use Box\Spout\Common\Entity\Style\Style;
8+
use Box\Spout\Common\Exception\InvalidArgumentException;
79

810
/**
911
* Class StyleBuilder
@@ -122,6 +124,25 @@ public function setShouldWrapText($shouldWrap = true)
122124
return $this;
123125
}
124126

127+
/**
128+
* Sets the cell alignment.
129+
*
130+
* @param string $cellAlignment The cell alignment
131+
*
132+
* @throws InvalidArgumentException If the given cell alignment is not valid
133+
* @return StyleBuilder
134+
*/
135+
public function setCellAlignment($cellAlignment)
136+
{
137+
if (!CellAlignment::isValid($cellAlignment)) {
138+
throw new InvalidArgumentException('Invalid cell alignment value');
139+
}
140+
141+
$this->style->setCellAlignment($cellAlignment);
142+
143+
return $this;
144+
}
145+
125146
/**
126147
* Set a border
127148
*

src/Spout/Writer/Common/Manager/Style/StyleMerger.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ private function mergeCellProperties(Style $styleToUpdate, Style $style, Style $
8585
if (!$style->hasSetWrapText() && $baseStyle->shouldWrapText()) {
8686
$styleToUpdate->setShouldWrapText();
8787
}
88+
if (!$style->hasSetCellAlignment() && $baseStyle->shouldApplyCellAlignment()) {
89+
$styleToUpdate->setCellAlignment($baseStyle->getCellAlignment());
90+
}
8891
if (!$style->getBorder() && $baseStyle->shouldApplyBorder()) {
8992
$styleToUpdate->setBorder($baseStyle->getBorder());
9093
}

src/Spout/Writer/ODS/Manager/Style/StyleManager.php

Lines changed: 67 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Box\Spout\Writer\ODS\Manager\Style;
44

55
use Box\Spout\Common\Entity\Style\BorderPart;
6+
use Box\Spout\Common\Entity\Style\CellAlignment;
67
use Box\Spout\Writer\Common\Entity\Worksheet;
78
use Box\Spout\Writer\ODS\Helper\BorderHelper;
89

@@ -199,6 +200,7 @@ protected function getStyleSectionContent($style)
199200
$content = '<style:style style:data-style-name="N0" style:family="table-cell" style:name="ce' . $styleIndex . '" style:parent-style-name="Default">';
200201

201202
$content .= $this->getTextPropertiesSectionContent($style);
203+
$content .= $this->getParagraphPropertiesSectionContent($style);
202204
$content .= $this->getTableCellPropertiesSectionContent($style);
203205

204206
$content .= '</style:style>';
@@ -214,26 +216,26 @@ protected function getStyleSectionContent($style)
214216
*/
215217
private function getTextPropertiesSectionContent($style)
216218
{
217-
$content = '';
218-
219-
if ($style->shouldApplyFont()) {
220-
$content .= $this->getFontSectionContent($style);
219+
if (!$style->shouldApplyFont()) {
220+
return '';
221221
}
222222

223-
return $content;
223+
return '<style:text-properties '
224+
. $this->getFontSectionContent($style)
225+
. '/>';
224226
}
225227

226228
/**
227-
* Returns the contents of the "<style:text-properties>" section, inside "<style:style>" section
229+
* Returns the contents of the fonts definition section, inside "<style:text-properties>" section
228230
*
229231
* @param \Box\Spout\Common\Entity\Style\Style $style
232+
*
230233
* @return string
231234
*/
232235
private function getFontSectionContent($style)
233236
{
234237
$defaultStyle = $this->getDefaultStyle();
235-
236-
$content = '<style:text-properties';
238+
$content = '';
237239

238240
$fontColor = $style->getFontColor();
239241
if ($fontColor !== $defaultStyle->getFontColor()) {
@@ -263,11 +265,60 @@ private function getFontSectionContent($style)
263265
$content .= ' style:text-line-through-style="solid"';
264266
}
265267

266-
$content .= '/>';
267-
268268
return $content;
269269
}
270270

271+
/**
272+
* Returns the contents of the "<style:paragraph-properties>" section, inside "<style:style>" section
273+
*
274+
* @param \Box\Spout\Common\Entity\Style\Style $style
275+
*
276+
* @return string
277+
*/
278+
private function getParagraphPropertiesSectionContent($style)
279+
{
280+
if (!$style->shouldApplyCellAlignment()) {
281+
return '';
282+
}
283+
284+
return '<style:paragraph-properties '
285+
. $this->getCellAlignmentSectionContent($style)
286+
. '/>';
287+
}
288+
289+
/**
290+
* Returns the contents of the cell alignment definition for the "<style:paragraph-properties>" section
291+
*
292+
* @param \Box\Spout\Common\Entity\Style\Style $style
293+
*
294+
* @return string
295+
*/
296+
private function getCellAlignmentSectionContent($style)
297+
{
298+
return sprintf(
299+
' fo:text-align="%s" ',
300+
$this->transformCellAlignment($style->getCellAlignment())
301+
);
302+
}
303+
304+
/**
305+
* Even though "left" and "right" alignments are part of the spec, and interpreted
306+
* respectively as "start" and "end", using the recommended values increase compatibility
307+
* with software that will read the created ODS file.
308+
*
309+
* @param string $cellAlignment
310+
*
311+
* @return string
312+
*/
313+
private function transformCellAlignment($cellAlignment)
314+
{
315+
switch ($cellAlignment) {
316+
case CellAlignment::LEFT: return 'start';
317+
case CellAlignment::RIGHT: return 'end';
318+
default: return $cellAlignment;
319+
}
320+
}
321+
271322
/**
272323
* Returns the contents of the "<style:table-cell-properties>" section, inside "<style:style>" section
273324
*
@@ -276,7 +327,7 @@ private function getFontSectionContent($style)
276327
*/
277328
private function getTableCellPropertiesSectionContent($style)
278329
{
279-
$content = '';
330+
$content = '<style:table-cell-properties ';
280331

281332
if ($style->shouldWrapText()) {
282333
$content .= $this->getWrapTextXMLContent();
@@ -290,6 +341,8 @@ private function getTableCellPropertiesSectionContent($style)
290341
$content .= $this->getBackgroundColorXMLContent($style);
291342
}
292343

344+
$content .= '/>';
345+
293346
return $content;
294347
}
295348

@@ -300,7 +353,7 @@ private function getTableCellPropertiesSectionContent($style)
300353
*/
301354
private function getWrapTextXMLContent()
302355
{
303-
return '<style:table-cell-properties fo:wrap-option="wrap" style:vertical-align="automatic"/>';
356+
return ' fo:wrap-option="wrap" style:vertical-align="automatic" ';
304357
}
305358

306359
/**
@@ -311,13 +364,11 @@ private function getWrapTextXMLContent()
311364
*/
312365
private function getBorderXMLContent($style)
313366
{
314-
$borderProperty = '<style:table-cell-properties %s />';
315-
316367
$borders = array_map(function (BorderPart $borderPart) {
317368
return BorderHelper::serializeBorderPart($borderPart);
318369
}, $style->getBorder()->getParts());
319370

320-
return sprintf($borderProperty, implode(' ', $borders));
371+
return sprintf(' %s ', implode(' ', $borders));
321372
}
322373

323374
/**
@@ -328,9 +379,6 @@ private function getBorderXMLContent($style)
328379
*/
329380
private function getBackgroundColorXMLContent($style)
330381
{
331-
return sprintf(
332-
'<style:table-cell-properties fo:background-color="#%s"/>',
333-
$style->getBackgroundColor()
334-
);
382+
return sprintf(' fo:background-color="#%s" ', $style->getBackgroundColor());
335383
}
336384
}

src/Spout/Writer/XLSX/Manager/Style/StyleManager.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,9 +249,16 @@ protected function getCellXfsSectionContent()
249249

250250
$content .= sprintf(' applyBorder="%d"', $style->shouldApplyBorder() ? 1 : 0);
251251

252-
if ($style->shouldWrapText()) {
252+
if ($style->shouldApplyCellAlignment() || $style->shouldWrapText()) {
253253
$content .= ' applyAlignment="1">';
254-
$content .= '<alignment wrapText="1"/>';
254+
$content .= '<alignment';
255+
if ($style->shouldApplyCellAlignment()) {
256+
$content .= sprintf(' horizontal="%s"', $style->getCellAlignment());
257+
}
258+
if ($style->shouldWrapText()) {
259+
$content .= ' wrapText="1"';
260+
}
261+
$content .= '/>';
255262
$content .= '</xf>';
256263
} else {
257264
$content .= '/>';

0 commit comments

Comments
 (0)