Skip to content

Commit 3ed2588

Browse files
committed
Merge pull request #88 from jeroenmoors/develop
Clone table rows on the fly when using a template document
2 parents 07a6794 + 43a00b1 commit 3ed2588

File tree

3 files changed

+147
-0
lines changed

3 files changed

+147
-0
lines changed

Classes/PHPWord/Template.php

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,99 @@ public function getVariables()
152152
return $matches[1];
153153
}
154154

155+
/**
156+
* Find the start position of the nearest table row before $offset
157+
*
158+
* @param mixed $offset
159+
*/
160+
private function _findRowStart($offset) {
161+
$rowStart = strrpos($this->_documentXML, "<w:tr ", ((strlen($this->_documentXML) - $offset) * -1));
162+
if (!$rowStart) {
163+
$rowStart = strrpos($this->_documentXML, "<w:tr>", ((strlen($this->_documentXML) - $offset) * -1));
164+
}
165+
if (!$rowStart) {
166+
trigger_error("Can not find the start position of the row to clone.");
167+
return false;
168+
}
169+
return $rowStart;
170+
}
171+
172+
/**
173+
* Find the end position of the nearest table row after $offset
174+
*
175+
* @param mixed $offset
176+
*/
177+
private function _findRowEnd($offset) {
178+
$rowEnd = strpos($this->_documentXML, "</w:tr>", $offset) + 7;
179+
return $rowEnd;
180+
}
181+
182+
/**
183+
* Get a slice of a string
184+
*
185+
* @param mixed $offset
186+
*/
187+
private function _getSlice($startPosition, $endPosition = 0) {
188+
if (!$endPosition) {
189+
$endPosition = strlen($this->_documentXML);
190+
}
191+
return substr($this->_documentXML, $startPosition, ($endPosition - $startPosition));
192+
}
193+
194+
/**
195+
* Clone a table row in a template document
196+
*
197+
* @param mixed $search
198+
* @param mixed $numberOfClones
199+
*/
200+
public function cloneRow($search, $numberOfClones) {
201+
if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
202+
$search = '${'.$search.'}';
203+
}
204+
205+
$tagPos = strpos($this->_documentXML, $search);
206+
if (!$tagPos) {
207+
trigger_error("Can not clone row, template variable not found or variable contains markup.");
208+
return false;
209+
}
210+
211+
$rowStart = $this->_findRowStart($tagPos);
212+
$rowEnd = $this->_findRowEnd($tagPos);
213+
$xmlRow = $this->_getSlice($rowStart, $rowEnd);
214+
215+
// Check if there's a cell spanning multiple rows.
216+
if (preg_match('#<w:vMerge w:val="restart"/>#', $xmlRow)) {
217+
$extraRowStart = $rowEnd;
218+
$extraRowEnd = $rowEnd;
219+
while(true) {
220+
$extraRowStart = $this->_findRowStart($extraRowEnd + 1);
221+
$extraRowEnd = $this->_findRowEnd($extraRowEnd + 1);
222+
223+
// If extraRowEnd is lower then 7, there was no next row found.
224+
if ($extraRowEnd < 7) {
225+
break;
226+
}
227+
228+
// If tmpXmlRow doesn't contain continue, this row is no longer part of the spanned row.
229+
$tmpXmlRow = $this->_getSlice($extraRowStart, $extraRowEnd);
230+
if (!preg_match('#<w:vMerge/>#', $tmpXmlRow) && !preg_match('#<w:vMerge w:val="continue" />#', $tmpXmlRow)) {
231+
break;
232+
}
233+
// This row was a spanned row, update $rowEnd and search for the next row.
234+
$rowEnd = $extraRowEnd;
235+
}
236+
$xmlRow = $this->_getSlice($rowStart, $rowEnd);
237+
}
238+
239+
$result = $this->_getSlice(0, $rowStart);
240+
for ($i = 1; $i <= $numberOfClones; $i++) {
241+
$result .= preg_replace('/\$\{(.*?)\}/','\${\\1#'.$i.'}', $xmlRow);
242+
}
243+
$result .= $this->_getSlice($rowEnd);
244+
245+
$this->_documentXML = $result;
246+
}
247+
155248
/**
156249
* Save Template
157250
*
17.5 KB
Binary file not shown.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
require_once '../Classes/PHPWord.php';
3+
4+
$PHPWord = new PHPWord();
5+
6+
$document = $PHPWord->loadTemplate('Sample_07_TemplateCloneRow.docx');
7+
8+
// Simple table
9+
$document->cloneRow('rowValue', 10);
10+
11+
$document->setValue('rowValue#1', 'Sun');
12+
$document->setValue('rowValue#2', 'Mercury');
13+
$document->setValue('rowValue#3', 'Venus');
14+
$document->setValue('rowValue#4', 'Earth');
15+
$document->setValue('rowValue#5', 'Mars');
16+
$document->setValue('rowValue#6', 'Jupiter');
17+
$document->setValue('rowValue#7', 'Saturn');
18+
$document->setValue('rowValue#8', 'Uranus');
19+
$document->setValue('rowValue#9', 'Neptun');
20+
$document->setValue('rowValue#10', 'Pluto');
21+
22+
$document->setValue('rowNumber#1', '1');
23+
$document->setValue('rowNumber#2', '2');
24+
$document->setValue('rowNumber#3', '3');
25+
$document->setValue('rowNumber#4', '4');
26+
$document->setValue('rowNumber#5', '5');
27+
$document->setValue('rowNumber#6', '6');
28+
$document->setValue('rowNumber#7', '7');
29+
$document->setValue('rowNumber#8', '8');
30+
$document->setValue('rowNumber#9', '9');
31+
$document->setValue('rowNumber#10', '10');
32+
33+
$document->setValue('weekday', date('l'));
34+
$document->setValue('time', date('H:i'));
35+
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+
54+
$document->save('Sample_03_TemplateCloneRow_result.docx');

0 commit comments

Comments
 (0)