Skip to content

Commit 590f08c

Browse files
committed
Update cloneRow function to support vertical spanned cells.
1 parent 21589ff commit 590f08c

File tree

3 files changed

+86
-12
lines changed

3 files changed

+86
-12
lines changed

Classes/PHPWord/Template.php

Lines changed: 67 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -121,35 +121,90 @@ public function getVariables()
121121
preg_match_all('/\$\{(.*?)}/i', $this->_documentXML, $matches);
122122
return $matches[1];
123123
}
124-
124+
125+
/**
126+
* Find the start position of the nearest table row before $offset
127+
*
128+
* @param mixed $offset
129+
*/
130+
private function _findRowStart($offset) {
131+
return strrpos($this->_documentXML, "<w:tr ", ((strlen($this->_documentXML) - $offset) * -1));
132+
}
133+
134+
/**
135+
* Find the end position of the nearest table row after $offset
136+
*
137+
* @param mixed $offset
138+
*/
139+
private function _findRowEnd($offset) {
140+
return strpos($this->_documentXML, "</w:tr>", $offset) + 7;
141+
}
142+
143+
/**
144+
* Get a slice of a string
145+
*
146+
* @param mixed $offset
147+
*/
148+
private function _getSlice($startPosition, $endPosition = 0) {
149+
if (!$endPosition) {
150+
$endPosition = strlen($this->_documentXML);
151+
}
152+
return substr($this->_documentXML, $startPosition, ($endPosition - $startPosition));
153+
}
154+
125155
/**
126156
* Clone a table row in a template document
127157
*
128158
* @param mixed $search
129159
* @param mixed $numberOfClones
130160
*/
131-
public function cloneRow($search, $numberOfClones) {
161+
public function cloneRow($search, $numberOfClones) {
132162
if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
133163
$search = '${'.$search.'}';
134164
}
135165

136-
$tagPos = strpos($this->_documentXML, $search);
166+
$tagPos = strpos($this->_documentXML, $search);
137167
if (!$tagPos) {
138168
trigger_error("Can not clone row, template variable not found or variable contains markup.");
139169
return false;
140170
}
141-
$rowStartPos = strrpos($this->_documentXML, "<w:tr ", ((strlen($this->_documentXML) - $tagPos) * -1));
142-
$rowEndPos = strpos($this->_documentXML, "</w:tr>", $tagPos) + 7;
143-
144-
$result = substr($this->_documentXML, 0, $rowStartPos);
145-
$xmlRow = substr($this->_documentXML, $rowStartPos, ($rowEndPos - $rowStartPos));
171+
172+
$rowStart = $this->_findRowStart($tagPos);
173+
$rowEnd = $this->_findRowEnd($tagPos);
174+
$xmlRow = $this->_getSlice($rowStart, $rowEnd);
175+
176+
// Check if there's a cell spanning multiple rows.
177+
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
178+
$extraRowStart = $rowEnd;
179+
$extraRowEnd = $rowEnd;
180+
while(true) {
181+
$extraRowStart = $this->_findRowStart($extraRowEnd + 1);
182+
$extraRowEnd = $this->_findRowEnd($extraRowEnd + 1);
183+
184+
// If extraRowEnd is lower then 7, there was no next row found.
185+
if ($extraRowEnd < 7) {
186+
break;
187+
}
188+
189+
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
190+
$tmpXmlRow = $this->_getSlice($extraRowStart, $extraRowEnd);
191+
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) && !preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
192+
break;
193+
}
194+
// This row was a spanned row, update $rowEnd and search for the next row.
195+
$rowEnd = $extraRowEnd;
196+
}
197+
$xmlRow = $this->_getSlice($rowStart, $rowEnd);
198+
}
199+
200+
$result = $this->_getSlice(0, $rowStart);
146201
for ($i = 1; $i <= $numberOfClones; $i++) {
147-
$result .= preg_replace('/\$\{(.*?)\}/','\${\\1#'.$i.'}', $xmlRow);
202+
$result .= preg_replace('/\$\{(.*?)\}/','\${\\1#'.$i.'}', $xmlRow);
148203
}
149-
$result .= substr($this->_documentXML, $rowEndPos);
150-
204+
$result .= $this->_getSlice($rowEnd);
205+
151206
$this->_documentXML = $result;
152-
}
207+
}
153208

154209
/**
155210
* Save Template

samples/Sample_03_TemplateCloneRow.docx

100644100755
396 Bytes
Binary file not shown.

samples/Sample_03_TemplateCloneRow.php

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
$document = $PHPWord->loadTemplate('Sample_03_TemplateCloneRow.docx');
77

8+
// Simple table
89
$document->cloneRow('rowValue', 10);
910

1011
$document->setValue('rowValue#1', 'Sun');
@@ -32,4 +33,22 @@
3233
$document->setValue('weekday', date('l'));
3334
$document->setValue('time', date('H:i'));
3435

36+
// Table with a spanned cell
37+
$document->cloneRow('userId', 3);
38+
39+
$document->setValue('userId#1', '1');
40+
$document->setValue('userFirstName#1', 'James');
41+
$document->setValue('userName#1', 'Taylor');
42+
$document->setValue('userPhone#1', '+1 428 889 773');
43+
44+
$document->setValue('userId#2', '2');
45+
$document->setValue('userFirstName#2', 'Robert');
46+
$document->setValue('userName#2', 'Bell');
47+
$document->setValue('userPhone#2', '+1 428 889 774');
48+
49+
$document->setValue('userId#3', '3');
50+
$document->setValue('userFirstName#3', 'Michael');
51+
$document->setValue('userName#3', 'Ray');
52+
$document->setValue('userPhone#3', '+1 428 889 775');
53+
3554
$document->save('Sample_03_TemplateCloneRow_result.docx');

0 commit comments

Comments
 (0)