diff --git a/Classes/PHPWord/Template.php b/Classes/PHPWord/Template.php
index 3adb44a4..28608348 100755
--- a/Classes/PHPWord/Template.php
+++ b/Classes/PHPWord/Template.php
@@ -121,35 +121,90 @@ class PHPWord_Template
preg_match_all('/\$\{(.*?)}/i', $this->_documentXML, $matches);
return $matches[1];
}
-
+
+ /**
+ * Find the start position of the nearest table row before $offset
+ *
+ * @param mixed $offset
+ */
+ private function _findRowStart($offset) {
+ return strrpos($this->_documentXML, "_documentXML) - $offset) * -1));
+ }
+
+ /**
+ * Find the end position of the nearest table row after $offset
+ *
+ * @param mixed $offset
+ */
+ private function _findRowEnd($offset) {
+ return strpos($this->_documentXML, "", $offset) + 7;
+ }
+
+ /**
+ * Get a slice of a string
+ *
+ * @param mixed $offset
+ */
+ private function _getSlice($startPosition, $endPosition = 0) {
+ if (!$endPosition) {
+ $endPosition = strlen($this->_documentXML);
+ }
+ return substr($this->_documentXML, $startPosition, ($endPosition - $startPosition));
+ }
+
/**
* Clone a table row in a template document
*
* @param mixed $search
* @param mixed $numberOfClones
*/
- public function cloneRow($search, $numberOfClones) {
+ public function cloneRow($search, $numberOfClones) {
if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') {
$search = '${'.$search.'}';
}
- $tagPos = strpos($this->_documentXML, $search);
+ $tagPos = strpos($this->_documentXML, $search);
if (!$tagPos) {
trigger_error("Can not clone row, template variable not found or variable contains markup.");
return false;
}
- $rowStartPos = strrpos($this->_documentXML, "_documentXML) - $tagPos) * -1));
- $rowEndPos = strpos($this->_documentXML, "", $tagPos) + 7;
-
- $result = substr($this->_documentXML, 0, $rowStartPos);
- $xmlRow = substr($this->_documentXML, $rowStartPos, ($rowEndPos - $rowStartPos));
+
+ $rowStart = $this->_findRowStart($tagPos);
+ $rowEnd = $this->_findRowEnd($tagPos);
+ $xmlRow = $this->_getSlice($rowStart, $rowEnd);
+
+ // Check if there's a cell spanning multiple rows.
+ if (preg_match('##', $xmlRow)) {
+ $extraRowStart = $rowEnd;
+ $extraRowEnd = $rowEnd;
+ while(true) {
+ $extraRowStart = $this->_findRowStart($extraRowEnd + 1);
+ $extraRowEnd = $this->_findRowEnd($extraRowEnd + 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('##', $tmpXmlRow) && !preg_match('##', $tmpXmlRow)) {
+ break;
+ }
+ // This row was a spanned row, update $rowEnd and search for the next row.
+ $rowEnd = $extraRowEnd;
+ }
+ $xmlRow = $this->_getSlice($rowStart, $rowEnd);
+ }
+
+ $result = $this->_getSlice(0, $rowStart);
for ($i = 1; $i <= $numberOfClones; $i++) {
- $result .= preg_replace('/\$\{(.*?)\}/','\${\\1#'.$i.'}', $xmlRow);
+ $result .= preg_replace('/\$\{(.*?)\}/','\${\\1#'.$i.'}', $xmlRow);
}
- $result .= substr($this->_documentXML, $rowEndPos);
-
+ $result .= $this->_getSlice($rowEnd);
+
$this->_documentXML = $result;
- }
+ }
/**
* Save Template
diff --git a/samples/Sample_03_TemplateCloneRow.docx b/samples/Sample_03_TemplateCloneRow.docx
old mode 100644
new mode 100755
index 909f27ab..25a8c418
Binary files a/samples/Sample_03_TemplateCloneRow.docx and b/samples/Sample_03_TemplateCloneRow.docx differ
diff --git a/samples/Sample_03_TemplateCloneRow.php b/samples/Sample_03_TemplateCloneRow.php
index 4806d6b7..2bbe2d5c 100755
--- a/samples/Sample_03_TemplateCloneRow.php
+++ b/samples/Sample_03_TemplateCloneRow.php
@@ -5,6 +5,7 @@ $PHPWord = new PHPWord();
$document = $PHPWord->loadTemplate('Sample_03_TemplateCloneRow.docx');
+// Simple table
$document->cloneRow('rowValue', 10);
$document->setValue('rowValue#1', 'Sun');
@@ -32,4 +33,22 @@ $document->setValue('rowNumber#10', '10');
$document->setValue('weekday', date('l'));
$document->setValue('time', date('H:i'));
+// Table with a spanned cell
+$document->cloneRow('userId', 3);
+
+$document->setValue('userId#1', '1');
+$document->setValue('userFirstName#1', 'James');
+$document->setValue('userName#1', 'Taylor');
+$document->setValue('userPhone#1', '+1 428 889 773');
+
+$document->setValue('userId#2', '2');
+$document->setValue('userFirstName#2', 'Robert');
+$document->setValue('userName#2', 'Bell');
+$document->setValue('userPhone#2', '+1 428 889 774');
+
+$document->setValue('userId#3', '3');
+$document->setValue('userFirstName#3', 'Michael');
+$document->setValue('userName#3', 'Ray');
+$document->setValue('userPhone#3', '+1 428 889 775');
+
$document->save('Sample_03_TemplateCloneRow_result.docx');