Skip to content

Commit e75f002

Browse files
committed
Introduce deleteRow() method for TemplateProcessor
1 parent aca1078 commit e75f002

File tree

3 files changed

+135
-0
lines changed

3 files changed

+135
-0
lines changed

src/PhpWord/TemplateProcessor.php

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,77 @@ public function cloneRow($search, $numberOfClones)
766766
$this->tempDocumentMainPart = $result;
767767
}
768768

769+
/**
770+
* Delete a table row in a template document.
771+
*
772+
* @param string $search
773+
*
774+
* @return void
775+
*
776+
* @throws \PhpOffice\PhpWord\Exception\Exception
777+
*/
778+
public function deleteRow($search)
779+
{
780+
if ('${' !== substr($search, 0, 2) && '}' !== substr($search, -1)) {
781+
$search = '${' . $search . '}';
782+
}
783+
784+
$tagPos = strpos($this->tempDocumentMainPart, $search);
785+
if (!$tagPos) {
786+
throw new Exception(sprintf("Can not delete row %s, template variable not found or variable contains markup.", $search));
787+
}
788+
789+
$tableStart = $this->findTableStart($tagPos);
790+
$tableEnd = $this->findTableEnd($tagPos);
791+
$xmlTable = $this->getSlice($tableStart, $tableEnd);
792+
793+
if (substr_count($xmlTable, '<w:tr') === 1) {
794+
$this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);
795+
796+
return;
797+
}
798+
799+
$rowStart = $this->findRowStart($tagPos);
800+
$rowEnd = $this->findRowEnd($tagPos);
801+
$xmlRow = $this->getSlice($rowStart, $rowEnd);
802+
803+
$this->tempDocumentMainPart = $this->getSlice(0, $rowStart) . $this->getSlice($rowEnd);
804+
805+
// Check if there's a cell spanning multiple rows.
806+
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
807+
// $extraRowStart = $rowEnd;
808+
$extraRowStart = $rowStart;
809+
while (true) {
810+
$extraRowStart = $this->findRowStart($extraRowStart + 1);
811+
$extraRowEnd = $this->findRowEnd($extraRowStart + 1);
812+
813+
// If extraRowEnd is lower then 7, there was no next row found.
814+
if ($extraRowEnd < 7) {
815+
break;
816+
}
817+
818+
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
819+
$tmpXmlRow = $this->getSlice($extraRowStart, $extraRowEnd);
820+
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) &&
821+
!preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)
822+
) {
823+
break;
824+
}
825+
826+
$tableStart = $this->findTableStart($extraRowEnd + 1);
827+
$tableEnd = $this->findTableEnd($extraRowEnd + 1);
828+
$xmlTable = $this->getSlice($tableStart, $tableEnd);
829+
if (substr_count($xmlTable, '<w:tr') === 1) {
830+
$this->tempDocumentMainPart = $this->getSlice(0, $tableStart) . $this->getSlice($tableEnd);
831+
832+
return;
833+
}
834+
835+
$this->tempDocumentMainPart = $this->getSlice(0, $extraRowStart) . $this->getSlice($extraRowEnd);
836+
}
837+
}
838+
}
839+
769840
/**
770841
* Clones a table row and populates it's values from a two-dimensional array in a template document.
771842
*
@@ -1088,6 +1159,43 @@ protected function getDocumentContentTypesName()
10881159
return '[Content_Types].xml';
10891160
}
10901161

1162+
/**
1163+
* Find the start position of the nearest table before $offset.
1164+
*
1165+
* @param integer $offset
1166+
*
1167+
* @return integer
1168+
*
1169+
* @throws \PhpOffice\PhpWord\Exception\Exception
1170+
*/
1171+
protected function findTableStart($offset)
1172+
{
1173+
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tbl ',
1174+
((strlen($this->tempDocumentMainPart) - $offset) * -1));
1175+
1176+
if (!$rowStart) {
1177+
$rowStart = strrpos($this->tempDocumentMainPart, '<w:tbl>',
1178+
((strlen($this->tempDocumentMainPart) - $offset) * -1));
1179+
}
1180+
if (!$rowStart) {
1181+
throw new Exception('Can not find the start position of the table.');
1182+
}
1183+
1184+
return $rowStart;
1185+
}
1186+
1187+
/**
1188+
* Find the end position of the nearest table row after $offset.
1189+
*
1190+
* @param integer $offset
1191+
*
1192+
* @return integer
1193+
*/
1194+
protected function findTableEnd($offset)
1195+
{
1196+
return strpos($this->tempDocumentMainPart, '</w:tbl>', $offset) + 7;
1197+
}
1198+
10911199
/**
10921200
* Find the start position of the nearest table row before $offset.
10931201
*

tests/PhpWord/TemplateProcessorTest.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,33 @@ final public function testXslStyleSheetCanNotBeAppliedOnFailureOfLoadingXmlFromT
179179
@$templateProcessor->applyXslStyleSheet($xslDomDocument);
180180
}
181181

182+
/**
183+
* @covers ::getVariables
184+
* @covers ::deleteRow
185+
* @covers ::saveAs
186+
* @test
187+
*/
188+
public function testDeleteRow()
189+
{
190+
$templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/delete-row.docx');
191+
192+
$this->assertEquals(
193+
array('deleteMe', 'deleteMeToo'),
194+
$templateProcessor->getVariables()
195+
);
196+
197+
$docName = 'delete-row-test-result.docx';
198+
$templateProcessor->deleteRow('deleteMe');
199+
$this->assertEquals(
200+
array(),
201+
$templateProcessor->getVariables()
202+
);
203+
$templateProcessor->saveAs($docName);
204+
$docFound = file_exists($docName);
205+
unlink($docName);
206+
$this->assertTrue($docFound);
207+
}
208+
182209
/**
183210
* @covers ::setValue
184211
* @covers ::cloneRow
12.9 KB
Binary file not shown.

0 commit comments

Comments
 (0)