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');