Skip to content

Commit 495d80d

Browse files
andomiellProgi1984
authored andcommitted
HTML Writer : Fixed rowspan for tables
1 parent a7b9030 commit 495d80d

File tree

3 files changed

+117
-32
lines changed

3 files changed

+117
-32
lines changed

samples/Sample_09_Tables.php

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,13 @@
5656

5757
/*
5858
* 3. colspan (gridSpan) and rowspan (vMerge)
59-
* ---------------------
60-
* | | B | |
61-
* | A |--------| E |
62-
* | | C | D | |
63-
* ---------------------
59+
* -------------------------
60+
* | A | B | C |
61+
* |-----|-----------| |
62+
* | D | |
63+
* ------|-----------| |
64+
* | E | F | G | |
65+
* -------------------------
6466
*/
6567

6668
$section->addPageBreak();
@@ -74,28 +76,23 @@
7476
$cellVCentered = ['valign' => 'center'];
7577

7678
$spanTableStyleName = 'Colspan Rowspan';
77-
$phpWord->addTableStyle($spanTableStyleName, $fancyTableStyle);
79+
$phpWord->addTableStyle($spanTableStyleName, $styleTable);
7880
$table = $section->addTable($spanTableStyleName);
7981

80-
$table->addRow();
81-
82-
$cell1 = $table->addCell(2000, $cellRowSpan);
83-
$textrun1 = $cell1->addTextRun($cellHCentered);
84-
$textrun1->addText('A');
85-
$textrun1->addFootnote()->addText('Row span');
86-
87-
$cell2 = $table->addCell(4000, $cellColSpan);
88-
$textrun2 = $cell2->addTextRun($cellHCentered);
89-
$textrun2->addText('B');
90-
$textrun2->addFootnote()->addText('Column span');
82+
$row1 = $table->addRow();
83+
$row1->addCell(500)->addText('A');
84+
$row1->addCell(1000, array('gridSpan' => 2))->addText('B');
85+
$row1->addCell(500, array('vMerge' => 'restart'))->addText('C');
9186

92-
$table->addCell(2000, $cellRowSpan)->addText('E', null, $cellHCentered);
87+
$row2 = $table->addRow();
88+
$row2->addCell(1500, array('gridSpan' => 3))->addText('D');
89+
$row2->addCell(null, array('vMerge' => 'continue'));
9390

94-
$table->addRow();
95-
$table->addCell(null, $cellRowContinue);
96-
$table->addCell(2000, $cellVCentered)->addText('C', null, $cellHCentered);
97-
$table->addCell(2000, $cellVCentered)->addText('D', null, $cellHCentered);
98-
$table->addCell(null, $cellRowContinue);
91+
$row3 = $table->addRow();
92+
$row3->addCell(500)->addText('E');
93+
$row3->addCell(500)->addText('F');
94+
$row3->addCell(500)->addText('G');
95+
$row3->addCell(null, array('vMerge' => 'continue'));
9996

10097
/*
10198
* 4. colspan (gridSpan) and rowspan (vMerge)

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

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,9 @@ public function write()
6565
$cellColSpan = $cellStyle->getGridSpan();
6666
$cellRowSpan = 1;
6767
$cellVMerge = $cellStyle->getVMerge();
68-
// If this is the first cell of the vertical merge, find out how man rows it spans
68+
// If this is the first cell of the vertical merge, find out how many rows it spans
6969
if ($cellVMerge === 'restart') {
70-
for ($k = $i + 1; $k < $rowCount; ++$k) {
71-
$kRowCells = $rows[$k]->getCells();
72-
if (isset($kRowCells[$j]) && $kRowCells[$j]->getStyle()->getVMerge() === 'continue') {
73-
++$cellRowSpan;
74-
} else {
75-
break;
76-
}
77-
}
70+
$cellRowSpan = $this->calculateCellRowSpan($rows, $i, $j);
7871
}
7972
// Ignore cells that are merged vertically with previous rows
8073
if ($cellVMerge !== 'continue') {
@@ -131,4 +124,62 @@ private function getTableStyle($tableStyle = null): string
131124

132125
return ' style="' . $style . '"';
133126
}
127+
128+
/**
129+
* Calculates cell rowspan.
130+
*
131+
* @param \PhpOffice\PhpWord\Element\Row[] $rows
132+
* @param int $rowIndex
133+
* @param int $colIndex
134+
*
135+
* @return int
136+
*/
137+
private function calculateCellRowSpan($rows, $rowIndex, $colIndex)
138+
{
139+
$currentRow = $rows[$rowIndex];
140+
$currentRowCells = $currentRow->getCells();
141+
$shiftedColIndex = 0;
142+
143+
foreach ($currentRowCells as $cell) {
144+
if ($cell === $currentRowCells[$colIndex]) {
145+
break;
146+
}
147+
148+
$colSpan = 1;
149+
150+
if ($cell->getStyle()->getGridSpan() !== null) {
151+
$colSpan = $cell->getStyle()->getGridSpan();
152+
}
153+
154+
$shiftedColIndex += $colSpan;
155+
}
156+
157+
$rowCount = count($rows);
158+
$rowSpan = 1;
159+
160+
for ($i = $rowIndex + 1; $i < $rowCount; $i++) {
161+
$rowCells = $rows[$i]->getCells();
162+
$colIndex = 0;
163+
164+
foreach ($rowCells as $cell) {
165+
if ($colIndex === $shiftedColIndex) {
166+
if ($cell->getStyle()->getVMerge() === 'continue') {
167+
$rowSpan++;
168+
}
169+
170+
break;
171+
}
172+
173+
$colSpan = 1;
174+
175+
if ($cell->getStyle()->getGridSpan() !== null) {
176+
$colSpan = $cell->getStyle()->getGridSpan();
177+
}
178+
179+
$colIndex += $colSpan;
180+
}
181+
}
182+
183+
return $rowSpan;
184+
}
134185
}

tests/PhpWordTests/Writer/HTML/ElementTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,43 @@ public function testWriteRowSpan(): void
139139
self::assertEquals(1, $xpath->query('/html/body/div/table/tr[2]/td')->length);
140140
}
141141

142+
/**
143+
* Tests writing table with rowspan and colspan
144+
*/
145+
public function testWriteRowSpanAndColSpan()
146+
{
147+
$phpWord = new PhpWord();
148+
$section = $phpWord->addSection();
149+
$table = $section->addTable();
150+
151+
$row1 = $table->addRow();
152+
$row1->addCell(500)->addText('A');
153+
$row1->addCell(1000, array('gridSpan' => 2))->addText('B');
154+
$row1->addCell(500, array('vMerge' => 'restart'))->addText('C');
155+
156+
$row2 = $table->addRow();
157+
$row2->addCell(1500, array('gridSpan' => 3))->addText('D');
158+
$row2->addCell(null, array('vMerge' => 'continue'));
159+
160+
$row3 = $table->addRow();
161+
$row3->addCell(500)->addText('E');
162+
$row3->addCell(500)->addText('F');
163+
$row3->addCell(500)->addText('G');
164+
$row3->addCell(null, array('vMerge' => 'continue'));
165+
166+
$dom = $this->getAsHTML($phpWord);
167+
$xpath = new \DOMXPath($dom);
168+
169+
$this->assertEquals(3, $xpath->query('/html/body/table/tr[1]/td')->length);
170+
$this->assertEquals('2', $xpath->query('/html/body/table/tr[1]/td[2]')->item(0)->attributes->getNamedItem('colspan')->textContent);
171+
$this->assertEquals('3', $xpath->query('/html/body/table/tr[1]/td[3]')->item(0)->attributes->getNamedItem('rowspan')->textContent);
172+
173+
$this->assertEquals(1, $xpath->query('/html/body/table/tr[2]/td')->length);
174+
$this->assertEquals('3', $xpath->query('/html/body/table/tr[2]/td[1]')->item(0)->attributes->getNamedItem('colspan')->textContent);
175+
176+
$this->assertEquals(3, $xpath->query('/html/body/table/tr[3]/td')->length);
177+
}
178+
142179
private function getAsHTML(PhpWord $phpWord)
143180
{
144181
$htmlWriter = new HTML($phpWord);

0 commit comments

Comments
 (0)