From 590f08c42bd6e61bc83f0dfef77e950e16a8d9b3 Mon Sep 17 00:00:00 2001 From: Jeroen Moors Date: Fri, 10 Jan 2014 14:26:02 +0100 Subject: [PATCH] Update cloneRow function to support vertical spanned cells. --- Classes/PHPWord/Template.php | 79 ++++++++++++++++++++---- samples/Sample_03_TemplateCloneRow.docx | Bin 17543 -> 17939 bytes samples/Sample_03_TemplateCloneRow.php | 19 ++++++ 3 files changed, 86 insertions(+), 12 deletions(-) mode change 100644 => 100755 samples/Sample_03_TemplateCloneRow.docx 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 909f27abfef13543ebc734fed015c7ca2978456e..25a8c418b6795873777334de254fae7081e1fa14 GIT binary patch delta 5084 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 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');