From e03596a7194b245796fa24108545388d8a13c4ae Mon Sep 17 00:00:00 2001 From: Evgeniya Gryaznova Date: Mon, 8 Nov 2021 21:10:52 +0900 Subject: [PATCH] Fix rowspan for tables in HTML Writer --- samples/Sample_09_Tables.php | 50 +++++++--------- src/PhpWord/Writer/HTML/Element/Table.php | 73 +++++++++++++++++++---- tests/PhpWord/Writer/HTML/ElementTest.php | 37 ++++++++++++ 3 files changed, 118 insertions(+), 42 deletions(-) diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php index 3115543865..496881acf6 100644 --- a/samples/Sample_09_Tables.php +++ b/samples/Sample_09_Tables.php @@ -55,46 +55,38 @@ /* * 3. colspan (gridSpan) and rowspan (vMerge) - * --------------------- - * | | B | | - * | A |--------| E | - * | | C | D | | - * --------------------- + * ------------------------- + * | A | B | C | + * |-----|-----------| | + * | D | | + * ------|-----------| | + * | E | F | G | | + * ------------------------- */ $section->addPageBreak(); $section->addText('Table with colspan and rowspan', $header); -$fancyTableStyle = array('borderSize' => 6, 'borderColor' => '999999'); -$cellRowSpan = array('vMerge' => 'restart', 'valign' => 'center', 'bgColor' => 'FFFF00'); -$cellRowContinue = array('vMerge' => 'continue'); -$cellColSpan = array('gridSpan' => 2, 'valign' => 'center'); -$cellHCentered = array('alignment' => \PhpOffice\PhpWord\SimpleType\Jc::CENTER); -$cellVCentered = array('valign' => 'center'); +$styleTable = array('borderSize' => 6, 'borderColor' => '999999'); $spanTableStyleName = 'Colspan Rowspan'; -$phpWord->addTableStyle($spanTableStyleName, $fancyTableStyle); +$phpWord->addTableStyle($spanTableStyleName, $styleTable); $table = $section->addTable($spanTableStyleName); -$table->addRow(); - -$cell1 = $table->addCell(2000, $cellRowSpan); -$textrun1 = $cell1->addTextRun($cellHCentered); -$textrun1->addText('A'); -$textrun1->addFootnote()->addText('Row span'); - -$cell2 = $table->addCell(4000, $cellColSpan); -$textrun2 = $cell2->addTextRun($cellHCentered); -$textrun2->addText('B'); -$textrun2->addFootnote()->addText('Column span'); +$row1 = $table->addRow(); +$row1->addCell(500)->addText('A'); +$row1->addCell(1000, array('gridSpan' => 2))->addText('B'); +$row1->addCell(500, array('vMerge' => 'restart'))->addText('C'); -$table->addCell(2000, $cellRowSpan)->addText('E', null, $cellHCentered); +$row2 = $table->addRow(); +$row2->addCell(1500, array('gridSpan' => 3))->addText('D'); +$row2->addCell(null, array('vMerge' => 'continue')); -$table->addRow(); -$table->addCell(null, $cellRowContinue); -$table->addCell(2000, $cellVCentered)->addText('C', null, $cellHCentered); -$table->addCell(2000, $cellVCentered)->addText('D', null, $cellHCentered); -$table->addCell(null, $cellRowContinue); +$row3 = $table->addRow(); +$row3->addCell(500)->addText('E'); +$row3->addCell(500)->addText('F'); +$row3->addCell(500)->addText('G'); +$row3->addCell(null, array('vMerge' => 'continue')); /* * 4. colspan (gridSpan) and rowspan (vMerge) diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php index 059574a272..99784ae673 100644 --- a/src/PhpWord/Writer/HTML/Element/Table.php +++ b/src/PhpWord/Writer/HTML/Element/Table.php @@ -63,20 +63,9 @@ public function write() $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 this is the first cell of the vertical merge, find out how many 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; - } - } + $cellRowSpan = $this->calculateCellRowSpan($rows, $i, $j); } // Ignore cells that are merged vertically with previous rows if ($cellVMerge !== 'continue') { @@ -139,4 +128,62 @@ private function getTableStyle($tableStyle = null) return $style . '"'; } + + /** + * Calculates cell rowspan. + * + * @param \PhpOffice\PhpWord\Element\Row[] $rows + * @param int $rowIndex + * @param int $colIndex + * + * @return int + */ + private function calculateCellRowSpan($rows, $rowIndex, $colIndex) + { + $currentRow = $rows[$rowIndex]; + $currentRowCells = $currentRow->getCells(); + $shiftedColIndex = 0; + + foreach ($currentRowCells as $cell) { + if ($cell === $currentRowCells[$colIndex]) { + break; + } + + $colSpan = 1; + + if ($cell->getStyle()->getGridSpan() !== null) { + $colSpan = $cell->getStyle()->getGridSpan(); + } + + $shiftedColIndex += $colSpan; + } + + $rowCount = count($rows); + $rowSpan = 1; + + for ($i = $rowIndex + 1; $i < $rowCount; $i++) { + $rowCells = $rows[$i]->getCells(); + $colIndex = 0; + + foreach ($rowCells as $cell) { + if ($colIndex === $shiftedColIndex) { + if ($cell->getStyle()->getVMerge() === 'continue') { + $rowSpan++; + } + + break; + } + + $colSpan = 1; + + if ($cell->getStyle()->getGridSpan() !== null) { + $colSpan = $cell->getStyle()->getGridSpan(); + } + + $colIndex += $colSpan; + } + } + + return $rowSpan; + } } diff --git a/tests/PhpWord/Writer/HTML/ElementTest.php b/tests/PhpWord/Writer/HTML/ElementTest.php index 4eb92fe557..8f9e4d2ec0 100644 --- a/tests/PhpWord/Writer/HTML/ElementTest.php +++ b/tests/PhpWord/Writer/HTML/ElementTest.php @@ -136,6 +136,43 @@ public function testWriteRowSpan() $this->assertEquals(1, $xpath->query('/html/body/table/tr[2]/td')->length); } + /** + * Tests writing table with rowspan and colspan + */ + public function testWriteRowSpanAndColSpan() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $table = $section->addTable(); + + $row1 = $table->addRow(); + $row1->addCell(500)->addText('A'); + $row1->addCell(1000, array('gridSpan' => 2))->addText('B'); + $row1->addCell(500, array('vMerge' => 'restart'))->addText('C'); + + $row2 = $table->addRow(); + $row2->addCell(1500, array('gridSpan' => 3))->addText('D'); + $row2->addCell(null, array('vMerge' => 'continue')); + + $row3 = $table->addRow(); + $row3->addCell(500)->addText('E'); + $row3->addCell(500)->addText('F'); + $row3->addCell(500)->addText('G'); + $row3->addCell(null, array('vMerge' => 'continue')); + + $dom = $this->getAsHTML($phpWord); + $xpath = new \DOMXPath($dom); + + $this->assertEquals(3, $xpath->query('/html/body/table/tr[1]/td')->length); + $this->assertEquals('2', $xpath->query('/html/body/table/tr[1]/td[2]')->item(0)->attributes->getNamedItem('colspan')->textContent); + $this->assertEquals('3', $xpath->query('/html/body/table/tr[1]/td[3]')->item(0)->attributes->getNamedItem('rowspan')->textContent); + + $this->assertEquals(1, $xpath->query('/html/body/table/tr[2]/td')->length); + $this->assertEquals('3', $xpath->query('/html/body/table/tr[2]/td[1]')->item(0)->attributes->getNamedItem('colspan')->textContent); + + $this->assertEquals(3, $xpath->query('/html/body/table/tr[3]/td')->length); + } + private function getAsHTML(PhpWord $phpWord) { $htmlWriter = new HTML($phpWord);