Merge pull request #2104 from simivar/feature/delete-row
Introduce deleteRow() method for TemplateProcessor
This commit is contained in:
commit
14c6e6f370
@ -759,6 +759,70 @@ class TemplateProcessor
|
||||
$this->tempDocumentMainPart = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a table row in a template document.
|
||||
*/
|
||||
public function deleteRow(string $search): void
|
||||
{
|
||||
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 = $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 +1143,39 @@ class TemplateProcessor
|
||||
return '[Content_Types].xml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the start position of the nearest table before $offset.
|
||||
*/
|
||||
protected function findTableStart(int $offset): int
|
||||
{
|
||||
$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.
|
||||
*/
|
||||
protected function findTableEnd(int $offset): int
|
||||
{
|
||||
return strpos($this->tempDocumentMainPart, '</w:tbl>', $offset) + 7;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the start position of the nearest table row before $offset.
|
||||
*
|
||||
|
||||
@ -182,6 +182,32 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase
|
||||
@$templateProcessor->applyXslStyleSheet($xslDomDocument);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::deleteRow
|
||||
* @covers ::getVariables
|
||||
* @covers ::saveAs
|
||||
*/
|
||||
public function testDeleteRow(): void
|
||||
{
|
||||
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/delete-row.docx');
|
||||
|
||||
self::assertEquals(
|
||||
['deleteMe', 'deleteMeToo'],
|
||||
$templateProcessor->getVariables()
|
||||
);
|
||||
|
||||
$docName = 'delete-row-test-result.docx';
|
||||
$templateProcessor->deleteRow('deleteMe');
|
||||
self::assertEquals(
|
||||
[],
|
||||
$templateProcessor->getVariables()
|
||||
);
|
||||
$templateProcessor->saveAs($docName);
|
||||
$docFound = file_exists($docName);
|
||||
unlink($docName);
|
||||
self::assertTrue($docFound);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::cloneRow
|
||||
* @covers ::saveAs
|
||||
|
||||
BIN
tests/PhpWordTests/_files/templates/delete-row.docx
Normal file
BIN
tests/PhpWordTests/_files/templates/delete-row.docx
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user