diff --git a/CHANGELOG.md b/CHANGELOG.md
index e2bf4eb7..9c3b4dfd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -28,6 +28,7 @@ v0.15.0 (?? ??? 2018)
- Fix parsing of `` tag. @troosan #1274
- Bookmark are not writton as internal link in html writer @troosan #1263
- It should be possible to add a Footnote in a ListItemRun @troosan #1287 #1287
+- Fix colspan and rowspan for tables in HTML Writer @mattbolt #1292
- Fix parsing of Heading and Title formating @troosan @gthomas2 #465
- Fix Dateformat typo, fix hours casing, add Month-Day-Year formats @ComputerTinker #591
diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php
index e458a5ee..32d89573 100644
--- a/samples/Sample_09_Tables.php
+++ b/samples/Sample_09_Tables.php
@@ -116,20 +116,20 @@ $phpWord->addTableStyle('Colspan Rowspan', $styleTable);
$table = $section->addTable('Colspan Rowspan');
$row = $table->addRow();
-
-$row->addCell(null, array('vMerge' => 'restart'))->addText('A');
-$row->addCell(null, array('gridSpan' => 2, 'vMerge' => 'restart'))->addText('B');
-$row->addCell()->addText('1');
+$row->addCell(1000, array('vMerge' => 'restart'))->addText('A');
+$row->addCell(1000, array('gridSpan' => 2, 'vMerge' => 'restart'))->addText('B');
+$row->addCell(1000)->addText('1');
$row = $table->addRow();
-$row->addCell(null, array('vMerge' => 'continue'));
-$row->addCell(null, array('vMerge' => 'continue', 'gridSpan' => 2));
-$row->addCell()->addText('2');
+$row->addCell(1000, array('vMerge' => 'continue'));
+$row->addCell(1000, array('vMerge' => 'continue', 'gridSpan' => 2));
+$row->addCell(1000)->addText('2');
+
$row = $table->addRow();
-$row->addCell(null, array('vMerge' => 'continue'));
-$row->addCell()->addText('C');
-$row->addCell()->addText('D');
-$row->addCell()->addText('3');
+$row->addCell(1000, array('vMerge' => 'continue'));
+$row->addCell(1000)->addText('C');
+$row->addCell(1000)->addText('D');
+$row->addCell(1000)->addText('3');
// 5. Nested table
diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php
index 95f7c1fa..844066f4 100644
--- a/src/PhpWord/Writer/HTML/Element/Table.php
+++ b/src/PhpWord/Writer/HTML/Element/Table.php
@@ -40,18 +40,60 @@ class Table extends AbstractElement
$rowCount = count($rows);
if ($rowCount > 0) {
$content .= '
' . PHP_EOL;
- foreach ($rows as $row) {
+ for ($i = 0; $i < $rowCount; $i++) {
/** @var $row \PhpOffice\PhpWord\Element\Row Type hint */
- $rowStyle = $row->getStyle();
+ $rowStyle = $rows[$i]->getStyle();
// $height = $row->getHeight();
$tblHeader = $rowStyle->isTblHeader();
$content .= '' . PHP_EOL;
- foreach ($row->getCells() as $cell) {
- $writer = new Container($this->parentWriter, $cell);
- $cellTag = $tblHeader ? 'th' : 'td';
- $content .= "<{$cellTag}>" . PHP_EOL;
- $content .= $writer->write();
- $content .= "{$cellTag}>" . PHP_EOL;
+ $rowCells = $rows[$i]->getCells();
+ $rowCellCount = count($rowCells);
+ for ($j = 0; $j < $rowCellCount; $j++) {
+ $cellStyle = $rowCells[$j]->getStyle();
+ $cellColSpan = $cellStyle->getGridSpan();
+ $cellRowSpan = 1;
+ $cellVMerge = $cellStyle->getVMerge();
+ // If this is the first cell of the vertical merge, find out how man rows it spans
+ if ($cellVMerge === 'restart') {
+ for ($k = $i + 1; $k < $rowCount; $k++) {
+ $kRowCells = $rows[$k]->getCells();
+ if (isset($kRowCells[$j])) {
+ if ($kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
+ $cellRowSpan++;
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ // Ignore cells that are merged vertically with previous rows
+ if ($cellVMerge !== 'continue') {
+ $cellTag = $tblHeader ? 'th' : 'td';
+ $cellColSpanAttr = (is_numeric($cellColSpan) && ($cellColSpan > 1) ? " colspan=\"{$cellColSpan}\"" : '');
+ $cellRowSpanAttr = ($cellRowSpan > 1 ? " rowspan=\"{$cellRowSpan}\"" : '');
+ $content .= "<{$cellTag}{$cellColSpanAttr}{$cellRowSpanAttr}>" . PHP_EOL;
+ $writer = new Container($this->parentWriter, $rowCells[$j]);
+ $content .= $writer->write();
+ if ($cellRowSpan > 1) {
+ // There shouldn't be any content in the subsequent merged cells, but lets check anyway
+ for ($k = $i + 1; $k < $rowCount; $k++) {
+ $kRowCells = $rows[$k]->getCells();
+ if (isset($kRowCells[$j])) {
+ if ($kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
+ $writer = new Container($this->parentWriter, $kRowCells[$j]);
+ $content .= $writer->write();
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ }
+ }
+ $content .= "{$cellTag}>" . PHP_EOL;
+ }
}
$content .= '
' . PHP_EOL;
}
diff --git a/tests/PhpWord/Writer/HTML/ElementTest.php b/tests/PhpWord/Writer/HTML/ElementTest.php
index b99a5c9a..90044b92 100644
--- a/tests/PhpWord/Writer/HTML/ElementTest.php
+++ b/tests/PhpWord/Writer/HTML/ElementTest.php
@@ -69,12 +69,73 @@ class ElementTest extends \PHPUnit\Framework\TestCase
$text2 = $section->addText('my other text');
$text2->setTrackChange(new TrackChange(TrackChange::DELETED, 'another author', new \DateTime()));
- $htmlWriter = new HTML($phpWord);
- $dom = new \DOMDocument();
- $dom->loadHTML($htmlWriter->getContent());
+ $dom = $this->getAsHTML($phpWord);
$xpath = new \DOMXpath($dom);
$this->assertTrue($xpath->query('/html/body/p[1]/ins')->length == 1);
$this->assertTrue($xpath->query('/html/body/p[2]/del')->length == 1);
}
+
+ /**
+ * Tests writing table with col span
+ */
+ public function testWriteColSpan()
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ $table = $section->addTable();
+ $row1 = $table->addRow();
+ $cell11 = $row1->addCell(1000, array('gridSpan' => 2));
+ $cell11->addText('cell spanning 2 bellow');
+ $row2 = $table->addRow();
+ $cell21 = $row2->addCell(500);
+ $cell21->addText('first cell');
+ $cell22 = $row2->addCell(500);
+ $cell22->addText('second cell');
+
+ $dom = $this->getAsHTML($phpWord);
+ $xpath = new \DOMXpath($dom);
+
+ $this->assertTrue($xpath->query('/html/body/table/tr[1]/td')->length == 1);
+ $this->assertEquals('2', $xpath->query('/html/body/table/tr/td[1]')->item(0)->attributes->getNamedItem('colspan')->textContent);
+ $this->assertTrue($xpath->query('/html/body/table/tr[2]/td')->length == 2);
+ }
+
+ /**
+ * Tests writing table with row span
+ */
+ public function testWriteRowSpan()
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ $table = $section->addTable();
+
+ $row1 = $table->addRow();
+ $row1->addCell(1000, array('vMerge' => 'restart'))->addText('row spanning 3 bellow');
+ $row1->addCell(500)->addText('first cell being spanned');
+
+ $row2 = $table->addRow();
+ $row2->addCell(null, array('vMerge' => 'continue'));
+ $row2->addCell(500)->addText('second cell being spanned');
+
+ $row3 = $table->addRow();
+ $row3->addCell(null, array('vMerge' => 'continue'));
+ $row3->addCell(500)->addText('third cell being spanned');
+
+ $dom = $this->getAsHTML($phpWord);
+ $xpath = new \DOMXpath($dom);
+
+ $this->assertTrue($xpath->query('/html/body/table/tr[1]/td')->length == 2);
+ $this->assertEquals('3', $xpath->query('/html/body/table/tr[1]/td[1]')->item(0)->attributes->getNamedItem('rowspan')->textContent);
+ $this->assertTrue($xpath->query('/html/body/table/tr[2]/td')->length == 1);
+ }
+
+ private function getAsHTML(PhpWord $phpWord)
+ {
+ $htmlWriter = new HTML($phpWord);
+ $dom = new \DOMDocument();
+ $dom->loadHTML($htmlWriter->getContent());
+
+ return $dom;
+ }
}