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;
|
$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.
|
* 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';
|
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.
|
* 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);
|
@$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 ::cloneRow
|
||||||
* @covers ::saveAs
|
* @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