Skip to content

Commit d8caa0b

Browse files
authored
Merge pull request #1292 from mattbolt/develop
Fix colspan and rowspan for tables in HTML Writer
2 parents 992d842 + d7fc1a5 commit d8caa0b

File tree

4 files changed

+126
-22
lines changed

4 files changed

+126
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ v0.15.0 (?? ??? 2018)
2828
- Fix parsing of `<w:br/>` tag. @troosan #1274
2929
- Bookmark are not writton as internal link in html writer @troosan #1263
3030
- It should be possible to add a Footnote in a ListItemRun @troosan #1287 #1287
31+
- Fix colspan and rowspan for tables in HTML Writer @mattbolt #1292
3132
- Fix parsing of Heading and Title formating @troosan @gthomas2 #465
3233
- Fix Dateformat typo, fix hours casing, add Month-Day-Year formats @ComputerTinker #591
3334

samples/Sample_09_Tables.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -116,20 +116,20 @@
116116
$table = $section->addTable('Colspan Rowspan');
117117

118118
$row = $table->addRow();
119-
120-
$row->addCell(null, array('vMerge' => 'restart'))->addText('A');
121-
$row->addCell(null, array('gridSpan' => 2, 'vMerge' => 'restart'))->addText('B');
122-
$row->addCell()->addText('1');
119+
$row->addCell(1000, array('vMerge' => 'restart'))->addText('A');
120+
$row->addCell(1000, array('gridSpan' => 2, 'vMerge' => 'restart'))->addText('B');
121+
$row->addCell(1000)->addText('1');
123122

124123
$row = $table->addRow();
125-
$row->addCell(null, array('vMerge' => 'continue'));
126-
$row->addCell(null, array('vMerge' => 'continue', 'gridSpan' => 2));
127-
$row->addCell()->addText('2');
124+
$row->addCell(1000, array('vMerge' => 'continue'));
125+
$row->addCell(1000, array('vMerge' => 'continue', 'gridSpan' => 2));
126+
$row->addCell(1000)->addText('2');
127+
128128
$row = $table->addRow();
129-
$row->addCell(null, array('vMerge' => 'continue'));
130-
$row->addCell()->addText('C');
131-
$row->addCell()->addText('D');
132-
$row->addCell()->addText('3');
129+
$row->addCell(1000, array('vMerge' => 'continue'));
130+
$row->addCell(1000)->addText('C');
131+
$row->addCell(1000)->addText('D');
132+
$row->addCell(1000)->addText('3');
133133

134134
// 5. Nested table
135135

src/PhpWord/Writer/HTML/Element/Table.php

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,60 @@ public function write()
4040
$rowCount = count($rows);
4141
if ($rowCount > 0) {
4242
$content .= '<table>' . PHP_EOL;
43-
foreach ($rows as $row) {
43+
for ($i = 0; $i < $rowCount; $i++) {
4444
/** @var $row \PhpOffice\PhpWord\Element\Row Type hint */
45-
$rowStyle = $row->getStyle();
45+
$rowStyle = $rows[$i]->getStyle();
4646
// $height = $row->getHeight();
4747
$tblHeader = $rowStyle->isTblHeader();
4848
$content .= '<tr>' . PHP_EOL;
49-
foreach ($row->getCells() as $cell) {
50-
$writer = new Container($this->parentWriter, $cell);
51-
$cellTag = $tblHeader ? 'th' : 'td';
52-
$content .= "<{$cellTag}>" . PHP_EOL;
53-
$content .= $writer->write();
54-
$content .= "</{$cellTag}>" . PHP_EOL;
49+
$rowCells = $rows[$i]->getCells();
50+
$rowCellCount = count($rowCells);
51+
for ($j = 0; $j < $rowCellCount; $j++) {
52+
$cellStyle = $rowCells[$j]->getStyle();
53+
$cellColSpan = $cellStyle->getGridSpan();
54+
$cellRowSpan = 1;
55+
$cellVMerge = $cellStyle->getVMerge();
56+
// If this is the first cell of the vertical merge, find out how man rows it spans
57+
if ($cellVMerge === 'restart') {
58+
for ($k = $i + 1; $k < $rowCount; $k++) {
59+
$kRowCells = $rows[$k]->getCells();
60+
if (isset($kRowCells[$j])) {
61+
if ($kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
62+
$cellRowSpan++;
63+
} else {
64+
break;
65+
}
66+
} else {
67+
break;
68+
}
69+
}
70+
}
71+
// Ignore cells that are merged vertically with previous rows
72+
if ($cellVMerge !== 'continue') {
73+
$cellTag = $tblHeader ? 'th' : 'td';
74+
$cellColSpanAttr = (is_numeric($cellColSpan) && ($cellColSpan > 1) ? " colspan=\"{$cellColSpan}\"" : '');
75+
$cellRowSpanAttr = ($cellRowSpan > 1 ? " rowspan=\"{$cellRowSpan}\"" : '');
76+
$content .= "<{$cellTag}{$cellColSpanAttr}{$cellRowSpanAttr}>" . PHP_EOL;
77+
$writer = new Container($this->parentWriter, $rowCells[$j]);
78+
$content .= $writer->write();
79+
if ($cellRowSpan > 1) {
80+
// There shouldn't be any content in the subsequent merged cells, but lets check anyway
81+
for ($k = $i + 1; $k < $rowCount; $k++) {
82+
$kRowCells = $rows[$k]->getCells();
83+
if (isset($kRowCells[$j])) {
84+
if ($kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
85+
$writer = new Container($this->parentWriter, $kRowCells[$j]);
86+
$content .= $writer->write();
87+
} else {
88+
break;
89+
}
90+
} else {
91+
break;
92+
}
93+
}
94+
}
95+
$content .= "</{$cellTag}>" . PHP_EOL;
96+
}
5597
}
5698
$content .= '</tr>' . PHP_EOL;
5799
}

tests/PhpWord/Writer/HTML/ElementTest.php

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,73 @@ public function testWriteTrackChanges()
6969
$text2 = $section->addText('my other text');
7070
$text2->setTrackChange(new TrackChange(TrackChange::DELETED, 'another author', new \DateTime()));
7171

72-
$htmlWriter = new HTML($phpWord);
73-
$dom = new \DOMDocument();
74-
$dom->loadHTML($htmlWriter->getContent());
72+
$dom = $this->getAsHTML($phpWord);
7573
$xpath = new \DOMXpath($dom);
7674

7775
$this->assertTrue($xpath->query('/html/body/p[1]/ins')->length == 1);
7876
$this->assertTrue($xpath->query('/html/body/p[2]/del')->length == 1);
7977
}
78+
79+
/**
80+
* Tests writing table with col span
81+
*/
82+
public function testWriteColSpan()
83+
{
84+
$phpWord = new PhpWord();
85+
$section = $phpWord->addSection();
86+
$table = $section->addTable();
87+
$row1 = $table->addRow();
88+
$cell11 = $row1->addCell(1000, array('gridSpan' => 2));
89+
$cell11->addText('cell spanning 2 bellow');
90+
$row2 = $table->addRow();
91+
$cell21 = $row2->addCell(500);
92+
$cell21->addText('first cell');
93+
$cell22 = $row2->addCell(500);
94+
$cell22->addText('second cell');
95+
96+
$dom = $this->getAsHTML($phpWord);
97+
$xpath = new \DOMXpath($dom);
98+
99+
$this->assertTrue($xpath->query('/html/body/table/tr[1]/td')->length == 1);
100+
$this->assertEquals('2', $xpath->query('/html/body/table/tr/td[1]')->item(0)->attributes->getNamedItem('colspan')->textContent);
101+
$this->assertTrue($xpath->query('/html/body/table/tr[2]/td')->length == 2);
102+
}
103+
104+
/**
105+
* Tests writing table with row span
106+
*/
107+
public function testWriteRowSpan()
108+
{
109+
$phpWord = new PhpWord();
110+
$section = $phpWord->addSection();
111+
$table = $section->addTable();
112+
113+
$row1 = $table->addRow();
114+
$row1->addCell(1000, array('vMerge' => 'restart'))->addText('row spanning 3 bellow');
115+
$row1->addCell(500)->addText('first cell being spanned');
116+
117+
$row2 = $table->addRow();
118+
$row2->addCell(null, array('vMerge' => 'continue'));
119+
$row2->addCell(500)->addText('second cell being spanned');
120+
121+
$row3 = $table->addRow();
122+
$row3->addCell(null, array('vMerge' => 'continue'));
123+
$row3->addCell(500)->addText('third cell being spanned');
124+
125+
$dom = $this->getAsHTML($phpWord);
126+
$xpath = new \DOMXpath($dom);
127+
128+
$this->assertTrue($xpath->query('/html/body/table/tr[1]/td')->length == 2);
129+
$this->assertEquals('3', $xpath->query('/html/body/table/tr[1]/td[1]')->item(0)->attributes->getNamedItem('rowspan')->textContent);
130+
$this->assertTrue($xpath->query('/html/body/table/tr[2]/td')->length == 1);
131+
}
132+
133+
private function getAsHTML(PhpWord $phpWord)
134+
{
135+
$htmlWriter = new HTML($phpWord);
136+
$dom = new \DOMDocument();
137+
$dom->loadHTML($htmlWriter->getContent());
138+
139+
return $dom;
140+
}
80141
}

0 commit comments

Comments
 (0)