Introduce deleteRow() method for TemplateProcessor

This commit is contained in:
simivar 2021-06-23 12:07:45 +02:00
parent fab9966b8c
commit 91504dfddc
3 changed files with 135 additions and 0 deletions

View File

@ -759,6 +759,77 @@ class TemplateProcessor
$this->tempDocumentMainPart = $result;
}
/**
* Delete a table row in a template document.
*
* @param string $search
*
* @return void
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function deleteRow($search)
{
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
$search = '${' . $search . '}';
}
$tagPos = strpos($this->tempDocumentMainPart, $search);
if (!$tagPos) {
throw new Exception(sprintf("Can not delete row %s, template variable not found or variable contains markup.", $search));
}
$tableStart = $this->findTableStart($tagPos);
$tableEnd = $this->findTableEnd($tagPos);
$xmlTable = $this->getSlice($tableStart, $tableEnd);
if (substr_count($xmlTable, '<w:tr') === 1) {
$this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);
return;
}
$rowStart = $this->findRowStart($tagPos);
$rowEnd = $this->findRowEnd($tagPos);
$xmlRow = $this->getSlice($rowStart, $rowEnd);
$this->tempDocumentMainPart = $this->getSlice(0, $rowStart) . $this->getSlice($rowEnd);
// Check if there's a cell spanning multiple rows.
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
// $extraRowStart = $rowEnd;
$extraRowStart = $rowStart;
while (true) {
$extraRowStart = $this->findRowStart($extraRowStart + 1);
$extraRowEnd = $this->findRowEnd($extraRowStart + 1);
// If extraRowEnd is lower then 7, there was no next row found.
if ($extraRowEnd < 7) {
break;
}
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)
) {
break;
}
$tableStart = $this->findTableStart($extraRowEnd + 1);
$tableEnd = $this->findTableEnd($extraRowEnd + 1);
$xmlTable = $this->getSlice($tableStart, $tableEnd);
if (substr_count($xmlTable, '<w:tr') === 1) {
$this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);
return;
}
$this->tempDocumentMainPart = $this->getSlice(0, $extraRowStart) . $this->getSlice($extraRowEnd);
}
}
}
/**
* Clones a table row and populates it's values from a two-dimensional array in a template document.
*
@ -1079,6 +1150,43 @@ class TemplateProcessor
return '[Content_Types].xml';
}
/**
* Find the start position of the nearest table before $offset.
*
* @param integer $offset
*
* @return integer
*
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
protected function findTableStart($offset)
{
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tbl ',
((strlen($this->tempDocumentMainPart) - $offset) * -1));
if (!$rowStart) {
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tbl>',
((strlen($this->tempDocumentMainPart) - $offset) * -1));
}
if (!$rowStart) {
throw new Exception('Can not find the start position of the table.');
}
return $rowStart;
}
/**
* Find the end position of the nearest table row after $offset.
*
* @param integer $offset
*
* @return integer
*/
protected function findTableEnd($offset)
{
return strpos($this->tempDocumentMainPart, '</w:tbl>', $offset) + 7;
}
/**
* Find the start position of the nearest table row before $offset.
*

View File

@ -182,6 +182,33 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
@$templateProcessor->applyXslStyleSheet($xslDomDocument);
}
/**
* @covers ::getVariables
* @covers ::deleteRow
* @covers ::saveAs
* @test
*/
public function testDeleteRow()
{
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/delete-row.docx');
$this->assertEquals(
array('deleteMe', 'deleteMeToo'),
$templateProcessor->getVariables()
);
$docName = 'delete-row-test-result.docx';
$templateProcessor->deleteRow('deleteMe');
$this->assertEquals(
array(),
$templateProcessor->getVariables()
);
$templateProcessor->saveAs($docName);
$docFound = file_exists($docName);
unlink($docName);
$this->assertTrue($docFound);
}
/**
* @covers ::cloneRow
* @covers ::saveAs

Binary file not shown.