diff --git a/Classes/PHPWord/Template.php b/Classes/PHPWord/Template.php index a1b26714..edf0de75 100755 --- a/Classes/PHPWord/Template.php +++ b/Classes/PHPWord/Template.php @@ -152,6 +152,99 @@ class PHPWord_Template return $matches[1]; } + /** + * Find the start position of the nearest table row before $offset + * + * @param mixed $offset + */ + private function _findRowStart($offset) { + $rowStart = strrpos($this->_documentXML, "_documentXML) - $offset) * -1)); + if (!$rowStart) { + $rowStart = strrpos($this->_documentXML, "", ((strlen($this->_documentXML) - $offset) * -1)); + } + if (!$rowStart) { + trigger_error("Can not find the start position of the row to clone."); + return false; + } + return $rowStart; + } + + /** + * Find the end position of the nearest table row after $offset + * + * @param mixed $offset + */ + private function _findRowEnd($offset) { + $rowEnd = strpos($this->_documentXML, "", $offset) + 7; + return $rowEnd; + } + + /** + * 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) { + if(substr($search, 0, 2) !== '${' && substr($search, -1) !== '}') { + $search = '${'.$search.'}'; + } + + $tagPos = strpos($this->_documentXML, $search); + if (!$tagPos) { + trigger_error("Can not clone row, template variable not found or variable contains markup."); + return false; + } + + $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 .= $this->_getSlice($rowEnd); + + $this->_documentXML = $result; + } + /** * Save Template * diff --git a/samples/Sample_07_TemplateCloneRow.docx b/samples/Sample_07_TemplateCloneRow.docx new file mode 100755 index 00000000..25a8c418 Binary files /dev/null and b/samples/Sample_07_TemplateCloneRow.docx differ diff --git a/samples/Sample_07_TemplateCloneRow.php b/samples/Sample_07_TemplateCloneRow.php new file mode 100755 index 00000000..1fa5bd56 --- /dev/null +++ b/samples/Sample_07_TemplateCloneRow.php @@ -0,0 +1,54 @@ +loadTemplate('Sample_07_TemplateCloneRow.docx'); + +// Simple table +$document->cloneRow('rowValue', 10); + +$document->setValue('rowValue#1', 'Sun'); +$document->setValue('rowValue#2', 'Mercury'); +$document->setValue('rowValue#3', 'Venus'); +$document->setValue('rowValue#4', 'Earth'); +$document->setValue('rowValue#5', 'Mars'); +$document->setValue('rowValue#6', 'Jupiter'); +$document->setValue('rowValue#7', 'Saturn'); +$document->setValue('rowValue#8', 'Uranus'); +$document->setValue('rowValue#9', 'Neptun'); +$document->setValue('rowValue#10', 'Pluto'); + +$document->setValue('rowNumber#1', '1'); +$document->setValue('rowNumber#2', '2'); +$document->setValue('rowNumber#3', '3'); +$document->setValue('rowNumber#4', '4'); +$document->setValue('rowNumber#5', '5'); +$document->setValue('rowNumber#6', '6'); +$document->setValue('rowNumber#7', '7'); +$document->setValue('rowNumber#8', '8'); +$document->setValue('rowNumber#9', '9'); +$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');