From f8f98cccabf58e456730459762ffea12e46d43eb Mon Sep 17 00:00:00 2001 From: Ivan Lanin Date: Tue, 13 May 2014 01:32:44 +0700 Subject: [PATCH] #237: Ability to define table width (in percent and twip) and position --- CHANGELOG.md | 1 + samples/Sample_09_Tables.php | 7 +- src/PhpWord/Shared/Html.php | 24 ++-- src/PhpWord/Style/Alignment.php | 78 +++++++++++ src/PhpWord/Style/Paragraph.php | 43 +++--- src/PhpWord/Style/Table.php | 129 +++++++++++++++--- src/PhpWord/Writer/RTF/Element/Text.php | 22 --- src/PhpWord/Writer/RTF/Element/Title.php | 2 + src/PhpWord/Writer/RTF/Style/Font.php | 29 ---- src/PhpWord/Writer/RTF/Style/Paragraph.php | 9 +- .../Writer/Word2007/Style/Alignment.php | 44 ++++++ .../Writer/Word2007/Style/Paragraph.php | 6 +- src/PhpWord/Writer/Word2007/Style/Table.php | 56 +++++--- .../Tests/Writer/Word2007/StyleTest.php | 2 +- 14 files changed, 321 insertions(+), 131 deletions(-) create mode 100644 src/PhpWord/Style/Alignment.php create mode 100644 src/PhpWord/Writer/Word2007/Style/Alignment.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 55629257..7bcc5a2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3; new r - ListItemRun: New element that can add a list item with inline formatting like a textrun - @basjan GH-235 - Table: Ability to add table inside a cell (nested table) - @ivanlanin GH-149 - RTF: UTF8 support for RTF: Internal UTF8 text is converted to Unicode before writing - @ivanlanin GH-158 +- Table: Ability to define table width (in percent and twip) and position - @ivanlanin GH-237 ### Bugfixes diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php index 5f77f8db..6732e336 100644 --- a/samples/Sample_09_Tables.php +++ b/samples/Sample_09_Tables.php @@ -87,11 +87,12 @@ $table->addCell(null, $cellRowContinue); // 4. Nested table $section->addTextBreak(2); -$section->addText('Nested table', $header); +$section->addText('Nested table in a centered and 50% width table.', $header); -$cell = $section->addTable()->addRow()->addCell(); +$table = $section->addTable(array('width' => 50 * 50, 'unit' => 'pct', 'align' => 'center')); +$cell = $table->addRow()->addCell(); $cell->addText('This cell contains nested table.'); -$innerCell = $cell->addTable()->addRow()->addCell(); +$innerCell = $cell->addTable(array('align' => 'center'))->addRow()->addCell(); $innerCell->addText('Inside nested table'); // Save file diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index c56e418b..59795af7 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -27,9 +27,8 @@ class Html * * Note: $stylesheet parameter is removed to avoid PHPMD error for unused parameter * - * @param \PhpOffice\PhpWord\Element\AbstractElement $object Where the parts need to be added + * @param \PhpOffice\PhpWord\Element\AbstractContainer $object Where the parts need to be added * @param string $html the code to parse - * */ public static function addHtml($object, $html) { @@ -53,7 +52,6 @@ class Html * * @param \DOMNode $node Node to check on attributes and to compile a style array * @param array $style is supplied, the inline style attributes are added to the already existing style - * */ protected static function parseInlineStyle($node, $style = array()) { @@ -100,10 +98,9 @@ class Html * parse a node and add a corresponding element to the object * * @param \DOMNode $node node to parse - * @param \PhpOffice\PhpWord\Element\AbstractElement $object object to add an element corresponding with the node + * @param \PhpOffice\PhpWord\Element\AbstractContainer $object object to add an element corresponding with the node * @param array $styles Array with all styles * @param array $data Array to transport data to a next level in the DOM tree, for example level of listitems - * */ protected static function parseNode( $node, @@ -171,17 +168,26 @@ class Html case 'table': $styles['paragraphStyle'] = self::parseInlineStyle($node, $styles['paragraphStyle']); $newobject = $object->addTable(); - // if ($attributes->getNamedItem('width') !== null)$newobject->setWidth($attributes->getNamedItem('width')->value); + // if ($attributes->getNamedItem('width') !== null) { + // $newobject->setWidth($attributes->getNamedItem('width')->value); + // } break; case 'tr': + /** @var \PhpOffice\PhpWord\Element\Table $object Scrutinizer type hint */ $styles['paragraphStyle'] = self::parseInlineStyle($node, $styles['paragraphStyle']); $newobject = $object->addRow(); - // if ($attributes->getNamedItem('height') !== null)$newobject->setHeight($attributes->getNamedItem('height')->value); + // if ($attributes->getNamedItem('height') !== null) { + // $newobject->setHeight($attributes->getNamedItem('height')->value); + // } break; case 'td': + /** @var \PhpOffice\PhpWord\Element\Row $object Scrutinizer type hint */ $styles['paragraphStyle'] = self::parseInlineStyle($node, $styles['paragraphStyle']); - // if ($attributes->getNamedItem('width') !== null)$newobject=$object->addCell($width=$attributes->getNamedItem('width')->value); - // else $newobject=$object->addCell(); + // if ($attributes->getNamedItem('width') !== null) { + // $newobject=$object->addCell($width=$attributes->getNamedItem('width')->value); + // } else { + // $newobject=$object->addCell(); + // } $newobject = $object->addCell(); break; case 'ul': diff --git a/src/PhpWord/Style/Alignment.php b/src/PhpWord/Style/Alignment.php new file mode 100644 index 00000000..3beabe37 --- /dev/null +++ b/src/PhpWord/Style/Alignment.php @@ -0,0 +1,78 @@ +setStyleByArray($style); + } + + /** + * Get alignment + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Set alignment + * + * @param string $value + * @return self + */ + public function setValue($value = null) + { + if (strtolower($value) == self::ALIGN_JUSTIFY) { + $value = self::ALIGN_BOTH; + } + $enum = array(self::ALIGN_LEFT, self::ALIGN_RIGHT, self::ALIGN_CENTER, self::ALIGN_BOTH, self::ALIGN_JUSTIFY); + $this->value = $this->setEnumVal($value, $enum, $this->value); + + return $this; + } +} diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index 9867afe3..56609119 100644 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -30,15 +30,6 @@ class Paragraph extends AbstractStyle */ const LINE_HEIGHT = 240; - /** - * @const string Alignment http://www.schemacentral.com/sc/ooxml/t-w_ST_Jc.html - */ - const ALIGN_LEFT = 'left'; // Align left - const ALIGN_RIGHT = 'right'; // Align right - const ALIGN_CENTER = 'center'; // Align center - const ALIGN_BOTH = 'both'; // Align both - const ALIGN_JUSTIFY = 'justify'; // Alias for align both - /** * Aliases * @@ -46,13 +37,6 @@ class Paragraph extends AbstractStyle */ protected $aliases = array('line-height' => 'lineHeight'); - /** - * Paragraph alignment - * - * @var string - */ - private $align; - /** * Text line height * @@ -123,6 +107,21 @@ class Paragraph extends AbstractStyle */ private $spacing; + /** + * Alignment + * + * @var \PhpOffice\PhpWord\Style\Alignment + */ + private $alignment; + + /** + * Create new instance + */ + public function __construct() + { + $this->alignment = new Alignment(); + } + /** * Set Style value * @@ -142,28 +141,24 @@ class Paragraph extends AbstractStyle } /** - * Get Paragraph Alignment + * Get alignment * * @return string */ public function getAlign() { - return $this->align; + return $this->alignment->getValue(); } /** - * Set Paragraph Alignment + * Set alignment * * @param string $value * @return self */ public function setAlign($value = null) { - if (strtolower($value) == self::ALIGN_JUSTIFY) { - $value = self::ALIGN_BOTH; - } - $enum = array(self::ALIGN_LEFT, self::ALIGN_RIGHT, self::ALIGN_CENTER, self::ALIGN_BOTH, self::ALIGN_JUSTIFY); - $this->align = $this->setEnumVal($value, $enum, $this->align); + $this->alignment->setValue($value); return $this; } diff --git a/src/PhpWord/Style/Table.php b/src/PhpWord/Style/Table.php index c9850e73..c077f499 100644 --- a/src/PhpWord/Style/Table.php +++ b/src/PhpWord/Style/Table.php @@ -22,6 +22,13 @@ namespace PhpOffice\PhpWord\Style; */ class Table extends Border { + /** + * @const string Table width units http://www.schemacentral.com/sc/ooxml/t-w_ST_TblWidth.html + */ + const WIDTH_AUTO = 'auto'; // Automatically determined width + const WIDTH_PERCENT = 'pct'; // Width in fiftieths (1/50) of a percent (1% = 50 unit) + const WIDTH_TWIP = 'dxa'; // Width in twentieths (1/20) of a point (twip) + /** * Style for first row * @@ -92,15 +99,31 @@ class Table extends Border */ private $shading; + /** + * @var \PhpOffice\PhpWord\Style\Alignment Alignment + */ + private $alignment; + + /** + * @var int|float Width value + */ + private $width = 0; + + /** + * @var string Width unit + */ + private $unit = self::WIDTH_AUTO; + /** * Create new table style * - * @param mixed $styleTable - * @param mixed $styleFirstRow + * @param mixed $tableStyle + * @param mixed $firstRowStyle */ - public function __construct($styleTable = null, $styleFirstRow = null) + public function __construct($tableStyle = null, $firstRowStyle = null) { - if (!is_null($styleFirstRow) && is_array($styleFirstRow)) { + $this->alignment = new Alignment(); + if (!is_null($firstRowStyle) && is_array($firstRowStyle)) { $this->firstRow = clone $this; unset($this->firstRow->firstRow); @@ -112,11 +135,11 @@ class Table extends Border unset($this->firstRow->borderInsideVSize); unset($this->firstRow->borderInsideHColor); unset($this->firstRow->borderInsideHSize); - $this->firstRow->setStyleByArray($styleFirstRow); + $this->firstRow->setStyleByArray($firstRowStyle); } - if (!is_null($styleTable) && is_array($styleTable)) { - $this->setStyleByArray($styleTable); + if (!is_null($tableStyle) && is_array($tableStyle)) { + $this->setStyleByArray($tableStyle); } } @@ -408,6 +431,24 @@ class Table extends Border return array($this->cellMarginTop, $this->cellMarginLeft, $this->cellMarginRight, $this->cellMarginBottom); } + /** + * Has margins? + * + * @return bool + */ + public function hasMargins() + { + $hasMargins = false; + $margins = $this->getCellMargin(); + for ($i = 0; $i < count($margins); $i++) { + if (!is_null($margins[$i])) { + $hasMargins = true; + } + } + + return $hasMargins; + } + /** * Get shading * @@ -432,20 +473,72 @@ class Table extends Border } /** - * Has margins? + * Get alignment * - * @return bool + * @return string */ - public function hasMargins() + public function getAlign() { - $hasMargins = false; - $margins = $this->getCellMargin(); - for ($i = 0; $i < count($margins); $i++) { - if (!is_null($margins[$i])) { - $hasMargins = true; - } - } + return $this->alignment->getValue(); + } - return $hasMargins; + /** + * Set alignment + * + * @param string $value + * @return self + */ + public function setAlign($value = null) + { + $this->alignment->setValue($value); + + return $this; + } + + /** + * Get width + * + * @return int|float + */ + public function getWidth() + { + return $this->width; + } + + /** + * Set width + * + * @param int|float $value + * @return self + */ + public function setWidth($value = null) + { + $this->width = $this->setNumericVal($value, $this->width); + + return $this; + } + + /** + * Get width unit + * + * @return string + */ + public function getUnit() + { + return $this->unit; + } + + /** + * Set width unit + * + * @param string $value + * @return self + */ + public function setUnit($value = null) + { + $enum = array(self::WIDTH_AUTO, self::WIDTH_PERCENT, self::WIDTH_TWIP); + $this->unit = $this->setEnumVal($value, $enum, $this->unit); + + return $this; } } diff --git a/src/PhpWord/Writer/RTF/Element/Text.php b/src/PhpWord/Writer/RTF/Element/Text.php index 51b24941..ae97fd3a 100644 --- a/src/PhpWord/Writer/RTF/Element/Text.php +++ b/src/PhpWord/Writer/RTF/Element/Text.php @@ -55,10 +55,6 @@ class Text extends AbstractElement $content .= String::toUnicode($this->element->getText()); $content .= '}'; - // Remarked to test using closure {} to avoid closing tags - // @since 0.11.0 - // $content .= $this->writeFontStyleClosing($fontStyle); - if (!$this->withoutP) { $content .= '\par' . PHP_EOL; } @@ -136,24 +132,6 @@ class Text extends AbstractElement return $content; } - /** - * Write font style ending - * - * @param \PhpOffice\PhpWord\Style\Font $style - * @return string - */ - private function writeFontStyleClosing($style) - { - if (!$style instanceof FontStyle) { - return ''; - } - - $styleWriter = new FontStyleWriter($style); - $content = $styleWriter->writeClosing(); - - return $content; - } - /** * Get font style * diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index 2be72ff8..d7db8f2d 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Writer\RTF\Element; +use PhpOffice\PhpWord\Shared\String; + /** * TextBreak element RTF writer * diff --git a/src/PhpWord/Writer/RTF/Style/Font.php b/src/PhpWord/Writer/RTF/Style/Font.php index c20558db..d14ee9c5 100644 --- a/src/PhpWord/Writer/RTF/Style/Font.php +++ b/src/PhpWord/Writer/RTF/Style/Font.php @@ -66,35 +66,6 @@ class Font extends AbstractStyle return $content; } - /** - * Write end style - * - * @return string - */ - public function writeClosing() - { - $style = $this->getStyle(); - if (!$style instanceof \PhpOffice\PhpWord\Style\Font) { - return; - } - - $content = ''; - $content .= '\cf0'; - $content .= '\f0'; - - $size = $style->getSize(); - $content .= $this->getValueIf(is_numeric($size), '\fs' . (PhpWord::DEFAULT_FONT_SIZE * 2)); - - $content .= $this->getValueIf($style->isBold(), '\b0'); - $content .= $this->getValueIf($style->isItalic(), '\i0'); - $content .= $this->getValueIf($style->getUnderline() != FontStyle::UNDERLINE_NONE, '\ul0'); - $content .= $this->getValueIf($style->isStrikethrough(), '\strike0'); - $content .= $this->getValueIf($style->isSuperScript(), '\super0'); - $content .= $this->getValueIf($style->isSubScript(), '\sub0'); - - return $content; - } - /** * Set font name index * diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index 8abdf2fb..8811cacf 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpWord\Writer\RTF\Style; +use PhpOffice\PhpWord\Style\Alignment; use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle; /** @@ -39,10 +40,10 @@ class Paragraph extends AbstractStyle } $alignments = array( - ParagraphStyle::ALIGN_LEFT => '\ql', - ParagraphStyle::ALIGN_RIGHT => '\qr', - ParagraphStyle::ALIGN_CENTER => '\qc', - ParagraphStyle::ALIGN_BOTH => '\qj', + Alignment::ALIGN_LEFT => '\ql', + Alignment::ALIGN_RIGHT => '\qr', + Alignment::ALIGN_CENTER => '\qc', + Alignment::ALIGN_BOTH => '\qj', ); $align = $style->getAlign(); diff --git a/src/PhpWord/Writer/Word2007/Style/Alignment.php b/src/PhpWord/Writer/Word2007/Style/Alignment.php new file mode 100644 index 00000000..cefadb57 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/Alignment.php @@ -0,0 +1,44 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\Alignment) { + return; + } + $value = $style->getValue(); + if ($value !== null) { + $xmlWriter = $this->getXmlWriter(); + $xmlWriter->startElement('w:jc'); + $xmlWriter->writeAttribute('w:val', $value); + $xmlWriter->endElement(); // w:jc + } + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Paragraph.php b/src/PhpWord/Writer/Word2007/Style/Paragraph.php index 88a75845..7322fd91 100644 --- a/src/PhpWord/Writer/Word2007/Style/Paragraph.php +++ b/src/PhpWord/Writer/Word2007/Style/Paragraph.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; +use PhpOffice\PhpWord\Style\Alignment as AlignmentStyle; + /** * Paragraph style writer * @@ -81,8 +83,8 @@ class Paragraph extends AbstractStyle $xmlWriter->writeElementIf(!is_null($styleName), 'w:pStyle', 'w:val', $styleName); // Alignment - $align = $style->getAlign(); - $xmlWriter->writeElementIf(!is_null($align), 'w:jc', 'w:val', $align); + $styleWriter = new Alignment($xmlWriter, new AlignmentStyle(array('value' => $style->getAlign()))); + $styleWriter->write(); // Pagination $xmlWriter->writeElementIf(!$style->hasWidowControl(), 'w:widowControl', 'w:val', '0'); diff --git a/src/PhpWord/Writer/Word2007/Style/Table.php b/src/PhpWord/Writer/Word2007/Style/Table.php index 0a0241c3..3fbf8497 100644 --- a/src/PhpWord/Writer/Word2007/Style/Table.php +++ b/src/PhpWord/Writer/Word2007/Style/Table.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; +use PhpOffice\PhpWord\Style\Alignment as AlignmentStyle; + /** * Table style writer * @@ -42,30 +44,46 @@ class Table extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $hasBorders = $style->hasBorders(); + + // w:tblPr $hasMargins = $style->hasMargins(); - if ($hasMargins || $hasBorders) { - $xmlWriter->startElement('w:tblPr'); - if ($hasMargins) { - $mbWriter = new MarginBorder($xmlWriter); - $mbWriter->setSizes($style->getCellMargin()); + $hasBorders = $style->hasBorders(); + $align = $style->getAlign(); - $xmlWriter->startElement('w:tblCellMar'); - $mbWriter->write(); - $xmlWriter->endElement(); // w:tblCellMar - } - if ($hasBorders) { - $mbWriter = new MarginBorder($xmlWriter); - $mbWriter->setSizes($style->getBorderSize()); - $mbWriter->setColors($style->getBorderColor()); + $xmlWriter->startElement('w:tblPr'); - $xmlWriter->startElement('w:tblBorders'); - $mbWriter->write(); - $xmlWriter->endElement(); // w:tblBorders - } - $xmlWriter->endElement(); // w:tblPr + $xmlWriter->startElement('w:tblW'); + $xmlWriter->writeAttribute('w:w', $style->getWidth()); + $xmlWriter->writeAttribute('w:type', $style->getUnit()); + $xmlWriter->endElement(); // w:tblW + + // Alignment + $styleWriter = new Alignment($xmlWriter, new AlignmentStyle(array('value' => $align))); + $styleWriter->write(); + + // Margins + if ($hasMargins) { + $mbWriter = new MarginBorder($xmlWriter); + $mbWriter->setSizes($style->getCellMargin()); + + $xmlWriter->startElement('w:tblCellMar'); + $mbWriter->write(); + $xmlWriter->endElement(); // w:tblCellMar } + // Borders + if ($hasBorders) { + $mbWriter = new MarginBorder($xmlWriter); + $mbWriter->setSizes($style->getBorderSize()); + $mbWriter->setColors($style->getBorderColor()); + + $xmlWriter->startElement('w:tblBorders'); + $mbWriter->write(); + $xmlWriter->endElement(); // w:tblBorders + } + + $xmlWriter->endElement(); // w:tblPr + // Only write background color and first row for full style if ($this->isFullStyle) { // Background color diff --git a/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php b/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php index 97a6c6bd..dfcf3fee 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/StyleTest.php @@ -29,7 +29,7 @@ class StyleTest extends \PHPUnit_Framework_TestCase public function testEmptyStyles() { $styles = array( - 'Cell', 'Font', 'Image', 'Indentation', 'LineNumbering', + 'Alignment', 'Cell', 'Font', 'Image', 'Indentation', 'LineNumbering', 'Paragraph', 'Section', 'Shading', 'Spacing', 'Tab', 'Table', 'TextBox' ); foreach ($styles as $style) {