From f9a05547f717e9c95fa2054f12d9f3fa6a56c4dd Mon Sep 17 00:00:00 2001 From: Henri MEDOT Date: Thu, 8 Mar 2018 23:46:22 +0100 Subject: [PATCH] Added support for Floating Table Positioning (tblpPr) (#639) Added support for Floating Table Positioning (tblpPr) --- CHANGELOG.md | 1 + docs/elements.rst | 1 + docs/styles.rst | 14 + samples/Sample_09_Tables.php | 12 + samples/Sample_26_Html.php | 3 + src/PhpWord/Reader/Word2007/AbstractPart.php | 30 ++ src/PhpWord/Style/Table.php | 30 ++ src/PhpWord/Style/TablePosition.php | 410 ++++++++++++++++++ src/PhpWord/Writer/Word2007/Style/Table.php | 5 + .../Writer/Word2007/Style/TablePosition.php | 65 +++ tests/PhpWord/Reader/Word2007/StyleTest.php | 35 +- tests/PhpWord/Style/TablePositionTest.php | 65 +++ tests/PhpWord/Style/TableTest.php | 13 + .../Writer/Word2007/Style/TableTest.php | 42 ++ 14 files changed, 725 insertions(+), 1 deletion(-) create mode 100644 src/PhpWord/Style/TablePosition.php create mode 100644 src/PhpWord/Writer/Word2007/Style/TablePosition.php create mode 100644 tests/PhpWord/Style/TablePositionTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index cdc4d67c..0c83ac35 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ v0.15.0 (?? ??? 2018) - Added support for Vertically Raised or Lowered Text (w:position) @anrikun @troosan #640 - Add support for MACROBUTTON field @phryneas @troosan #1021 - Add support for Hyphenation @Trainmaster #1282 (Document: `autoHyphenation`, `consecutiveHyphenLimit`, `hyphenationZone`, `doNotHyphenateCaps`, Paragraph: `suppressAutoHyphens`) +- Added support for Floating Table Positioning (tblpPr) @anrikun #639 ### Fixed - Fix reading of docx default style - @troosan #1238 diff --git a/docs/elements.rst b/docs/elements.rst index 4d1b9383..4c5ad03b 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -482,6 +482,7 @@ Track changes can be set on text elements. There are 2 ways to set the change in Either by calling the `setChangeInfo()`, or by setting the `TrackChange` instance on the element with `setTrackChange()`. .. code-block:: php + $phpWord = new \PhpOffice\PhpWord\PhpWord(); // New portrait section diff --git a/docs/styles.rst b/docs/styles.rst index b4ed328d..98088e78 100644 --- a/docs/styles.rst +++ b/docs/styles.rst @@ -108,6 +108,20 @@ Available Table style options: - ``unit``. The unit to use for the width. One of ``\PhpOffice\PhpWord\SimpleType\TblWidth``. Defaults to *auto*. - ``layout``. Table layout, either *fixed* or *autofit* See ``\PhpOffice\PhpWord\Style\Table`` for constants. - ``cellSpacing`` Cell spacing in *twip* +- ``position`` Floating Table Positioning, see below for options + +Floating Table Positioning options: + +- ``leftFromText`` Distance From Left of Table to Text in *twip* +- ``rightFromText`` Distance From Right of Table to Text in *twip* +- ``topFromText`` Distance From Top of Table to Text in *twip* +- ``bottomFromText`` Distance From Top of Table to Text in *twip* +- ``vertAnchor`` Table Vertical Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::VANCHOR_*`` +- ``horzAnchor`` Table Horizontal Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::HANCHOR_*`` +- ``tblpXSpec`` Relative Horizontal Alignment From Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::XALIGN_*`` +- ``tblpX`` Absolute Horizontal Distance From Anchorin *twip* +- ``tblpYSpec`` Relative Vertical Alignment From Anchor, one of ``\PhpOffice\PhpWord\Style\TablePosition::YALIGN_*`` +- ``tblpY`` Absolute Vertical Distance From Anchorin *twip* Available Row style options: diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php index ba41aa54..e458a5ee 100644 --- a/samples/Sample_09_Tables.php +++ b/samples/Sample_09_Tables.php @@ -1,4 +1,7 @@ addText('This cell contains nested table.'); $innerCell = $cell->addTable(array('alignment' => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER))->addRow()->addCell(); $innerCell->addText('Inside nested table'); +// 6. Table with floating position + +$section->addTextBreak(2); +$section->addText('Table with floating positioning.', $header); + +$table = $section->addTable(array('borderSize' => 6, 'borderColor' => '999999', 'position' => array('vertAnchor' => TablePosition::VANCHOR_TEXT, 'bottomFromText' => Converter::cmToTwip(1)))); +$cell = $table->addRow()->addCell(); +$cell->addText('This is a single cell.'); + // Save file echo write($phpWord, basename(__FILE__, '.php'), $writers); if (!CLI) { diff --git a/samples/Sample_26_Html.php b/samples/Sample_26_Html.php index f6086357..31e5984f 100644 --- a/samples/Sample_26_Html.php +++ b/samples/Sample_26_Html.php @@ -4,6 +4,7 @@ include_once 'Sample_Header.php'; // New Word Document echo date('H:i:s') , ' Create new PhpWord object' , EOL; $phpWord = new \PhpOffice\PhpWord\PhpWord(); +$phpWord->addParagraphStyle('Heading2', array('alignment' => 'center')); $section = $phpWord->addSection(); $html = '

Adding element via HTML

'; @@ -17,6 +18,8 @@ $html .= '

היי, זה $html .= '

Unordered (bulleted) list:

'; $html .= ''; +$html .= '

centered title

'; + $html .= '

Ordered (numbered) list:

'; $html .= '
  1. List 1 item 1

  2. diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index 2cbfba37..7ba53ad1 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -462,12 +462,42 @@ abstract class AbstractPart $styleDefs['layout'] = array(self::READ_VALUE, 'w:tblLayout', 'w:type'); $styleDefs['cellSpacing'] = array(self::READ_VALUE, 'w:tblCellSpacing', 'w:w'); $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs); + + $tablePositionNode = $xmlReader->getElement('w:tblpPr', $styleNode); + if ($tablePositionNode !== null) { + $style['position'] = $this->readTablePosition($xmlReader, $tablePositionNode); + } } } return $style; } + /** + * Read w:tblpPr + * + * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \DOMElement $domNode + * @return array + */ + private function readTablePosition(XMLReader $xmlReader, \DOMElement $domNode) + { + $styleDefs = array( + 'leftFromText' => array(self::READ_VALUE, '.', 'w:leftFromText'), + 'rightFromText' => array(self::READ_VALUE, '.', 'w:rightFromText'), + 'topFromText' => array(self::READ_VALUE, '.', 'w:topFromText'), + 'bottomFromText' => array(self::READ_VALUE, '.', 'w:bottomFromText'), + 'vertAnchor' => array(self::READ_VALUE, '.', 'w:vertAnchor'), + 'horzAnchor' => array(self::READ_VALUE, '.', 'w:horzAnchor'), + 'tblpXSpec' => array(self::READ_VALUE, '.', 'w:tblpXSpec'), + 'tblpX' => array(self::READ_VALUE, '.', 'w:tblpX'), + 'tblpYSpec' => array(self::READ_VALUE, '.', 'w:tblpYSpec'), + 'tblpY' => array(self::READ_VALUE, '.', 'w:tblpY'), + ); + + return $this->readStyleDefs($xmlReader, $domNode, $styleDefs); + } + /** * Read w:tcPr * diff --git a/src/PhpWord/Style/Table.php b/src/PhpWord/Style/Table.php index 8aba172a..92a24dde 100644 --- a/src/PhpWord/Style/Table.php +++ b/src/PhpWord/Style/Table.php @@ -152,6 +152,13 @@ class Table extends Border */ private $layout = self::LAYOUT_AUTO; + /** + * Position + * + * @var \PhpOffice\PhpWord\Style\TablePosition + */ + private $position; + /** * Create new table style * @@ -694,4 +701,27 @@ class Table extends Border return $this; } + + /** + * Get position + * + * @return \PhpOffice\PhpWord\Style\TablePosition + */ + public function getPosition() + { + return $this->position; + } + + /** + * Set position + * + * @param mixed $value + * @return self + */ + public function setPosition($value = null) + { + $this->setObjectVal($value, 'TablePosition', $this->position); + + return $this; + } } diff --git a/src/PhpWord/Style/TablePosition.php b/src/PhpWord/Style/TablePosition.php new file mode 100644 index 00000000..7cb0a0be --- /dev/null +++ b/src/PhpWord/Style/TablePosition.php @@ -0,0 +1,410 @@ +setStyleByArray($style); + } + + /** + * Get distance from left of table to text + * + * @return int + */ + public function getLeftFromText() + { + return $this->leftFromText; + } + + /** + * Set distance from left of table to text + * + * @param int $value + * @return self + */ + public function setLeftFromText($value = null) + { + $this->leftFromText = $this->setNumericVal($value, $this->leftFromText); + + return $this; + } + + /** + * Get distance from right of table to text + * + * @return int + */ + public function getRightFromText() + { + return $this->rightFromText; + } + + /** + * Set distance from right of table to text + * + * @param int $value + * @return self + */ + public function setRightFromText($value = null) + { + $this->rightFromText = $this->setNumericVal($value, $this->rightFromText); + + return $this; + } + + /** + * Get distance from top of table to text + * + * @return int + */ + public function getTopFromText() + { + return $this->topFromText; + } + + /** + * Set distance from top of table to text + * + * @param int $value + * @return self + */ + public function setTopFromText($value = null) + { + $this->topFromText = $this->setNumericVal($value, $this->topFromText); + + return $this; + } + + /** + * Get distance from bottom of table to text + * + * @return int + */ + public function getBottomFromText() + { + return $this->bottomFromText; + } + + /** + * Set distance from bottom of table to text + * + * @param int $value + * @return self + */ + public function setBottomFromText($value = null) + { + $this->bottomFromText = $this->setNumericVal($value, $this->bottomFromText); + + return $this; + } + + /** + * Get table vertical anchor + * + * @return string + */ + public function getVertAnchor() + { + return $this->vertAnchor; + } + + /** + * Set table vertical anchor + * + * @param string $value + * @return self + */ + public function setVertAnchor($value = null) + { + $enum = array( + self::VANCHOR_TEXT, + self::VANCHOR_MARGIN, + self::VANCHOR_PAGE, + ); + $this->vertAnchor = $this->setEnumVal($value, $enum, $this->vertAnchor); + + return $this; + } + + /** + * Get table horizontal anchor + * + * @return string + */ + public function getHorzAnchor() + { + return $this->horzAnchor; + } + + /** + * Set table horizontal anchor + * + * @param string $value + * @return self + */ + public function setHorzAnchor($value = null) + { + $enum = array( + self::HANCHOR_TEXT, + self::HANCHOR_MARGIN, + self::HANCHOR_PAGE, + ); + $this->horzAnchor = $this->setEnumVal($value, $enum, $this->horzAnchor); + + return $this; + } + + /** + * Get relative horizontal alignment from anchor + * + * @return string + */ + public function getTblpXSpec() + { + return $this->tblpXSpec; + } + + /** + * Set relative horizontal alignment from anchor + * + * @param string $value + * @return self + */ + public function setTblpXSpec($value = null) + { + $enum = array( + self::XALIGN_LEFT, + self::XALIGN_CENTER, + self::XALIGN_RIGHT, + self::XALIGN_INSIDE, + self::XALIGN_OUTSIDE, + ); + $this->tblpXSpec = $this->setEnumVal($value, $enum, $this->tblpXSpec); + + return $this; + } + + /** + * Get absolute horizontal distance from anchor + * + * @return int + */ + public function getTblpX() + { + return $this->tblpX; + } + + /** + * Set absolute horizontal distance from anchor + * + * @param int $value + * @return self + */ + public function setTblpX($value = null) + { + $this->tblpX = $this->setNumericVal($value, $this->tblpX); + + return $this; + } + + /** + * Get relative vertical alignment from anchor + * + * @return string + */ + public function getTblpYSpec() + { + return $this->tblpYSpec; + } + + /** + * Set relative vertical alignment from anchor + * + * @param string $value + * @return self + */ + public function setTblpYSpec($value = null) + { + $enum = array( + self::YALIGN_INLINE, + self::YALIGN_TOP, + self::YALIGN_CENTER, + self::YALIGN_BOTTOM, + self::YALIGN_INSIDE, + self::YALIGN_OUTSIDE, + ); + $this->tblpYSpec = $this->setEnumVal($value, $enum, $this->tblpYSpec); + + return $this; + } + + /** + * Get absolute vertical distance from anchor + * + * @return int + */ + public function getTblpY() + { + return $this->tblpY; + } + + /** + * Set absolute vertical distance from anchor + * + * @param int $value + * @return self + */ + public function setTblpY($value = null) + { + $this->tblpY = $this->setNumericVal($value, $this->tblpY); + + return $this; + } +} diff --git a/src/PhpWord/Writer/Word2007/Style/Table.php b/src/PhpWord/Writer/Word2007/Style/Table.php index 14226212..058e60e2 100644 --- a/src/PhpWord/Writer/Word2007/Style/Table.php +++ b/src/PhpWord/Writer/Word2007/Style/Table.php @@ -80,6 +80,11 @@ class Table extends AbstractStyle $this->writeTblWidth($xmlWriter, 'w:tblW', $style->getUnit(), $style->getWidth()); $this->writeTblWidth($xmlWriter, 'w:tblCellSpacing', TblWidth::TWIP, $style->getCellSpacing()); $this->writeLayout($xmlWriter, $style->getLayout()); + + // Position + $styleWriter = new TablePosition($xmlWriter, $style->getPosition()); + $styleWriter->write(); + $this->writeMargin($xmlWriter, $style); $this->writeBorder($xmlWriter, $style); diff --git a/src/PhpWord/Writer/Word2007/Style/TablePosition.php b/src/PhpWord/Writer/Word2007/Style/TablePosition.php new file mode 100644 index 00000000..14fa6a0d --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Style/TablePosition.php @@ -0,0 +1,65 @@ +getStyle(); + if (!$style instanceof \PhpOffice\PhpWord\Style\TablePosition) { + return; + } + + $values = array(); + $properties = array( + 'leftFromText', + 'rightFromText', + 'topFromText', + 'bottomFromText', + 'vertAnchor', + 'horzAnchor', + 'tblpXSpec', + 'tblpX', + 'tblpYSpec', + 'tblpY', + ); + foreach ($properties as $property) { + $method = 'get' . $property; + if (method_exists($style, $method)) { + $values[$property] = $style->$method(); + } + } + $values = array_filter($values); + + if ($values) { + $xmlWriter = $this->getXmlWriter(); + $xmlWriter->startElement('w:tblpPr'); + foreach ($values as $property => $value) { + $xmlWriter->writeAttribute('w:' . $property, $value); + } + $xmlWriter->endElement(); + } + } +} diff --git a/tests/PhpWord/Reader/Word2007/StyleTest.php b/tests/PhpWord/Reader/Word2007/StyleTest.php index 93e4a1f0..3b04b677 100644 --- a/tests/PhpWord/Reader/Word2007/StyleTest.php +++ b/tests/PhpWord/Reader/Word2007/StyleTest.php @@ -20,6 +20,7 @@ namespace PhpOffice\PhpWord\Reader\Word2007; use PhpOffice\PhpWord\AbstractTestReader; use PhpOffice\PhpWord\SimpleType\TblWidth; use PhpOffice\PhpWord\Style\Table; +use PhpOffice\PhpWord\Style\TablePosition; /** * Test class for PhpOffice\PhpWord\Reader\Word2007\Styles @@ -61,12 +62,44 @@ class StyleTest extends AbstractTestReader $elements = $phpWord->getSection(0)->getElements(); $this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]); $this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle()); - $this->assertEquals(TblWidth::AUTO, $elements[0]->getStyle()->getUnit()); /** @var \PhpOffice\PhpWord\Style\Table $tableStyle */ $tableStyle = $elements[0]->getStyle(); + $this->assertEquals(TblWidth::AUTO, $tableStyle->getUnit()); $this->assertEquals(10.5, $tableStyle->getCellSpacing()); } + /** + * Test reading of table position + */ + public function testReadTablePosition() + { + $documentXml = ' + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle()); + $this->assertNotNull($elements[0]->getStyle()->getPosition()); + $this->assertInstanceOf('PhpOffice\PhpWord\Style\TablePosition', $elements[0]->getStyle()->getPosition()); + /** @var \PhpOffice\PhpWord\Style\TablePosition $tableStyle */ + $tableStyle = $elements[0]->getStyle()->getPosition(); + $this->assertEquals(10, $tableStyle->getLeftFromText()); + $this->assertEquals(20, $tableStyle->getRightFromText()); + $this->assertEquals(30, $tableStyle->getTopFromText()); + $this->assertEquals(40, $tableStyle->getBottomFromText()); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $tableStyle->getVertAnchor()); + $this->assertEquals(TablePosition::HANCHOR_MARGIN, $tableStyle->getHorzAnchor()); + $this->assertEquals(TablePosition::XALIGN_CENTER, $tableStyle->getTblpXSpec()); + $this->assertEquals(50, $tableStyle->getTblpX()); + $this->assertEquals(TablePosition::YALIGN_TOP, $tableStyle->getTblpYSpec()); + $this->assertEquals(60, $tableStyle->getTblpY()); + } + /** * Test reading of position */ diff --git a/tests/PhpWord/Style/TablePositionTest.php b/tests/PhpWord/Style/TablePositionTest.php new file mode 100644 index 00000000..77b22e4e --- /dev/null +++ b/tests/PhpWord/Style/TablePositionTest.php @@ -0,0 +1,65 @@ + TablePosition::VANCHOR_PAGE, 'bottomFromText' => 20); + + $object = new TablePosition($styleTable); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $object->getVertAnchor()); + $this->assertEquals(20, $object->getBottomFromText()); + } + + /** + * Test setting style with normal value + */ + public function testSetGetNormal() + { + $object = new TablePosition(); + + $attributes = array( + 'leftFromText' => 4, + 'rightFromText' => 4, + 'topFromText' => 4, + 'bottomFromText' => 4, + 'vertAnchor' => TablePosition::VANCHOR_PAGE, + 'horzAnchor' => TablePosition::HANCHOR_TEXT, + 'tblpXSpec' => TablePosition::XALIGN_CENTER, + 'tblpX' => 5, + 'tblpYSpec' => TablePosition::YALIGN_OUTSIDE, + 'tblpY' => 6, + ); + foreach ($attributes as $key => $value) { + $set = "set{$key}"; + $get = "get{$key}"; + $object->$set($value); + $this->assertEquals($value, $object->$get()); + } + } +} diff --git a/tests/PhpWord/Style/TableTest.php b/tests/PhpWord/Style/TableTest.php index 9dec422e..1ee718df 100644 --- a/tests/PhpWord/Style/TableTest.php +++ b/tests/PhpWord/Style/TableTest.php @@ -195,4 +195,17 @@ class TableTest extends \PHPUnit\Framework\TestCase $object = new Table(array('cellSpacing' => 20)); $this->assertEquals(20, $object->getCellSpacing()); } + + /** + * Tests table floating position + */ + public function testTablePosition() + { + $object = new Table(); + $this->assertNull($object->getPosition()); + + $object->setPosition(array('vertAnchor' => TablePosition::VANCHOR_PAGE)); + $this->assertNotNull($object->getPosition()); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $object->getPosition()->getVertAnchor()); + } } diff --git a/tests/PhpWord/Writer/Word2007/Style/TableTest.php b/tests/PhpWord/Writer/Word2007/Style/TableTest.php index c0a0b3ad..2b67507a 100644 --- a/tests/PhpWord/Writer/Word2007/Style/TableTest.php +++ b/tests/PhpWord/Writer/Word2007/Style/TableTest.php @@ -18,6 +18,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; use PhpOffice\PhpWord\Style\Table; +use PhpOffice\PhpWord\Style\TablePosition; use PhpOffice\PhpWord\TestHelperDOCX; /** @@ -76,4 +77,45 @@ class TableTest extends \PHPUnit\Framework\TestCase $this->assertEquals(10.3, $doc->getElementAttribute($path, 'w:w')); $this->assertEquals(\PhpOffice\PhpWord\SimpleType\TblWidth::TWIP, $doc->getElementAttribute($path, 'w:type')); } + + /** + * Test write table position + */ + public function testTablePosition() + { + $tablePosition = array( + 'leftFromText' => 10, + 'rightFromText' => 20, + 'topFromText' => 30, + 'bottomFromText' => 40, + 'vertAnchor' => TablePosition::VANCHOR_PAGE, + 'horzAnchor' => TablePosition::HANCHOR_MARGIN, + 'tblpXSpec' => TablePosition::XALIGN_CENTER, + 'tblpX' => 50, + 'tblpYSpec' => TablePosition::YALIGN_TOP, + 'tblpY' => 60, + ); + $tableStyle = new Table(); + $tableStyle->setPosition($tablePosition); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable($tableStyle); + $table->addRow(); + + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:tbl/w:tblPr/w:tblpPr'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals(10, $doc->getElementAttribute($path, 'w:leftFromText')); + $this->assertEquals(20, $doc->getElementAttribute($path, 'w:rightFromText')); + $this->assertEquals(30, $doc->getElementAttribute($path, 'w:topFromText')); + $this->assertEquals(40, $doc->getElementAttribute($path, 'w:bottomFromText')); + $this->assertEquals(TablePosition::VANCHOR_PAGE, $doc->getElementAttribute($path, 'w:vertAnchor')); + $this->assertEquals(TablePosition::HANCHOR_MARGIN, $doc->getElementAttribute($path, 'w:horzAnchor')); + $this->assertEquals(TablePosition::XALIGN_CENTER, $doc->getElementAttribute($path, 'w:tblpXSpec')); + $this->assertEquals(50, $doc->getElementAttribute($path, 'w:tblpX')); + $this->assertEquals(TablePosition::YALIGN_TOP, $doc->getElementAttribute($path, 'w:tblpYSpec')); + $this->assertEquals(60, $doc->getElementAttribute($path, 'w:tblpY')); + } }