From 7553b4cc796294eca3ec4067b6c1b0396e12ab54 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 14:26:05 +0300 Subject: [PATCH 01/35] setImageValue() + fix adding files via ZipArchive --- src/PhpWord/Shared/ZipArchive.php | 14 +- src/PhpWord/TemplateProcessor.php | 220 ++++++++++++++++++++++-- tests/PhpWord/TemplateProcessorTest.php | 50 ++++++ 3 files changed, 268 insertions(+), 16 deletions(-) diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php index 4dc4af4e14..ce233c635d 100644 --- a/src/PhpWord/Shared/ZipArchive.php +++ b/src/PhpWord/Shared/ZipArchive.php @@ -129,6 +129,7 @@ public function open($filename, $flags = null) { $result = true; $this->filename = $filename; + $this->tempDir = Settings::getTempDir(); if (!$this->usePclzip) { $zip = new \ZipArchive(); @@ -139,7 +140,6 @@ public function open($filename, $flags = null) $this->numFiles = $zip->numFiles; } else { $zip = new \PclZip($this->filename); - $this->tempDir = Settings::getTempDir(); $this->numFiles = count($zip->listContent()); } $this->zip = $zip; @@ -232,10 +232,10 @@ public function pclzipAddFile($filename, $localname = null) // To Rename the file while adding it to the zip we // need to create a temp file with the correct name - $tempFile = false; + $tempFile = false; if ($filenameParts['basename'] != $localnameParts['basename']) { $tempFile = true; // temp file created - $temppath = $this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']; + $temppath = $this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']; copy($filename, $temppath); $filename = $temppath; $filenameParts = pathinfo($temppath); @@ -244,7 +244,13 @@ public function pclzipAddFile($filename, $localname = null) $pathRemoved = $filenameParts['dirname']; $pathAdded = $localnameParts['dirname']; - $res = $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $pathRemoved, PCLZIP_OPT_ADD_PATH, $pathAdded); + if (!$this->usePclzip) { + $pathAdded = $pathAdded . '/' . ltrim(str_replace('\\', '/', substr($filename, strlen($pathRemoved))), '/'); + //$res = $zip->addFile($filename, $pathAdded); + $res = $zip->addFromString($pathAdded, file_get_contents($filename)); // addFile can't use subfolders in some cases + } else { + $res = $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $pathRemoved, PCLZIP_OPT_ADD_PATH, $pathAdded); + } if ($tempFile) { // Remove temp file, if created diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index e7a8d039c4..87b211529f 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -62,6 +62,27 @@ class TemplateProcessor */ protected $tempDocumentFooters = array(); + /** + * Document relations (in XML format) of the temporary document. + * + * @var string[] + */ + protected $tempDocumentRelations = array(); + + /** + * Document content types (in XML format) of the temporary document. + * + * @var string + */ + protected $tempDocumentContentTypes = ""; + + /** + * new inserted images list + * + * @var string[] + */ + protected $tempDocumentNewImages = array(); + /** * @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception. * @@ -88,21 +109,34 @@ public function __construct($documentTemplate) $this->zipClass->open($this->tempDocumentFilename); $index = 1; while (false !== $this->zipClass->locateName($this->getHeaderName($index))) { - $this->tempDocumentHeaders[$index] = $this->fixBrokenMacros( - $this->zipClass->getFromName($this->getHeaderName($index)) - ); + $this->tempDocumentHeaders[$index] = $this->readPartWithRels( $this->getHeaderName($index) ); $index++; } $index = 1; while (false !== $this->zipClass->locateName($this->getFooterName($index))) { - $this->tempDocumentFooters[$index] = $this->fixBrokenMacros( - $this->zipClass->getFromName($this->getFooterName($index)) - ); + $this->tempDocumentFooters[$index] = $this->readPartWithRels( $this->getFooterName($index) ); $index++; - } - $this->tempDocumentMainPart = $this->fixBrokenMacros($this->zipClass->getFromName($this->getMainPartName())); + } + + $this->tempDocumentMainPart = $this->readPartWithRels( $this->getMainPartName() ); + $this->tempDocumentContentTypes = $this->zipClass->getFromName($this->getDocumentContentTypesName()); } + /** + * @param string $fileName + * + * @return string + */ + protected function readPartWithRels($fileName) + { + $relsFileName = $this->getRelationsName($fileName); + $partRelations = $this->zipClass->getFromName($relsFileName); + if ($partRelations !== false) { + $this->tempDocumentRelations[$fileName] = $partRelations; + } + return $this->fixBrokenMacros( $this->zipClass->getFromName($fileName) ); + } + /** * @param string $xml * @param \XSLTProcessor $xsltProcessor @@ -236,6 +270,123 @@ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_ $this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit); } + /** + * @param mixed $search + * @param mixed $replace Path to image, or array("path" => xx, "width" => yy, "height" => zz) + * @param integer $limit + * + * @return void + */ + public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT) + { + // prepare $search_replace + if (!is_array($search)) + $search = array($search); + + $replaces_list = array(); + if (!is_array($replace) || isset($replace["path"])) { + $replaces_list[] = $replace; + } else { + $replaces_list = array_values($replace); + } + + $search_replace = array(); + foreach ($search as $searchIdx => $searchString) + $search_replace[$searchString] = isset($replaces_list[$searchIdx]) ? $replaces_list[$searchIdx] : $replaces_list[0]; + // + + // define templates + // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) + $imgTpl = ''; + $typeTpl = ''; + $relationTpl = ''; + $newRelationsTpl = ''."\n".''; + $newRelationsTypeTpl = ''; + $extTransform = array( + "jpg" => "jpeg", + "JPG" => "jpeg", + "png" => "png", + "PNG" => "png", + ); + // + + $searchParts = array( + $this->getMainPartName() => &$this->tempDocumentMainPart, + ); + foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) + $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; + foreach (array_keys($this->tempDocumentFooters) as $headerIndex) + $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; + + foreach ($searchParts as $partFileName => &$partContent) { + $partVariables = $this->getVariablesForPart($partContent); + + $partSearchReplaces = array(); + foreach ($search_replace as $search => $replace) { + if (!in_array($search, $partVariables)) { + continue; + } + + // get image path and size + $width = 115; + $height = 70; + if (is_array($replace) && isset($replace["path"])) { + $imgPath = $replace["path"]; + if (isset($replace["width"])) + $width = $replace["width"]; + if (isset($replace["height"])) + $height = $replace["height"]; + } + else + $imgPath = $replace; + + // get image index + $imgIndex = $this->getNextRelationsIndex($partFileName); + $rid = 'rId' . $imgIndex; + + // get image embed name + if (isset($this->tempDocumentNewImages[$imgPath])) { + $imgName = $this->tempDocumentNewImages[$imgPath]; + } else { + // transform extension + $imgExt = pathinfo($imgPath, PATHINFO_EXTENSION); + if (isset($extTransform)) + $imgExt = $extTransform[$imgExt]; + + // add image to document + $imgName = 'image' . $imgIndex . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; + $this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName); + $this->tempDocumentNewImages[$imgPath] = $imgName; + + // setup type for image + $xmlImageType = str_replace(['{IMG}', '{EXT}'], [$imgName, $imgExt], $typeTpl) ; + $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; + } + + $xmlImage = str_replace(['{RID}', '{WIDTH}', '{HEIGHT}'], [$rid, $width, $height], $imgTpl) ; + $xmlImageRelation = str_replace(['{RID}', '{IMG}'], [$rid, $imgName], $relationTpl); + + if (!isset($this->tempDocumentRelations[$partFileName])) + { + // create new relations file + $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; + // and add it to content types + $xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl); + $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; + } + + // add image to relations + $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; + + // collect prepared replaces + $partSearchReplaces["".self::ensureMacroCompleted($search).""] = $xmlImage; + } + + if ($partSearchReplaces) + $partContent = $this->setValueForPart(array_keys($partSearchReplaces), $partSearchReplaces, $partContent, $limit); + } + } + /** * Returns array of all variables in template. * @@ -399,22 +550,39 @@ public function deleteBlock($blockname) public function save() { foreach ($this->tempDocumentHeaders as $index => $xml) { - $this->zipClass->addFromString($this->getHeaderName($index), $xml); + $this->savePartWithRels( $this->getHeaderName($index), $xml ); } - $this->zipClass->addFromString($this->getMainPartName(), $this->tempDocumentMainPart); + $this->savePartWithRels( $this->getMainPartName(), $this->tempDocumentMainPart ); foreach ($this->tempDocumentFooters as $index => $xml) { - $this->zipClass->addFromString($this->getFooterName($index), $xml); + $this->savePartWithRels( $this->getFooterName($index), $xml ); } + $this->zipClass->addFromString($this->getDocumentContentTypesName(), $this->tempDocumentContentTypes); + // Close zip file if (false === $this->zipClass->close()) { throw new Exception('Could not close zip file.'); } return $this->tempDocumentFilename; - } + } + + /** + * @param string $fileName + * @param string $xml + * + * @return void + */ + protected function savePartWithRels($fileName, $xml) + { + $this->zipClass->addFromString($fileName, $xml); + if (isset($this->tempDocumentRelations[$fileName])) { + $relsFileName = $this->getRelationsName($fileName); + $this->zipClass->addFromString($relsFileName, $this->tempDocumentRelations[$fileName]); + } + } /** * Saves the result document to the user defined file. @@ -533,6 +701,34 @@ protected function getFooterName($index) return sprintf('word/footer%d.xml', $index); } + /** + * Get the name of the relations file for document part. + * + * @param string $docuemntPartName + * + * @return string + */ + protected function getRelationsName($documentPartName) + { + return 'word/_rels/'.pathinfo($documentPartName, PATHINFO_BASENAME).'.rels'; + } + + protected function getNextRelationsIndex($documentPartName) + { + if (isset($this->tempDocumentRelations[$documentPartName])) { + return substr_count($this->tempDocumentRelations[$documentPartName], 'assertTrue($docFound); } + /** + * @covers ::setImageValue + * @test + */ + public function testSetImageValue() + { + $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx'); + $imagePath = __DIR__ . '/_files/images/earth.jpg'; + + $variablesReplace = array( + 'headerValue' => $imagePath, + 'documentContent' => ["path" => $imagePath, "width" => 500, "height" => 500], + 'footerValue' => ["path" => $imagePath, "width" => 50, "height" => 50], + ); + $templateProcessor->setImageValue(array_keys($variablesReplace), $variablesReplace); + + $docName = 'header-footer-images-test-result.docx'; + $templateProcessor->saveAs($docName); + $docFound = file_exists($docName); + + if ($docFound) { + $expectedDocumentZip = new \ZipArchive(); + $expectedDocumentZip->open($docName); + $expectedContentTypesXml = $expectedDocumentZip->getFromName('[Content_Types].xml'); + $expectedDocumentRelationsXml = $expectedDocumentZip->getFromName('word/_rels/document.xml.rels'); + $expectedHeaderRelationsXml = $expectedDocumentZip->getFromName('word/_rels/header1.xml.rels'); + $expectedFooterRelationsXml = $expectedDocumentZip->getFromName('word/_rels/footer1.xml.rels'); + $expectedMainPartXml = $expectedDocumentZip->getFromName('word/document.xml'); + $expectedHeaderPartXml = $expectedDocumentZip->getFromName('word/header1.xml'); + $expectedFooterPartXml = $expectedDocumentZip->getFromName('word/footer1.xml'); + $expectedImage = $expectedDocumentZip->getFromName('word/media/image5_document.jpeg'); + if (false === $expectedDocumentZip->close()) { + throw new \Exception("Could not close zip file \"{$docName}\"."); + } + + $this->assertTrue(!empty($expectedImage), 'Embed image doesn\'t found.'); + $this->assertTrue(strpos($expectedContentTypesXml, '/word/media/image5_document.jpeg') > 0, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedContentTypesXml, '/word/_rels/header1.xml.rels') > 0, '[Content_Types].xml missed "/word/_rels/header1.xml.rels"'); + $this->assertTrue(strpos($expectedContentTypesXml, '/word/_rels/footer1.xml.rels') > 0, '[Content_Types].xml missed "/word/_rels/footer1.xml.rels"'); + $this->assertTrue(strpos($expectedMainPartXml, '${documentContent}') === false, 'word/document.xml has no image.'); + $this->assertTrue(strpos($expectedHeaderPartXml, '${headerValue}') === false, 'word/header1.xml has no image.'); + $this->assertTrue(strpos($expectedFooterPartXml, '${footerValue}') === false, 'word/footer1.xml has no image.'); + $this->assertTrue(strpos($expectedDocumentRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedHeaderRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedFooterRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); + + unlink($docName); + } + } + /** * @covers ::cloneBlock * @covers ::deleteBlock From 76bc755094274f3ec44603af4f58ddd65ca2b9aa Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 14:29:29 +0300 Subject: [PATCH 02/35] tab to spaces --- src/PhpWord/Shared/ZipArchive.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php index ce233c635d..37fa639345 100644 --- a/src/PhpWord/Shared/ZipArchive.php +++ b/src/PhpWord/Shared/ZipArchive.php @@ -232,10 +232,10 @@ public function pclzipAddFile($filename, $localname = null) // To Rename the file while adding it to the zip we // need to create a temp file with the correct name - $tempFile = false; + $tempFile = false; if ($filenameParts['basename'] != $localnameParts['basename']) { $tempFile = true; // temp file created - $temppath = $this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']; + $temppath = $this->tempDir . DIRECTORY_SEPARATOR . $localnameParts['basename']; copy($filename, $temppath); $filename = $temppath; $filenameParts = pathinfo($temppath); @@ -245,9 +245,9 @@ public function pclzipAddFile($filename, $localname = null) $pathAdded = $localnameParts['dirname']; if (!$this->usePclzip) { - $pathAdded = $pathAdded . '/' . ltrim(str_replace('\\', '/', substr($filename, strlen($pathRemoved))), '/'); - //$res = $zip->addFile($filename, $pathAdded); - $res = $zip->addFromString($pathAdded, file_get_contents($filename)); // addFile can't use subfolders in some cases + $pathAdded = $pathAdded . '/' . ltrim(str_replace('\\', '/', substr($filename, strlen($pathRemoved))), '/'); + //$res = $zip->addFile($filename, $pathAdded); + $res = $zip->addFromString($pathAdded, file_get_contents($filename)); // addFile can't use subfolders in some cases } else { $res = $zip->add($filename, PCLZIP_OPT_REMOVE_PATH, $pathRemoved, PCLZIP_OPT_ADD_PATH, $pathAdded); } From 8e8fd8ec62507c9ccb209b14b220cc63a7679ebf Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 14:45:51 +0300 Subject: [PATCH 03/35] tabs to spaces --- src/PhpWord/Shared/ZipArchive.php | 2 +- src/PhpWord/TemplateProcessor.php | 84 +++++++++++++++---------------- 2 files changed, 43 insertions(+), 43 deletions(-) diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php index 37fa639345..3249018408 100644 --- a/src/PhpWord/Shared/ZipArchive.php +++ b/src/PhpWord/Shared/ZipArchive.php @@ -129,7 +129,7 @@ public function open($filename, $flags = null) { $result = true; $this->filename = $filename; - $this->tempDir = Settings::getTempDir(); + $this->tempDir = Settings::getTempDir(); if (!$this->usePclzip) { $zip = new \ZipArchive(); diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 87b211529f..8c432a1c3b 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -82,7 +82,7 @@ class TemplateProcessor * @var string[] */ protected $tempDocumentNewImages = array(); - + /** * @since 0.12.0 Throws CreateTemporaryFileException and CopyFileException instead of Exception. * @@ -109,16 +109,16 @@ public function __construct($documentTemplate) $this->zipClass->open($this->tempDocumentFilename); $index = 1; while (false !== $this->zipClass->locateName($this->getHeaderName($index))) { - $this->tempDocumentHeaders[$index] = $this->readPartWithRels( $this->getHeaderName($index) ); + $this->tempDocumentHeaders[$index] = $this->readPartWithRels( $this->getHeaderName($index) ); $index++; } $index = 1; while (false !== $this->zipClass->locateName($this->getFooterName($index))) { - $this->tempDocumentFooters[$index] = $this->readPartWithRels( $this->getFooterName($index) ); + $this->tempDocumentFooters[$index] = $this->readPartWithRels( $this->getFooterName($index) ); $index++; - } - - $this->tempDocumentMainPart = $this->readPartWithRels( $this->getMainPartName() ); + } + + $this->tempDocumentMainPart = $this->readPartWithRels( $this->getMainPartName() ); $this->tempDocumentContentTypes = $this->zipClass->getFromName($this->getDocumentContentTypesName()); } @@ -127,15 +127,15 @@ public function __construct($documentTemplate) * * @return string */ - protected function readPartWithRels($fileName) - { + protected function readPartWithRels($fileName) + { $relsFileName = $this->getRelationsName($fileName); $partRelations = $this->zipClass->getFromName($relsFileName); if ($partRelations !== false) { $this->tempDocumentRelations[$fileName] = $partRelations; } - return $this->fixBrokenMacros( $this->zipClass->getFromName($fileName) ); - } + return $this->fixBrokenMacros( $this->zipClass->getFromName($fileName) ); + } /** * @param string $xml @@ -181,7 +181,7 @@ protected function transformXml($xml, $xsltProcessor) /** * Applies XSL style sheet to template's parts. - * + * * Note: since the method doesn't make any guess on logic of the provided XSL style sheet, * make sure that output is correctly escaped. Otherwise you may get broken document. * @@ -317,7 +317,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; foreach (array_keys($this->tempDocumentFooters) as $headerIndex) $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; - + foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); @@ -343,7 +343,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // get image index $imgIndex = $this->getNextRelationsIndex($partFileName); $rid = 'rId' . $imgIndex; - + // get image embed name if (isset($this->tempDocumentNewImages[$imgPath])) { $imgName = $this->tempDocumentNewImages[$imgPath]; @@ -352,15 +352,15 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $imgExt = pathinfo($imgPath, PATHINFO_EXTENSION); if (isset($extTransform)) $imgExt = $extTransform[$imgExt]; - + // add image to document $imgName = 'image' . $imgIndex . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; $this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName); $this->tempDocumentNewImages[$imgPath] = $imgName; - + // setup type for image $xmlImageType = str_replace(['{IMG}', '{EXT}'], [$imgName, $imgExt], $typeTpl) ; - $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; + $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; } $xmlImage = str_replace(['{RID}', '{WIDTH}', '{HEIGHT}'], [$rid, $width, $height], $imgTpl) ; @@ -372,12 +372,12 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; // and add it to content types $xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl); - $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; + $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; } // add image to relations - $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; - + $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; + // collect prepared replaces $partSearchReplaces["".self::ensureMacroCompleted($search).""] = $xmlImage; } @@ -550,13 +550,13 @@ public function deleteBlock($blockname) public function save() { foreach ($this->tempDocumentHeaders as $index => $xml) { - $this->savePartWithRels( $this->getHeaderName($index), $xml ); + $this->savePartWithRels( $this->getHeaderName($index), $xml ); } - $this->savePartWithRels( $this->getMainPartName(), $this->tempDocumentMainPart ); + $this->savePartWithRels( $this->getMainPartName(), $this->tempDocumentMainPart ); foreach ($this->tempDocumentFooters as $index => $xml) { - $this->savePartWithRels( $this->getFooterName($index), $xml ); + $this->savePartWithRels( $this->getFooterName($index), $xml ); } $this->zipClass->addFromString($this->getDocumentContentTypesName(), $this->tempDocumentContentTypes); @@ -567,22 +567,22 @@ public function save() } return $this->tempDocumentFilename; - } - + } + /** * @param string $fileName * @param string $xml * * @return void */ - protected function savePartWithRels($fileName, $xml) - { - $this->zipClass->addFromString($fileName, $xml); - if (isset($this->tempDocumentRelations[$fileName])) { - $relsFileName = $this->getRelationsName($fileName); - $this->zipClass->addFromString($relsFileName, $this->tempDocumentRelations[$fileName]); - } - } + protected function savePartWithRels($fileName, $xml) + { + $this->zipClass->addFromString($fileName, $xml); + if (isset($this->tempDocumentRelations[$fileName])) { + $relsFileName = $this->getRelationsName($fileName); + $this->zipClass->addFromString($relsFileName, $this->tempDocumentRelations[$fileName]); + } + } /** * Saves the result document to the user defined file. @@ -710,16 +710,16 @@ protected function getFooterName($index) */ protected function getRelationsName($documentPartName) { - return 'word/_rels/'.pathinfo($documentPartName, PATHINFO_BASENAME).'.rels'; - } - - protected function getNextRelationsIndex($documentPartName) - { - if (isset($this->tempDocumentRelations[$documentPartName])) { - return substr_count($this->tempDocumentRelations[$documentPartName], 'tempDocumentRelations[$documentPartName])) { + return substr_count($this->tempDocumentRelations[$documentPartName], ' Date: Wed, 25 Oct 2017 14:53:40 +0300 Subject: [PATCH 04/35] syntax fixes --- src/PhpWord/TemplateProcessor.php | 42 +++++++++++++++++-------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 8c432a1c3b..68c7d13b92 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -109,16 +109,16 @@ public function __construct($documentTemplate) $this->zipClass->open($this->tempDocumentFilename); $index = 1; while (false !== $this->zipClass->locateName($this->getHeaderName($index))) { - $this->tempDocumentHeaders[$index] = $this->readPartWithRels( $this->getHeaderName($index) ); + $this->tempDocumentHeaders[$index] = $this->readPartWithRels($this->getHeaderName($index)); $index++; } $index = 1; while (false !== $this->zipClass->locateName($this->getFooterName($index))) { - $this->tempDocumentFooters[$index] = $this->readPartWithRels( $this->getFooterName($index) ); + $this->tempDocumentFooters[$index] = $this->readPartWithRels($this->getFooterName($index)); $index++; } - $this->tempDocumentMainPart = $this->readPartWithRels( $this->getMainPartName() ); + $this->tempDocumentMainPart = $this->readPartWithRels($this->getMainPartName()); $this->tempDocumentContentTypes = $this->zipClass->getFromName($this->getDocumentContentTypesName()); } @@ -134,7 +134,7 @@ protected function readPartWithRels($fileName) if ($partRelations !== false) { $this->tempDocumentRelations[$fileName] = $partRelations; } - return $this->fixBrokenMacros( $this->zipClass->getFromName($fileName) ); + return $this->fixBrokenMacros($this->zipClass->getFromName($fileName)); } /** @@ -280,8 +280,9 @@ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT) { // prepare $search_replace - if (!is_array($search)) - $search = array($search); + if (!is_array($search)) { + $search = array($search); + } $replaces_list = array(); if (!is_array($replace) || isset($replace["path"])) { @@ -291,8 +292,9 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM } $search_replace = array(); - foreach ($search as $searchIdx => $searchString) - $search_replace[$searchString] = isset($replaces_list[$searchIdx]) ? $replaces_list[$searchIdx] : $replaces_list[0]; + foreach ($search as $searchIdx => $searchString) { + $search_replace[$searchString] = isset($replaces_list[$searchIdx]) ? $replaces_list[$searchIdx] : $replaces_list[0]; + } // // define templates @@ -313,10 +315,12 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $searchParts = array( $this->getMainPartName() => &$this->tempDocumentMainPart, ); - foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) - $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; - foreach (array_keys($this->tempDocumentFooters) as $headerIndex) - $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; + foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) { + $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; + } + foreach (array_keys($this->tempDocumentFooters) as $headerIndex) { + $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; + } foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); @@ -366,8 +370,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $xmlImage = str_replace(['{RID}', '{WIDTH}', '{HEIGHT}'], [$rid, $width, $height], $imgTpl) ; $xmlImageRelation = str_replace(['{RID}', '{IMG}'], [$rid, $imgName], $relationTpl); - if (!isset($this->tempDocumentRelations[$partFileName])) - { + if (!isset($this->tempDocumentRelations[$partFileName])) { // create new relations file $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; // and add it to content types @@ -382,8 +385,9 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $partSearchReplaces["".self::ensureMacroCompleted($search).""] = $xmlImage; } - if ($partSearchReplaces) - $partContent = $this->setValueForPart(array_keys($partSearchReplaces), $partSearchReplaces, $partContent, $limit); + if ($partSearchReplaces) { + $partContent = $this->setValueForPart(array_keys($partSearchReplaces), $partSearchReplaces, $partContent, $limit); + } } } @@ -550,13 +554,13 @@ public function deleteBlock($blockname) public function save() { foreach ($this->tempDocumentHeaders as $index => $xml) { - $this->savePartWithRels( $this->getHeaderName($index), $xml ); + $this->savePartWithRels($this->getHeaderName($index), $xml); } - $this->savePartWithRels( $this->getMainPartName(), $this->tempDocumentMainPart ); + $this->savePartWithRels($this->getMainPartName(), $this->tempDocumentMainPart); foreach ($this->tempDocumentFooters as $index => $xml) { - $this->savePartWithRels( $this->getFooterName($index), $xml ); + $this->savePartWithRels($this->getFooterName($index), $xml); } $this->zipClass->addFromString($this->getDocumentContentTypesName(), $this->tempDocumentContentTypes); From 879a9b43e90c94c1a5e3378c4bd3bec3e2cc81ec Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 15:13:06 +0300 Subject: [PATCH 05/35] syntax fixes --- src/PhpWord/TemplateProcessor.php | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 68c7d13b92..5b476fa50e 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -281,8 +281,8 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM { // prepare $search_replace if (!is_array($search)) { - $search = array($search); - } + $search = array($search); + } $replaces_list = array(); if (!is_array($replace) || isset($replace["path"])) { @@ -293,8 +293,8 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $search_replace = array(); foreach ($search as $searchIdx => $searchString) { - $search_replace[$searchString] = isset($replaces_list[$searchIdx]) ? $replaces_list[$searchIdx] : $replaces_list[0]; - } + $search_replace[$searchString] = isset($replaces_list[$searchIdx]) ? $replaces_list[$searchIdx] : $replaces_list[0]; + } // // define templates @@ -316,11 +316,11 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->getMainPartName() => &$this->tempDocumentMainPart, ); foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) { - $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; - } + $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; + } foreach (array_keys($this->tempDocumentFooters) as $headerIndex) { - $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; - } + $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; + } foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); @@ -336,10 +336,12 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $height = 70; if (is_array($replace) && isset($replace["path"])) { $imgPath = $replace["path"]; - if (isset($replace["width"])) - $width = $replace["width"]; - if (isset($replace["height"])) - $height = $replace["height"]; + if (isset($replace["width"])) { + $width = $replace["width"]; + } + if (isset($replace["height"])) { + $height = $replace["height"]; + } } else $imgPath = $replace; @@ -386,8 +388,8 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM } if ($partSearchReplaces) { - $partContent = $this->setValueForPart(array_keys($partSearchReplaces), $partSearchReplaces, $partContent, $limit); - } + $partContent = $this->setValueForPart(array_keys($partSearchReplaces), $partSearchReplaces, $partContent, $limit); + } } } From 8a9293ab4c9dadc66c0c90c5a1097e6cee2aa5b6 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 15:26:04 +0300 Subject: [PATCH 06/35] tabs to spaces --- src/PhpWord/TemplateProcessor.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 5b476fa50e..e304e25f35 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -337,11 +337,11 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM if (is_array($replace) && isset($replace["path"])) { $imgPath = $replace["path"]; if (isset($replace["width"])) { - $width = $replace["width"]; - } + $width = $replace["width"]; + } if (isset($replace["height"])) { - $height = $replace["height"]; - } + $height = $replace["height"]; + } } else $imgPath = $replace; From 869a3633452a1cf1ababd7d9a36c632f09cb2f8d Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 15:38:22 +0300 Subject: [PATCH 07/35] syntax fixes --- src/PhpWord/TemplateProcessor.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index e304e25f35..22c5c6b52d 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -342,9 +342,9 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM if (isset($replace["height"])) { $height = $replace["height"]; } - } - else + } else { $imgPath = $replace; + } // get image index $imgIndex = $this->getNextRelationsIndex($partFileName); @@ -356,8 +356,9 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM } else { // transform extension $imgExt = pathinfo($imgPath, PATHINFO_EXTENSION); - if (isset($extTransform)) + if (isset($extTransform)) { $imgExt = $extTransform[$imgExt]; + } // add image to document $imgName = 'image' . $imgIndex . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; From 5179074a5375f49aa069c320b8d79d5c3f8d9976 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 15:52:58 +0300 Subject: [PATCH 08/35] php 5.3 compatibility fix --- tests/PhpWord/TemplateProcessorTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 120c8765e1..0f6559a447 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -211,8 +211,8 @@ public function testSetImageValue() $variablesReplace = array( 'headerValue' => $imagePath, - 'documentContent' => ["path" => $imagePath, "width" => 500, "height" => 500], - 'footerValue' => ["path" => $imagePath, "width" => 50, "height" => 50], + 'documentContent' => array("path" => $imagePath, "width" => 500, "height" => 500), + 'footerValue' => array("path" => $imagePath, "width" => 50, "height" => 50), ); $templateProcessor->setImageValue(array_keys($variablesReplace), $variablesReplace); From aaf59ac908bf977f3851c99b5bec1ecddf381bdc Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 16:07:38 +0300 Subject: [PATCH 09/35] syntax fixes --- src/PhpWord/TemplateProcessor.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 22c5c6b52d..eddf169fe2 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -284,16 +284,16 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $search = array($search); } - $replaces_list = array(); + $replacesList = array(); if (!is_array($replace) || isset($replace["path"])) { - $replaces_list[] = $replace; + $replacesList[] = $replace; } else { - $replaces_list = array_values($replace); + $replacesList = array_values($replace); } - $search_replace = array(); + $searchReplace = array(); foreach ($search as $searchIdx => $searchString) { - $search_replace[$searchString] = isset($replaces_list[$searchIdx]) ? $replaces_list[$searchIdx] : $replaces_list[0]; + $searchReplace[$searchString] = isset($replacesList[$searchIdx]) ? $replacesList[$searchIdx] : $replacesList[0]; } // @@ -326,7 +326,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $partVariables = $this->getVariablesForPart($partContent); $partSearchReplaces = array(); - foreach ($search_replace as $search => $replace) { + foreach ($searchReplace as $search => $replace) { if (!in_array($search, $partVariables)) { continue; } From a37f60fc393006e1b3728dd10b977ee74b586f37 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Wed, 25 Oct 2017 16:23:17 +0300 Subject: [PATCH 10/35] php 5.3 compatibility fix --- src/PhpWord/TemplateProcessor.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index eddf169fe2..bee346ff5c 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -366,12 +366,12 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentNewImages[$imgPath] = $imgName; // setup type for image - $xmlImageType = str_replace(['{IMG}', '{EXT}'], [$imgName, $imgExt], $typeTpl) ; + $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl) ; $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; } - $xmlImage = str_replace(['{RID}', '{WIDTH}', '{HEIGHT}'], [$rid, $width, $height], $imgTpl) ; - $xmlImageRelation = str_replace(['{RID}', '{IMG}'], [$rid, $imgName], $relationTpl); + $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $width, $height), $imgTpl) ; + $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); if (!isset($this->tempDocumentRelations[$partFileName])) { // create new relations file From 1e2ef76fb7ed555b415d4c248e3752a2a671764f Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Sat, 11 Nov 2017 16:10:39 +0200 Subject: [PATCH 11/35] syntax fix --- src/PhpWord/TemplateProcessor.php | 49 +++++++++++-------------- tests/PhpWord/TemplateProcessorTest.php | 4 +- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index bee346ff5c..97de775f30 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -74,7 +74,7 @@ class TemplateProcessor * * @var string */ - protected $tempDocumentContentTypes = ""; + protected $tempDocumentContentTypes = ''; /** * new inserted images list @@ -273,9 +273,7 @@ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_ /** * @param mixed $search * @param mixed $replace Path to image, or array("path" => xx, "width" => yy, "height" => zz) - * @param integer $limit - * - * @return void + * @param int $limit */ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_DEFAULT) { @@ -285,7 +283,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM } $replacesList = array(); - if (!is_array($replace) || isset($replace["path"])) { + if (!is_array($replace) || isset($replace['path'])) { $replacesList[] = $replace; } else { $replacesList = array_values($replace); @@ -295,31 +293,29 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM foreach ($search as $searchIdx => $searchString) { $searchReplace[$searchString] = isset($replacesList[$searchIdx]) ? $replacesList[$searchIdx] : $replacesList[0]; } - // // define templates // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) $imgTpl = ''; $typeTpl = ''; $relationTpl = ''; - $newRelationsTpl = ''."\n".''; + $newRelationsTpl = '' . "\n" . ''; $newRelationsTypeTpl = ''; $extTransform = array( - "jpg" => "jpeg", - "JPG" => "jpeg", - "png" => "png", - "PNG" => "png", + 'jpg' => 'jpeg', + 'JPG' => 'jpeg', + 'png' => 'png', + 'PNG' => 'png', ); - // $searchParts = array( $this->getMainPartName() => &$this->tempDocumentMainPart, ); foreach (array_keys($this->tempDocumentHeaders) as $headerIndex) { - $searchParts[ $this->getHeaderName($headerIndex) ] = &$this->tempDocumentHeaders[$headerIndex]; + $searchParts[$this->getHeaderName($headerIndex)] = &$this->tempDocumentHeaders[$headerIndex]; } foreach (array_keys($this->tempDocumentFooters) as $headerIndex) { - $searchParts[ $this->getFooterName($headerIndex) ] = &$this->tempDocumentFooters[$headerIndex]; + $searchParts[$this->getFooterName($headerIndex)] = &$this->tempDocumentFooters[$headerIndex]; } foreach ($searchParts as $partFileName => &$partContent) { @@ -334,13 +330,13 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // get image path and size $width = 115; $height = 70; - if (is_array($replace) && isset($replace["path"])) { - $imgPath = $replace["path"]; - if (isset($replace["width"])) { - $width = $replace["width"]; + if (is_array($replace) && isset($replace['path'])) { + $imgPath = $replace['path']; + if (isset($replace['width'])) { + $width = $replace['width']; } - if (isset($replace["height"])) { - $height = $replace["height"]; + if (isset($replace['height'])) { + $height = $replace['height']; } } else { $imgPath = $replace; @@ -366,11 +362,11 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentNewImages[$imgPath] = $imgName; // setup type for image - $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl) ; + $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl); $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; } - $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $width, $height), $imgTpl) ; + $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $width, $height), $imgTpl); $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); if (!isset($this->tempDocumentRelations[$partFileName])) { @@ -385,7 +381,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; // collect prepared replaces - $partSearchReplaces["".self::ensureMacroCompleted($search).""] = $xmlImage; + $partSearchReplaces['' . self::ensureMacroCompleted($search) . ''] = $xmlImage; } if ($partSearchReplaces) { @@ -579,8 +575,6 @@ public function save() /** * @param string $fileName * @param string $xml - * - * @return void */ protected function savePartWithRels($fileName, $xml) { @@ -715,9 +709,9 @@ protected function getFooterName($index) * * @return string */ - protected function getRelationsName($documentPartName) + protected function getRelationsName(string $documentPartName) { - return 'word/_rels/'.pathinfo($documentPartName, PATHINFO_BASENAME).'.rels'; + return 'word/_rels/' . pathinfo($documentPartName, PATHINFO_BASENAME) . '.rels'; } protected function getNextRelationsIndex($documentPartName) @@ -725,6 +719,7 @@ protected function getNextRelationsIndex($documentPartName) if (isset($this->tempDocumentRelations[$documentPartName])) { return substr_count($this->tempDocumentRelations[$documentPartName], ' $imagePath, - 'documentContent' => array("path" => $imagePath, "width" => 500, "height" => 500), - 'footerValue' => array("path" => $imagePath, "width" => 50, "height" => 50), + 'documentContent' => array('path' => $imagePath, 'width' => 500, 'height' => 500), + 'footerValue' => array('path' => $imagePath, 'width' => 50, 'height' => 50), ); $templateProcessor->setImageValue(array_keys($variablesReplace), $variablesReplace); From 3109939aaacc05c4954bbdee64c7d55466e94898 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Sat, 11 Nov 2017 16:55:11 +0200 Subject: [PATCH 12/35] fix phpdoc variable name --- src/PhpWord/TemplateProcessor.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 2ce935b26d..790c1e46b2 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -134,6 +134,7 @@ protected function readPartWithRels($fileName) if ($partRelations !== false) { $this->tempDocumentRelations[$fileName] = $partRelations; } + return $this->fixBrokenMacros($this->zipClass->getFromName($fileName)); } @@ -693,11 +694,11 @@ protected function getFooterName($index) /** * Get the name of the relations file for document part. * - * @param string $docuemntPartName + * @param string $documentPartName * * @return string */ - protected function getRelationsName(string $documentPartName) + protected function getRelationsName($documentPartName) { return 'word/_rels/' . pathinfo($documentPartName, PATHINFO_BASENAME) . '.rels'; } From 6f63982a09904e370f1475516507ee9a6dd2e36b Mon Sep 17 00:00:00 2001 From: Gatis Ozols Date: Sat, 6 Jan 2018 00:22:57 +0200 Subject: [PATCH 13/35] Changed logic that determines extension image file extension for document to depend on MIME type. This same logic is used in Element/Image.php --- src/PhpWord/TemplateProcessor.php | 33 +++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 790c1e46b2..0f1ba6cdb9 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -299,11 +299,11 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $newRelationsTpl = '' . "\n" . ''; $newRelationsTypeTpl = ''; $extTransform = array( - 'jpg' => 'jpeg', - 'JPG' => 'jpeg', - 'png' => 'png', - 'PNG' => 'png', - ); + 'image/jpeg' => 'jpeg', + 'image/png' => 'png', + 'image/bmp' => 'bmp', + 'image/gif' => 'gif' + ); $searchParts = array( $this->getMainPartName() => &$this->tempDocumentMainPart, @@ -325,8 +325,6 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM } // get image path and size - $width = 115; - $height = 70; if (is_array($replace) && isset($replace['path'])) { $imgPath = $replace['path']; if (isset($replace['width'])) { @@ -339,6 +337,20 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $imgPath = $replace; } + $imageData = @getimagesize($imgPath); + if (!is_array($imageData)) { + throw new Exception(sprintf('Invalid image: %s', $imgPath)); + } + list($actualWidth, $actualHeight, $imageType) = $imageData; + $imageMimeType = image_type_to_mime_type($imageType); + + if(!isset($width)) { + $width = $actualWidth; + } + if(!isset($height)) { + $height = $actualHeight; + } + // get image index $imgIndex = $this->getNextRelationsIndex($partFileName); $rid = 'rId' . $imgIndex; @@ -348,9 +360,10 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $imgName = $this->tempDocumentNewImages[$imgPath]; } else { // transform extension - $imgExt = pathinfo($imgPath, PATHINFO_EXTENSION); - if (isset($extTransform)) { - $imgExt = $extTransform[$imgExt]; + if (isset($extTransform[$imageMimeType])) { + $imgExt = $extTransform[$imageMimeType]; + } else { + throw new Exception("Unsupported image type $imageMimeType"); } // add image to document From 04dd86f6d8ae5e255263d9fb4c2ba2ab6393140c Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Thu, 18 Jan 2018 17:51:11 +0200 Subject: [PATCH 14/35] syntax fixes --- src/PhpWord/TemplateProcessor.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 0f1ba6cdb9..b31e62c2dc 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -300,9 +300,9 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $newRelationsTypeTpl = ''; $extTransform = array( 'image/jpeg' => 'jpeg', - 'image/png' => 'png', - 'image/bmp' => 'bmp', - 'image/gif' => 'gif' + 'image/png' => 'png', + 'image/bmp' => 'bmp', + 'image/gif' => 'gif', ); $searchParts = array( @@ -344,10 +344,10 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM list($actualWidth, $actualHeight, $imageType) = $imageData; $imageMimeType = image_type_to_mime_type($imageType); - if(!isset($width)) { + if (!isset($width)) { $width = $actualWidth; } - if(!isset($height)) { + if (!isset($height)) { $height = $actualHeight; } From dbcbbeb0033df092e230ef85dc6a857fdcde6779 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 16 Feb 2018 20:44:13 +0200 Subject: [PATCH 15/35] support tags with arguments --- src/PhpWord/TemplateProcessor.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index b31e62c2dc..d306d97a0e 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -391,7 +391,11 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; // collect prepared replaces - $partSearchReplaces['' . self::ensureMacroCompleted($search) . ''] = $xmlImage; + $search = self::ensureMacroCompleted($search); + $matches = array(); + if (preg_match('/<[^<]+' . preg_quote($search) . '[^>]+>/u', $partContent, $matches)) { + $partSearchReplaces[$matches[0]] = $xmlImage; + } } if ($partSearchReplaces) { From ef50aeb01c4f03dff0a20a295bc331c3d399b93d Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 16 Mar 2018 15:03:23 +0200 Subject: [PATCH 16/35] simpler replace inline variables + test --- src/PhpWord/TemplateProcessor.php | 3 +- tests/PhpWord/TemplateProcessorTest.php | 39 +++++++++++++++++++++++-- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index d306d97a0e..0b42b02cf2 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -393,7 +393,8 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // collect prepared replaces $search = self::ensureMacroCompleted($search); $matches = array(); - if (preg_match('/<[^<]+' . preg_quote($search) . '[^>]+>/u', $partContent, $matches)) { + // just find substring. It not necessary to be alone in a tag + if (preg_match('/' . preg_quote($search) . '/u', $partContent, $matches)) { $partSearchReplaces[$matches[0]] = $xmlImage; } } diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 0d19d316e7..89152d277c 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -247,8 +247,43 @@ public function testSetImageValue() $this->assertTrue(strpos($expectedFooterRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); unlink($docName); - } - } + } else { + throw new \Exception("Generated file '{$docName}' not found!"); + } + + + // dynamic generated doc + $testFileName = 'images-test-sample.docx'; + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('${Test} --- ${Test}'); + $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); + $objWriter->save($testFileName); + if (!file_exists($testFileName)) { + throw new \Exception("Generated file '{$testFileName}' not found!"); + return; + } + + $resultFileName = 'images-test-result.docx'; + $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($testFileName); +// unlink($testFileName); + $templateProcessor->setImageValue("Test", $imagePath); + $templateProcessor->saveAs($resultFileName); + if (!file_exists($resultFileName)) { + throw new \Exception("Generated file '{$resultFileName}' not found!"); + return; + } + + $expectedDocumentZip = new \ZipArchive(); + $expectedDocumentZip->open($resultFileName); + $expectedMainPartXml = $expectedDocumentZip->getFromName('word/document.xml'); + if (false === $expectedDocumentZip->close()) { + throw new \Exception("Could not close zip file \"{$resultFileName}\"."); + } +// unlink($resultFileName); + + $this->assertTrue(strpos($expectedMainPartXml, '${Test}') === false, 'word/document.xml has no image.'); + } /** * @covers ::cloneBlock From 65ebf4acd1fd3952398e7cde3659ed8452b45e3c Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 16 Mar 2018 15:27:23 +0200 Subject: [PATCH 17/35] spaces fix --- src/PhpWord/TemplateProcessor.php | 2 +- tests/PhpWord/TemplateProcessorTest.php | 45 ++++++++++++------------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 0b42b02cf2..90e892b22d 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -393,7 +393,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // collect prepared replaces $search = self::ensureMacroCompleted($search); $matches = array(); - // just find substring. It not necessary to be alone in a tag + // just find substring. It not necessary to be alone in a tag if (preg_match('/' . preg_quote($search) . '/u', $partContent, $matches)) { $partSearchReplaces[$matches[0]] = $xmlImage; } diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 89152d277c..eea7888c94 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -249,41 +249,40 @@ public function testSetImageValue() unlink($docName); } else { throw new \Exception("Generated file '{$docName}' not found!"); - } - + } - // dynamic generated doc + // dynamic generated doc $testFileName = 'images-test-sample.docx'; - $phpWord = new \PhpOffice\PhpWord\PhpWord(); - $section = $phpWord->addSection(); - $section->addText('${Test} --- ${Test}'); - $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); - $objWriter->save($testFileName); + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('${Test} --- ${Test}'); + $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); + $objWriter->save($testFileName); if (!file_exists($testFileName)) { throw new \Exception("Generated file '{$testFileName}' not found!"); - return; - } + return; + } $resultFileName = 'images-test-result.docx'; - $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($testFileName); -// unlink($testFileName); - $templateProcessor->setImageValue("Test", $imagePath); - $templateProcessor->saveAs($resultFileName); + $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($testFileName); + // unlink($testFileName); + $templateProcessor->setImageValue('Test', $imagePath); + $templateProcessor->saveAs($resultFileName); if (!file_exists($resultFileName)) { throw new \Exception("Generated file '{$resultFileName}' not found!"); - return; - } + return; + } - $expectedDocumentZip = new \ZipArchive(); - $expectedDocumentZip->open($resultFileName); + $expectedDocumentZip = new \ZipArchive(); + $expectedDocumentZip->open($resultFileName); $expectedMainPartXml = $expectedDocumentZip->getFromName('word/document.xml'); - if (false === $expectedDocumentZip->close()) { - throw new \Exception("Could not close zip file \"{$resultFileName}\"."); - } -// unlink($resultFileName); + if (false === $expectedDocumentZip->close()) { + throw new \Exception("Could not close zip file \"{$resultFileName}\"."); + } + // unlink($resultFileName); $this->assertTrue(strpos($expectedMainPartXml, '${Test}') === false, 'word/document.xml has no image.'); - } + } /** * @covers ::cloneBlock From 9fcf06482f63065c86a8abe57632e99ffc0b1d5c Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 13:10:09 +0300 Subject: [PATCH 18/35] allow setup size of image into template variable --- src/PhpWord/TemplateProcessor.php | 222 +++++++++++++++++++++--------- 1 file changed, 157 insertions(+), 65 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 90e892b22d..7d99686b03 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -293,11 +293,12 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // define templates // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) - $imgTpl = ''; + $imgTpl = ''; $typeTpl = ''; $relationTpl = ''; $newRelationsTpl = '' . "\n" . ''; $newRelationsTypeTpl = ''; + $sizeRegexp = '/^([0-9]*(cm|mm|in|pt|pc|px|%|em|ex|)|auto)$/i'; $extTransform = array( 'image/jpeg' => 'jpeg', 'image/png' => 'png', @@ -320,82 +321,173 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $partSearchReplaces = array(); foreach ($searchReplace as $search => $replace) { - if (!in_array($search, $partVariables)) { - continue; - } - - // get image path and size - if (is_array($replace) && isset($replace['path'])) { - $imgPath = $replace['path']; - if (isset($replace['width'])) { - $width = $replace['width']; + $varsToReplace = array_filter($partVariables, function ($partVar) use ($search) { + return ($partVar == $search) || preg_match('/^' . preg_quote($search) . ':/', $partVar); + }); + + foreach ($varsToReplace as $varNameWithArgs) { + $search = $varNameWithArgs; + // extract variable args + $varElements = explode(':', $varNameWithArgs); + array_shift($varElements); + $varInlineArgs = array(); + // size format documentation: https://msdn.microsoft.com/en-us/library/documentformat.openxml.vml.shape%28v=office.14%29.aspx?f=255&MSPPError=-2147217396 + foreach ($varElements as $argIdx => $varArg) { + if (strpos($varArg, '=')) { // arg=value + list($argName, $argValue) = explode('=', $varArg, 2); + $argName = strtolower($argName); + if ($argName == 'size') { + list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $argValue, 2); + } else { + $varInlineArgs[strtolower($argName)] = $argValue; + } + } elseif (preg_match('/^([0-9]*[a-z%]{0,2}|auto)x([0-9]*[a-z%]{0,2}|auto)$/i', $varArg)) { // 60x40 + list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $varArg, 2); + } else { // :60:40:f + switch ($argIdx) { + case 0: + $varInlineArgs['width'] = $varArg; + break; + case 1: + $varInlineArgs['height'] = $varArg; + break; + case 2: + $varInlineArgs['ratio'] = $varArg; + break; + } + } } - if (isset($replace['height'])) { - $height = $replace['height']; + + // get image path and size + $width = null; + $height = null; + if (is_array($replace) && isset($replace['path'])) { + $imgPath = $replace['path']; + if (isset($replace['width'])) { + $width = $replace['width']; + } + if (isset($replace['height'])) { + $height = $replace['height']; + } + } else { + $imgPath = $replace; } - } else { - $imgPath = $replace; - } - $imageData = @getimagesize($imgPath); - if (!is_array($imageData)) { - throw new Exception(sprintf('Invalid image: %s', $imgPath)); - } - list($actualWidth, $actualHeight, $imageType) = $imageData; - $imageMimeType = image_type_to_mime_type($imageType); + // choose width + if (is_null($width) && isset($varInlineArgs['width'])) { + $width = $varInlineArgs['width']; + } + if (!preg_match($sizeRegexp, $width)) { + $width = null; + } + if (is_null($width)) { + $width = 115; + } + if (is_numeric($width)) { + $width .= 'px'; + } - if (!isset($width)) { - $width = $actualWidth; - } - if (!isset($height)) { - $height = $actualHeight; - } + // choose height + if (is_null($height) && isset($varInlineArgs['height'])) { + $height = $varInlineArgs['height']; + } + if (!preg_match($sizeRegexp, $height)) { + $height = null; + } + if (is_null($height)) { + $height = 70; + } + if (is_numeric($height)) { + $height .= 'px'; + } - // get image index - $imgIndex = $this->getNextRelationsIndex($partFileName); - $rid = 'rId' . $imgIndex; - - // get image embed name - if (isset($this->tempDocumentNewImages[$imgPath])) { - $imgName = $this->tempDocumentNewImages[$imgPath]; - } else { - // transform extension - if (isset($extTransform[$imageMimeType])) { - $imgExt = $extTransform[$imageMimeType]; - } else { - throw new Exception("Unsupported image type $imageMimeType"); + // fix aspect ratio (by default) + if ($varInlineArgs['ratio'] !== 'f') { + $imageInfo = getimagesize($imgPath); + $imageRatio = $imageInfo[0] / $imageInfo[1]; + + if (($width === '') && ($height === '')) { // defined size are empty + $width = $imageInfo[0] . 'px'; + $height = $imageInfo[1] . 'px'; + } elseif ($width === '') { // defined width is empty + $heightFloat = (float) $height; + $widthFloat = $heightFloat * $imageRatio; + $matches = array(); + preg_match("/\d([a-z%]+)$/", $height, $matches); + $width = $widthFloat . $matches[1]; + } elseif ($height === '') { // defined height is empty + $widthFloat = (float) $width; + $heightFloat = $widthFloat / $imageRatio; + $matches = array(); + preg_match("/\d([a-z%]+)$/", $width, $matches); + $height = $heightFloat . $matches[1]; + } else { // we have defined size, but we need also check it aspect ratio + $widthMatches = array(); + preg_match("/\d([a-z%]+)$/", $width, $widthMatches); + $heightMatches = array(); + preg_match("/\d([a-z%]+)$/", $height, $heightMatches); + // try to fix only if dimensions are same + if ($widthMatches[1] == $heightMatches[1]) { + $dimention = $widthMatches[1]; + $widthFloat = (float) $width; + $heightFloat = (float) $height; + $definedRatio = $widthFloat / $heightFloat; + + if ($imageRatio > $definedRatio) { // image wider than defined box + $height = ($widthFloat / $imageRatio) . $dimention; + } elseif ($imageRatio < $definedRatio) { // image higher than defined box + $width = ($heightFloat * $imageRatio) . $dimention; + } + } + } } - // add image to document - $imgName = 'image' . $imgIndex . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; - $this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName); - $this->tempDocumentNewImages[$imgPath] = $imgName; + // get image index + $imgIndex = $this->getNextRelationsIndex($partFileName); + $rid = 'rId' . $imgIndex; - // setup type for image - $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl); - $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; - } + // get image embed name + if (isset($this->tempDocumentNewImages[$imgPath])) { + $imgName = $this->tempDocumentNewImages[$imgPath]; + } else { + // transform extension + if (isset($extTransform[$imageMimeType])) { + $imgExt = $extTransform[$imageMimeType]; + } else { + throw new Exception("Unsupported image type $imageMimeType"); + } + + // add image to document + $imgName = 'image' . $imgIndex . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; + $this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName); + $this->tempDocumentNewImages[$imgPath] = $imgName; + + // setup type for image + $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl); + $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; + } - $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $width, $height), $imgTpl); - $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); + $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $width, $height), $imgTpl); + $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); - if (!isset($this->tempDocumentRelations[$partFileName])) { - // create new relations file - $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; - // and add it to content types - $xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl); - $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; - } + if (!isset($this->tempDocumentRelations[$partFileName])) { + // create new relations file + $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; + // and add it to content types + $xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl); + $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; + } - // add image to relations - $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; + // add image to relations + $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; - // collect prepared replaces - $search = self::ensureMacroCompleted($search); - $matches = array(); - // just find substring. It not necessary to be alone in a tag - if (preg_match('/' . preg_quote($search) . '/u', $partContent, $matches)) { - $partSearchReplaces[$matches[0]] = $xmlImage; + // collect prepared replaces + $search = self::ensureMacroCompleted($search); + $matches = array(); + // just find substring. It not necessary to be alone in a tag + if (preg_match('/' . preg_quote($search) . '/u', $partContent, $matches)) { + $partSearchReplaces[$matches[0]] = $xmlImage; + } } } From de250541e680f9dae8a77ac0a4a977b27a43e6e2 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 13:23:23 +0300 Subject: [PATCH 19/35] restore using mime types --- src/PhpWord/TemplateProcessor.php | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7d99686b03..ca4f570585 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -373,6 +373,13 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $imgPath = $replace; } + $imageData = @getimagesize($imgPath); + if (!is_array($imageData)) { + throw new Exception(sprintf('Invalid image: %s', $imgPath)); + } + list($actualWidth, $actualHeight, $imageType) = $imageData; + $imageMimeType = image_type_to_mime_type($imageType); + // choose width if (is_null($width) && isset($varInlineArgs['width'])) { $width = $varInlineArgs['width']; @@ -403,12 +410,11 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // fix aspect ratio (by default) if ($varInlineArgs['ratio'] !== 'f') { - $imageInfo = getimagesize($imgPath); - $imageRatio = $imageInfo[0] / $imageInfo[1]; + $imageRatio = $actualWidth / $actualHeight; if (($width === '') && ($height === '')) { // defined size are empty - $width = $imageInfo[0] . 'px'; - $height = $imageInfo[1] . 'px'; + $width = $actualWidth . 'px'; + $height = $actualHeight . 'px'; } elseif ($width === '') { // defined width is empty $heightFloat = (float) $height; $widthFloat = $heightFloat * $imageRatio; From a6b26000ad0e7fa44fb91070aa8493e3e13fc9dd Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 13:34:06 +0300 Subject: [PATCH 20/35] better variable names --- src/PhpWord/TemplateProcessor.php | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index ca4f570585..386f582cd8 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -320,13 +320,12 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $partVariables = $this->getVariablesForPart($partContent); $partSearchReplaces = array(); - foreach ($searchReplace as $search => $replace) { - $varsToReplace = array_filter($partVariables, function ($partVar) use ($search) { - return ($partVar == $search) || preg_match('/^' . preg_quote($search) . ':/', $partVar); + foreach ($searchReplace as $searchString => $replaceImage) { + $varsToReplace = array_filter($partVariables, function ($partVar) use ($searchString) { + return ($partVar == $searchString) || preg_match('/^' . preg_quote($searchString) . ':/', $partVar); }); foreach ($varsToReplace as $varNameWithArgs) { - $search = $varNameWithArgs; // extract variable args $varElements = explode(':', $varNameWithArgs); array_shift($varElements); @@ -361,16 +360,16 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // get image path and size $width = null; $height = null; - if (is_array($replace) && isset($replace['path'])) { - $imgPath = $replace['path']; - if (isset($replace['width'])) { - $width = $replace['width']; + if (is_array($replaceImage) && isset($replaceImage['path'])) { + $imgPath = $replaceImage['path']; + if (isset($replaceImage['width'])) { + $width = $replaceImage['width']; } - if (isset($replace['height'])) { - $height = $replace['height']; + if (isset($replaceImage['height'])) { + $height = $replaceImage['height']; } } else { - $imgPath = $replace; + $imgPath = $replaceImage; } $imageData = @getimagesize($imgPath); @@ -488,10 +487,10 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; // collect prepared replaces - $search = self::ensureMacroCompleted($search); + $varNameWithArgFixed = self::ensureMacroCompleted($varNameWithArg); $matches = array(); // just find substring. It not necessary to be alone in a tag - if (preg_match('/' . preg_quote($search) . '/u', $partContent, $matches)) { + if (preg_match('/' . preg_quote($varNameWithArgFixed) . '/u', $partContent, $matches)) { $partSearchReplaces[$matches[0]] = $xmlImage; } } From 2031cccbf90dc84324b708d1f5a7f78949bf60e2 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 14:55:21 +0300 Subject: [PATCH 21/35] fix variable name --- src/PhpWord/TemplateProcessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 386f582cd8..7347dbf27d 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -487,10 +487,10 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; // collect prepared replaces - $varNameWithArgFixed = self::ensureMacroCompleted($varNameWithArg); + $varNameWithArgsFixed = self::ensureMacroCompleted($varNameWithArgs); $matches = array(); // just find substring. It not necessary to be alone in a tag - if (preg_match('/' . preg_quote($varNameWithArgFixed) . '/u', $partContent, $matches)) { + if (preg_match('/' . preg_quote($varNameWithArgsFixed) . '/u', $partContent, $matches)) { $partSearchReplaces[$matches[0]] = $xmlImage; } } From 28fd8a0e6bc333ac9e6fe2f4d4e488e60d78a794 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 15:28:48 +0300 Subject: [PATCH 22/35] reduce size of methods --- src/PhpWord/TemplateProcessor.php | 267 ++++++++++++++++-------------- 1 file changed, 145 insertions(+), 122 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7347dbf27d..fcfaee4296 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -267,6 +267,146 @@ public function setValue($search, $replace, $limit = self::MAXIMUM_REPLACEMENTS_ $this->tempDocumentFooters = $this->setValueForPart($search, $replace, $this->tempDocumentFooters, $limit); } + private function getImageArgs($varNameWithArgs) + { + $varElements = explode(':', $varNameWithArgs); + array_shift($varElements); // first element is name of variable => remove it + + $varInlineArgs = array(); + // size format documentation: https://msdn.microsoft.com/en-us/library/documentformat.openxml.vml.shape%28v=office.14%29.aspx?f=255&MSPPError=-2147217396 + foreach ($varElements as $argIdx => $varArg) { + if (strpos($varArg, '=')) { // arg=value + list($argName, $argValue) = explode('=', $varArg, 2); + $argName = strtolower($argName); + if ($argName == 'size') { + list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $argValue, 2); + } else { + $varInlineArgs[strtolower($argName)] = $argValue; + } + } elseif (preg_match('/^([0-9]*[a-z%]{0,2}|auto)x([0-9]*[a-z%]{0,2}|auto)$/i', $varArg)) { // 60x40 + list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $varArg, 2); + } else { // :60:40:f + switch ($argIdx) { + case 0: + $varInlineArgs['width'] = $varArg; + break; + case 1: + $varInlineArgs['height'] = $varArg; + break; + case 2: + $varInlineArgs['ratio'] = $varArg; + break; + } + } + } + + return $varInlineArgs; + } + + private function prepareImageAttrs($replaceImage, $varInlineArgs) + { + $sizeRegexp = '/^([0-9]*(cm|mm|in|pt|pc|px|%|em|ex|)|auto)$/i'; + + // get image path and size + $width = null; + $height = null; + if (is_array($replaceImage) && isset($replaceImage['path'])) { + $imgPath = $replaceImage['path']; + if (isset($replaceImage['width'])) { + $width = $replaceImage['width']; + } + if (isset($replaceImage['height'])) { + $height = $replaceImage['height']; + } + } else { + $imgPath = $replaceImage; + } + + $imageData = @getimagesize($imgPath); + if (!is_array($imageData)) { + throw new Exception(sprintf('Invalid image: %s', $imgPath)); + } + list($actualWidth, $actualHeight, $imageType) = $imageData; + $imageMimeType = image_type_to_mime_type($imageType); + + // choose width + if (is_null($width) && isset($varInlineArgs['width'])) { + $width = $varInlineArgs['width']; + } + if (!preg_match($sizeRegexp, $width)) { + $width = null; + } + if (is_null($width)) { + $width = 115; + } + if (is_numeric($width)) { + $width .= 'px'; + } + + // choose height + if (is_null($height) && isset($varInlineArgs['height'])) { + $height = $varInlineArgs['height']; + } + if (!preg_match($sizeRegexp, $height)) { + $height = null; + } + if (is_null($height)) { + $height = 70; + } + if (is_numeric($height)) { + $height .= 'px'; + } + + // fix aspect ratio (by default) + if (empty($varInlineArgs['ratio']) || $varInlineArgs['ratio'] !== 'f') { + $imageRatio = $actualWidth / $actualHeight; + + if (($width === '') && ($height === '')) { // defined size are empty + $width = $actualWidth . 'px'; + $height = $actualHeight . 'px'; + } elseif ($width === '') { // defined width is empty + $heightFloat = (float) $height; + $widthFloat = $heightFloat * $imageRatio; + $matches = array(); + preg_match("/\d([a-z%]+)$/", $height, $matches); + $width = $widthFloat . $matches[1]; + } elseif ($height === '') { // defined height is empty + $widthFloat = (float) $width; + $heightFloat = $widthFloat / $imageRatio; + $matches = array(); + preg_match("/\d([a-z%]+)$/", $width, $matches); + $height = $heightFloat . $matches[1]; + } else { // we have defined size, but we need also check it aspect ratio + $widthMatches = array(); + preg_match("/\d([a-z%]+)$/", $width, $widthMatches); + $heightMatches = array(); + preg_match("/\d([a-z%]+)$/", $height, $heightMatches); + // try to fix only if dimensions are same + if ($widthMatches[1] == $heightMatches[1]) { + $dimention = $widthMatches[1]; + $widthFloat = (float) $width; + $heightFloat = (float) $height; + $definedRatio = $widthFloat / $heightFloat; + + if ($imageRatio > $definedRatio) { // image wider than defined box + $height = ($widthFloat / $imageRatio) . $dimention; + } elseif ($imageRatio < $definedRatio) { // image higher than defined box + $width = ($heightFloat * $imageRatio) . $dimention; + } + } + } + } + + $imageAttrs = array( + 'src' => $imgPath, + 'mime' => $imageMimeType, + 'width' => $width, + 'height' => $height, + ); + + return $imageAttrs; + } + /** * @param mixed $search * @param mixed $replace Path to image, or array("path" => xx, "width" => yy, "height" => zz) @@ -298,7 +438,6 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $relationTpl = ''; $newRelationsTpl = '' . "\n" . ''; $newRelationsTypeTpl = ''; - $sizeRegexp = '/^([0-9]*(cm|mm|in|pt|pc|px|%|em|ex|)|auto)$/i'; $extTransform = array( 'image/jpeg' => 'jpeg', 'image/png' => 'png', @@ -326,126 +465,10 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM }); foreach ($varsToReplace as $varNameWithArgs) { - // extract variable args - $varElements = explode(':', $varNameWithArgs); - array_shift($varElements); - $varInlineArgs = array(); - // size format documentation: https://msdn.microsoft.com/en-us/library/documentformat.openxml.vml.shape%28v=office.14%29.aspx?f=255&MSPPError=-2147217396 - foreach ($varElements as $argIdx => $varArg) { - if (strpos($varArg, '=')) { // arg=value - list($argName, $argValue) = explode('=', $varArg, 2); - $argName = strtolower($argName); - if ($argName == 'size') { - list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $argValue, 2); - } else { - $varInlineArgs[strtolower($argName)] = $argValue; - } - } elseif (preg_match('/^([0-9]*[a-z%]{0,2}|auto)x([0-9]*[a-z%]{0,2}|auto)$/i', $varArg)) { // 60x40 - list($varInlineArgs['width'], $varInlineArgs['height']) = explode('x', $varArg, 2); - } else { // :60:40:f - switch ($argIdx) { - case 0: - $varInlineArgs['width'] = $varArg; - break; - case 1: - $varInlineArgs['height'] = $varArg; - break; - case 2: - $varInlineArgs['ratio'] = $varArg; - break; - } - } - } - - // get image path and size - $width = null; - $height = null; - if (is_array($replaceImage) && isset($replaceImage['path'])) { - $imgPath = $replaceImage['path']; - if (isset($replaceImage['width'])) { - $width = $replaceImage['width']; - } - if (isset($replaceImage['height'])) { - $height = $replaceImage['height']; - } - } else { - $imgPath = $replaceImage; - } - - $imageData = @getimagesize($imgPath); - if (!is_array($imageData)) { - throw new Exception(sprintf('Invalid image: %s', $imgPath)); - } - list($actualWidth, $actualHeight, $imageType) = $imageData; - $imageMimeType = image_type_to_mime_type($imageType); - - // choose width - if (is_null($width) && isset($varInlineArgs['width'])) { - $width = $varInlineArgs['width']; - } - if (!preg_match($sizeRegexp, $width)) { - $width = null; - } - if (is_null($width)) { - $width = 115; - } - if (is_numeric($width)) { - $width .= 'px'; - } - - // choose height - if (is_null($height) && isset($varInlineArgs['height'])) { - $height = $varInlineArgs['height']; - } - if (!preg_match($sizeRegexp, $height)) { - $height = null; - } - if (is_null($height)) { - $height = 70; - } - if (is_numeric($height)) { - $height .= 'px'; - } - - // fix aspect ratio (by default) - if ($varInlineArgs['ratio'] !== 'f') { - $imageRatio = $actualWidth / $actualHeight; - - if (($width === '') && ($height === '')) { // defined size are empty - $width = $actualWidth . 'px'; - $height = $actualHeight . 'px'; - } elseif ($width === '') { // defined width is empty - $heightFloat = (float) $height; - $widthFloat = $heightFloat * $imageRatio; - $matches = array(); - preg_match("/\d([a-z%]+)$/", $height, $matches); - $width = $widthFloat . $matches[1]; - } elseif ($height === '') { // defined height is empty - $widthFloat = (float) $width; - $heightFloat = $widthFloat / $imageRatio; - $matches = array(); - preg_match("/\d([a-z%]+)$/", $width, $matches); - $height = $heightFloat . $matches[1]; - } else { // we have defined size, but we need also check it aspect ratio - $widthMatches = array(); - preg_match("/\d([a-z%]+)$/", $width, $widthMatches); - $heightMatches = array(); - preg_match("/\d([a-z%]+)$/", $height, $heightMatches); - // try to fix only if dimensions are same - if ($widthMatches[1] == $heightMatches[1]) { - $dimention = $widthMatches[1]; - $widthFloat = (float) $width; - $heightFloat = (float) $height; - $definedRatio = $widthFloat / $heightFloat; - - if ($imageRatio > $definedRatio) { // image wider than defined box - $height = ($widthFloat / $imageRatio) . $dimention; - } elseif ($imageRatio < $definedRatio) { // image higher than defined box - $width = ($heightFloat * $imageRatio) . $dimention; - } - } - } - } + $varInlineArgs = $this->getImageArgs($varNameWithArgs); + $preparedImageAttrs = $this->prepareImageAttrs($replaceImage, $varInlineArgs); + $imgPath = $preparedImageAttrs['src']; + $imageMimeType = $preparedImageAttrs['mime']; // get image index $imgIndex = $this->getNextRelationsIndex($partFileName); @@ -472,7 +495,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; } - $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $width, $height), $imgTpl); + $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $preparedImageAttrs['width'], $preparedImageAttrs['height']), $imgTpl); $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); if (!isset($this->tempDocumentRelations[$partFileName])) { From a4f815366ed799efa66f4693a060a3aa43af6b89 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 15:56:49 +0300 Subject: [PATCH 23/35] fix broken xml structure after replace variables --- src/PhpWord/TemplateProcessor.php | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index fcfaee4296..56b1817768 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -458,7 +458,6 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); - $partSearchReplaces = array(); foreach ($searchReplace as $searchString => $replaceImage) { $varsToReplace = array_filter($partVariables, function ($partVar) use ($searchString) { return ($partVar == $searchString) || preg_match('/^' . preg_quote($searchString) . ':/', $partVar); @@ -512,16 +511,16 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // collect prepared replaces $varNameWithArgsFixed = self::ensureMacroCompleted($varNameWithArgs); $matches = array(); - // just find substring. It not necessary to be alone in a tag - if (preg_match('/' . preg_quote($varNameWithArgsFixed) . '/u', $partContent, $matches)) { - $partSearchReplaces[$matches[0]] = $xmlImage; + if (preg_match('/(<[^<]+>)([^<]*)(' . preg_quote($varNameWithArgsFixed) . ')([^>]*)(<[^>]+>)/Uu', $partContent, $matches)) { + $wholeTag = $matches[0]; + array_shift($matches); + list($openTag, $prefix, , $postfix, $closeTag) = $matches; + $replaceXml = $openTag . $prefix . $closeTag . $xmlImage . $openTag . $postfix . $closeTag; + // replace on each iteration, because in one tag we can have 2+ inline variables => before proceed next variable we need to change $partContent + $partContent = $this->setValueForPart($wholeTag, $replaceXml, $partContent, $limit); } } } - - if ($partSearchReplaces) { - $partContent = $this->setValueForPart(array_keys($partSearchReplaces), $partSearchReplaces, $partContent, $limit); - } } } From 3f8735292ca65365c7888d71994ddbe9056bc62e Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 16:29:27 +0300 Subject: [PATCH 24/35] more reduce functions size --- src/PhpWord/TemplateProcessor.php | 134 +++++++++++++++--------------- 1 file changed, 65 insertions(+), 69 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 56b1817768..e315d7f0c4 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -303,10 +303,67 @@ private function getImageArgs($varNameWithArgs) return $varInlineArgs; } - private function prepareImageAttrs($replaceImage, $varInlineArgs) + private function chooseImageDimension($baseValue, $inlineValue, $defaultValue) + { + $value = $baseValue; + if (is_null($value) && isset($inlineValue)) { + $value = $inlineValue; + } + if (!preg_match('/^([0-9]*(cm|mm|in|pt|pc|px|%|em|ex|)|auto)$/i', $value)) { + $value = null; + } + if (is_null($value)) { + $value = $defaultValue; + } + if (is_numeric($value)) { + $value .= 'px'; + } + + return $value; + } + + private function fixImageWidthHeightRatio(&$width, &$height, $actualWidth, $actualHeight) { - $sizeRegexp = '/^([0-9]*(cm|mm|in|pt|pc|px|%|em|ex|)|auto)$/i'; + $imageRatio = $actualWidth / $actualHeight; + + if (($width === '') && ($height === '')) { // defined size are empty + $width = $actualWidth . 'px'; + $height = $actualHeight . 'px'; + } elseif ($width === '') { // defined width is empty + $heightFloat = (float) $height; + $widthFloat = $heightFloat * $imageRatio; + $matches = array(); + preg_match("/\d([a-z%]+)$/", $height, $matches); + $width = $widthFloat . $matches[1]; + } elseif ($height === '') { // defined height is empty + $widthFloat = (float) $width; + $heightFloat = $widthFloat / $imageRatio; + $matches = array(); + preg_match("/\d([a-z%]+)$/", $width, $matches); + $height = $heightFloat . $matches[1]; + } else { // we have defined size, but we need also check it aspect ratio + $widthMatches = array(); + preg_match("/\d([a-z%]+)$/", $width, $widthMatches); + $heightMatches = array(); + preg_match("/\d([a-z%]+)$/", $height, $heightMatches); + // try to fix only if dimensions are same + if ($widthMatches[1] == $heightMatches[1]) { + $dimention = $widthMatches[1]; + $widthFloat = (float) $width; + $heightFloat = (float) $height; + $definedRatio = $widthFloat / $heightFloat; + if ($imageRatio > $definedRatio) { // image wider than defined box + $height = ($widthFloat / $imageRatio) . $dimention; + } elseif ($imageRatio < $definedRatio) { // image higher than defined box + $width = ($heightFloat * $imageRatio) . $dimention; + } + } + } + } + + private function prepareImageAttrs($replaceImage, $varInlineArgs) + { // get image path and size $width = null; $height = null; @@ -322,84 +379,23 @@ private function prepareImageAttrs($replaceImage, $varInlineArgs) $imgPath = $replaceImage; } + $width = $this->chooseImageDimension($width, isset($varInlineArgs['width']) ? $varInlineArgs['width'] : null, 115); + $height = $this->chooseImageDimension($height, isset($varInlineArgs['height']) ? $varInlineArgs['height'] : null, 70); + $imageData = @getimagesize($imgPath); if (!is_array($imageData)) { throw new Exception(sprintf('Invalid image: %s', $imgPath)); } list($actualWidth, $actualHeight, $imageType) = $imageData; - $imageMimeType = image_type_to_mime_type($imageType); - - // choose width - if (is_null($width) && isset($varInlineArgs['width'])) { - $width = $varInlineArgs['width']; - } - if (!preg_match($sizeRegexp, $width)) { - $width = null; - } - if (is_null($width)) { - $width = 115; - } - if (is_numeric($width)) { - $width .= 'px'; - } - - // choose height - if (is_null($height) && isset($varInlineArgs['height'])) { - $height = $varInlineArgs['height']; - } - if (!preg_match($sizeRegexp, $height)) { - $height = null; - } - if (is_null($height)) { - $height = 70; - } - if (is_numeric($height)) { - $height .= 'px'; - } // fix aspect ratio (by default) if (empty($varInlineArgs['ratio']) || $varInlineArgs['ratio'] !== 'f') { - $imageRatio = $actualWidth / $actualHeight; - - if (($width === '') && ($height === '')) { // defined size are empty - $width = $actualWidth . 'px'; - $height = $actualHeight . 'px'; - } elseif ($width === '') { // defined width is empty - $heightFloat = (float) $height; - $widthFloat = $heightFloat * $imageRatio; - $matches = array(); - preg_match("/\d([a-z%]+)$/", $height, $matches); - $width = $widthFloat . $matches[1]; - } elseif ($height === '') { // defined height is empty - $widthFloat = (float) $width; - $heightFloat = $widthFloat / $imageRatio; - $matches = array(); - preg_match("/\d([a-z%]+)$/", $width, $matches); - $height = $heightFloat . $matches[1]; - } else { // we have defined size, but we need also check it aspect ratio - $widthMatches = array(); - preg_match("/\d([a-z%]+)$/", $width, $widthMatches); - $heightMatches = array(); - preg_match("/\d([a-z%]+)$/", $height, $heightMatches); - // try to fix only if dimensions are same - if ($widthMatches[1] == $heightMatches[1]) { - $dimention = $widthMatches[1]; - $widthFloat = (float) $width; - $heightFloat = (float) $height; - $definedRatio = $widthFloat / $heightFloat; - - if ($imageRatio > $definedRatio) { // image wider than defined box - $height = ($widthFloat / $imageRatio) . $dimention; - } elseif ($imageRatio < $definedRatio) { // image higher than defined box - $width = ($heightFloat * $imageRatio) . $dimention; - } - } - } + $this->fixImageWidthHeightRatio($width, $height, $actualWidth, $actualHeight); } $imageAttrs = array( 'src' => $imgPath, - 'mime' => $imageMimeType, + 'mime' => image_type_to_mime_type($imageType), 'width' => $width, 'height' => $height, ); @@ -508,7 +504,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM // add image to relations $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; - // collect prepared replaces + // replace variable $varNameWithArgsFixed = self::ensureMacroCompleted($varNameWithArgs); $matches = array(); if (preg_match('/(<[^<]+>)([^<]*)(' . preg_quote($varNameWithArgsFixed) . ')([^>]*)(<[^>]+>)/Uu', $partContent, $matches)) { From 5038b3b0b559f3ba59819cd3556e4fac73a252bc Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 16:51:20 +0300 Subject: [PATCH 25/35] more reduce methods size --- src/PhpWord/TemplateProcessor.php | 104 ++++++++++++++++-------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index e315d7f0c4..8ef9c5c782 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -403,6 +403,55 @@ private function prepareImageAttrs($replaceImage, $varInlineArgs) return $imageAttrs; } + private function addImageToRelations($partFileName, $rid, $imgPath, $imageMimeType) + { + // define templates + $typeTpl = ''; + $relationTpl = ''; + $newRelationsTpl = '' . "\n" . ''; + $newRelationsTypeTpl = ''; + $extTransform = array( + 'image/jpeg' => 'jpeg', + 'image/png' => 'png', + 'image/bmp' => 'bmp', + 'image/gif' => 'gif', + ); + + // get image embed name + if (isset($this->tempDocumentNewImages[$imgPath])) { + $imgName = $this->tempDocumentNewImages[$imgPath]; + } else { + // transform extension + if (isset($extTransform[$imageMimeType])) { + $imgExt = $extTransform[$imageMimeType]; + } else { + throw new Exception("Unsupported image type $imageMimeType"); + } + + // add image to document + $imgName = 'image_' . $rid . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; + $this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName); + $this->tempDocumentNewImages[$imgPath] = $imgName; + + // setup type for image + $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl); + $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; + } + + $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); + + if (!isset($this->tempDocumentRelations[$partFileName])) { + // create new relations file + $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; + // and add it to content types + $xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl); + $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; + } + + // add image to relations + $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; + } + /** * @param mixed $search * @param mixed $replace Path to image, or array("path" => xx, "width" => yy, "height" => zz) @@ -427,20 +476,7 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $searchReplace[$searchString] = isset($replacesList[$searchIdx]) ? $replacesList[$searchIdx] : $replacesList[0]; } - // define templates - // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) - $imgTpl = ''; - $typeTpl = ''; - $relationTpl = ''; - $newRelationsTpl = '' . "\n" . ''; - $newRelationsTypeTpl = ''; - $extTransform = array( - 'image/jpeg' => 'jpeg', - 'image/png' => 'png', - 'image/bmp' => 'bmp', - 'image/gif' => 'gif', - ); - + // collect document parts $searchParts = array( $this->getMainPartName() => &$this->tempDocumentMainPart, ); @@ -451,6 +487,10 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $searchParts[$this->getFooterName($headerIndex)] = &$this->tempDocumentFooters[$headerIndex]; } + // define templates + // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) + $imgTpl = ''; + foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); @@ -463,46 +503,14 @@ public function setImageValue($search, $replace, $limit = self::MAXIMUM_REPLACEM $varInlineArgs = $this->getImageArgs($varNameWithArgs); $preparedImageAttrs = $this->prepareImageAttrs($replaceImage, $varInlineArgs); $imgPath = $preparedImageAttrs['src']; - $imageMimeType = $preparedImageAttrs['mime']; // get image index $imgIndex = $this->getNextRelationsIndex($partFileName); $rid = 'rId' . $imgIndex; - // get image embed name - if (isset($this->tempDocumentNewImages[$imgPath])) { - $imgName = $this->tempDocumentNewImages[$imgPath]; - } else { - // transform extension - if (isset($extTransform[$imageMimeType])) { - $imgExt = $extTransform[$imageMimeType]; - } else { - throw new Exception("Unsupported image type $imageMimeType"); - } - - // add image to document - $imgName = 'image' . $imgIndex . '_' . pathinfo($partFileName, PATHINFO_FILENAME) . '.' . $imgExt; - $this->zipClass->pclzipAddFile($imgPath, 'word/media/' . $imgName); - $this->tempDocumentNewImages[$imgPath] = $imgName; - - // setup type for image - $xmlImageType = str_replace(array('{IMG}', '{EXT}'), array($imgName, $imgExt), $typeTpl); - $this->tempDocumentContentTypes = str_replace('', $xmlImageType, $this->tempDocumentContentTypes) . ''; - } - + // replace preparations + $this->addImageToRelations($partFileName, $rid, $imgPath, $preparedImageAttrs['mime']); $xmlImage = str_replace(array('{RID}', '{WIDTH}', '{HEIGHT}'), array($rid, $preparedImageAttrs['width'], $preparedImageAttrs['height']), $imgTpl); - $xmlImageRelation = str_replace(array('{RID}', '{IMG}'), array($rid, $imgName), $relationTpl); - - if (!isset($this->tempDocumentRelations[$partFileName])) { - // create new relations file - $this->tempDocumentRelations[$partFileName] = $newRelationsTpl; - // and add it to content types - $xmlRelationsType = str_replace('{RELS}', $this->getRelationsName($partFileName), $newRelationsTypeTpl); - $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; - } - - // add image to relations - $this->tempDocumentRelations[$partFileName] = str_replace('', $xmlImageRelation, $this->tempDocumentRelations[$partFileName]) . ''; // replace variable $varNameWithArgsFixed = self::ensureMacroCompleted($varNameWithArgs); From 795965117f4821395ef2119e4d75967587ceab14 Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 17:54:00 +0300 Subject: [PATCH 26/35] support of 'ratio' replace attribute + documentation --- docs/templates-processing.rst | 18 ++++++++++++++++++ src/PhpWord/TemplateProcessor.php | 9 ++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index af03b24586..0cc5683ac9 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -7,14 +7,32 @@ You can create an OOXML document template with included search-patterns (macros) To deal with a template file, use ``new TemplateProcessor`` statement. After TemplateProcessor instance creation the document template is copied into the temporary directory. Then you can use ``TemplateProcessor::setValue`` method to change the value of a search pattern. The search-pattern model is: ``${search-pattern}``. +The search-pattern model for images can be like: +- ``${search-image-pattern}`` +- ``${search-image-pattern:[width]:[height]:[ratio]}`` +- ``${search-image-pattern:[width]x[height]}`` +- ``${search-image-pattern:size=[width]x[height]}`` +- ``${search-image-pattern:width=[width]:height=[height]:ratio=false}`` +Where: +- [width] and [height] can be just numbers or numbers with measure, which supported by Word (cm|mm|in|pt|pc|px|%|em|ex) +- [ratio] uses only for ``false``, ``-`` or ``f`` to turn off respect aspect ration of image. By default template image size uses as 'container' size. + Example: +.. code-block:: doc + + ${CompanyLogo} + ${UserLogo:50:50} ${Name} - ${City} - ${Street} + .. code-block:: php $templateProcessor = new TemplateProcessor('Template.docx'); $templateProcessor->setValue('Name', 'John Doe'); $templateProcessor->setValue(array('City', 'Street'), array('Detroit', '12th Street')); + $templateProcessor->setImageValue('CompanyLogo', 'path/to/company/logo.png'); + $templateProcessor->setImageValue('UserLogo', array('path' => 'path/to/logo.png', 'width' => 100, 'height' => 100, 'ratio' => false)); + It is not possible to directly add new OOXML elements to the template file being processed, but it is possible to transform headers, main document part, and footers of the template using XSLT (see ``TemplateProcessor::applyXslStyleSheet``). See ``Sample_07_TemplateCloneRow.php`` for example on how to create diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 8ef9c5c782..f320b88463 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -367,6 +367,7 @@ private function prepareImageAttrs($replaceImage, $varInlineArgs) // get image path and size $width = null; $height = null; + $ratio = null; if (is_array($replaceImage) && isset($replaceImage['path'])) { $imgPath = $replaceImage['path']; if (isset($replaceImage['width'])) { @@ -375,6 +376,9 @@ private function prepareImageAttrs($replaceImage, $varInlineArgs) if (isset($replaceImage['height'])) { $height = $replaceImage['height']; } + if (isset($replaceImage['ratio'])) { + $ratio = $replaceImage['ratio']; + } } else { $imgPath = $replaceImage; } @@ -389,7 +393,10 @@ private function prepareImageAttrs($replaceImage, $varInlineArgs) list($actualWidth, $actualHeight, $imageType) = $imageData; // fix aspect ratio (by default) - if (empty($varInlineArgs['ratio']) || $varInlineArgs['ratio'] !== 'f') { + if (is_null($ratio) && isset($varInlineArgs['ratio'])) { + $ratio = $varInlineArgs['ratio']; + } + if (is_null($ratio) || !in_array(strtolower($ratio), array('', '-', 'f', 'false'))) { $this->fixImageWidthHeightRatio($width, $height, $actualWidth, $actualHeight); } From c6515db8bc89cb4f3d5637f211bfc8c1680f22bc Mon Sep 17 00:00:00 2001 From: Maxim Bulygin Date: Fri, 25 May 2018 18:10:20 +0300 Subject: [PATCH 27/35] update unit test --- tests/PhpWord/TemplateProcessorTest.php | 18 +++++++++--------- .../_files/templates/header-footer.docx | Bin 4495 -> 15730 bytes 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index eea7888c94..7e811b47ac 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -187,7 +187,7 @@ public function testMacrosCanBeReplacedInHeaderAndFooter() { $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/header-footer.docx'); - $this->assertEquals(array('documentContent', 'headerValue', 'footerValue'), $templateProcessor->getVariables()); + $this->assertEquals(array('documentContent', 'headerValue:100:100', 'footerValue'), $templateProcessor->getVariables()); $macroNames = array('headerValue', 'documentContent', 'footerValue'); $macroValues = array('Header Value', 'Document text.', 'Footer Value'); @@ -212,7 +212,7 @@ public function testSetImageValue() $variablesReplace = array( 'headerValue' => $imagePath, 'documentContent' => array('path' => $imagePath, 'width' => 500, 'height' => 500), - 'footerValue' => array('path' => $imagePath, 'width' => 50, 'height' => 50), + 'footerValue' => array('path' => $imagePath, 'width' => 100, 'height' => 50, 'ratio' => false), ); $templateProcessor->setImageValue(array_keys($variablesReplace), $variablesReplace); @@ -230,21 +230,21 @@ public function testSetImageValue() $expectedMainPartXml = $expectedDocumentZip->getFromName('word/document.xml'); $expectedHeaderPartXml = $expectedDocumentZip->getFromName('word/header1.xml'); $expectedFooterPartXml = $expectedDocumentZip->getFromName('word/footer1.xml'); - $expectedImage = $expectedDocumentZip->getFromName('word/media/image5_document.jpeg'); + $expectedImage = $expectedDocumentZip->getFromName('word/media/image_rId11_document.jpeg'); if (false === $expectedDocumentZip->close()) { throw new \Exception("Could not close zip file \"{$docName}\"."); } $this->assertTrue(!empty($expectedImage), 'Embed image doesn\'t found.'); - $this->assertTrue(strpos($expectedContentTypesXml, '/word/media/image5_document.jpeg') > 0, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedContentTypesXml, '/word/media/image_rId11_document.jpeg') > 0, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); $this->assertTrue(strpos($expectedContentTypesXml, '/word/_rels/header1.xml.rels') > 0, '[Content_Types].xml missed "/word/_rels/header1.xml.rels"'); $this->assertTrue(strpos($expectedContentTypesXml, '/word/_rels/footer1.xml.rels') > 0, '[Content_Types].xml missed "/word/_rels/footer1.xml.rels"'); $this->assertTrue(strpos($expectedMainPartXml, '${documentContent}') === false, 'word/document.xml has no image.'); $this->assertTrue(strpos($expectedHeaderPartXml, '${headerValue}') === false, 'word/header1.xml has no image.'); $this->assertTrue(strpos($expectedFooterPartXml, '${footerValue}') === false, 'word/footer1.xml has no image.'); - $this->assertTrue(strpos($expectedDocumentRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); - $this->assertTrue(strpos($expectedHeaderRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); - $this->assertTrue(strpos($expectedFooterRelationsXml, 'media/image5_document.jpeg') > 0, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedDocumentRelationsXml, 'media/image_rId11_document.jpeg') > 0, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedHeaderRelationsXml, 'media/image_rId11_document.jpeg') > 0, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); + $this->assertTrue(strpos($expectedFooterRelationsXml, 'media/image_rId11_document.jpeg') > 0, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); unlink($docName); } else { @@ -265,7 +265,7 @@ public function testSetImageValue() $resultFileName = 'images-test-result.docx'; $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($testFileName); - // unlink($testFileName); + unlink($testFileName); $templateProcessor->setImageValue('Test', $imagePath); $templateProcessor->saveAs($resultFileName); if (!file_exists($resultFileName)) { @@ -279,7 +279,7 @@ public function testSetImageValue() if (false === $expectedDocumentZip->close()) { throw new \Exception("Could not close zip file \"{$resultFileName}\"."); } - // unlink($resultFileName); + unlink($resultFileName); $this->assertTrue(strpos($expectedMainPartXml, '${Test}') === false, 'word/document.xml has no image.'); } diff --git a/tests/PhpWord/_files/templates/header-footer.docx b/tests/PhpWord/_files/templates/header-footer.docx index 647d52222d612ef33a0113e8ea3ad84ad3c96085..dc2e9e8beaabaefe8ddc10384cdd747ce335cc10 100644 GIT binary patch literal 15730 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 L)>RFzpP&90bK-2! literal 4495 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 Date: Sun, 23 Dec 2018 22:30:57 +0100 Subject: [PATCH 28/35] Update CHANGELOG.md --- CHANGELOG.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dc8b45879..c663558056 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,9 @@ Change Log All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +### Added +- Add support for setting images in TemplateProcessor - @SailorMax #1170 + v0.14.0 (?? Dec 2017) ---------------------- This release fixes several bugs and adds some new features. @@ -443,4 +446,4 @@ This is the first release after a long development hiatus in [CodePlex](https:// - Basic CI with Travis - @Progi1984 - Added PHPWord_Exception and exception when could not copy the template - @Progi1984 - IMPROVED: Moved examples out of Classes directory - @Progi1984 -- IMPROVED: Advanced string replace in setValue for Template - @Esmeraldo [#49](http://phpword.codeplex.com/workitem/49) \ No newline at end of file +- IMPROVED: Advanced string replace in setValue for Template - @Esmeraldo [#49](http://phpword.codeplex.com/workitem/49) From 495227c923f35cfcd4ceb6eb69a5dbdab989401a Mon Sep 17 00:00:00 2001 From: troosan Date: Sun, 23 Dec 2018 23:11:12 +0100 Subject: [PATCH 29/35] fix check style error --- tests/PhpWord/TemplateProcessorTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 59a42b414f..3895f5e5a8 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -260,7 +260,6 @@ public function testSetImageValue() $objWriter->save($testFileName); if (!file_exists($testFileName)) { throw new \Exception("Generated file '{$testFileName}' not found!"); - return; } $resultFileName = 'images-test-result.docx'; @@ -270,7 +269,6 @@ public function testSetImageValue() $templateProcessor->saveAs($resultFileName); if (!file_exists($resultFileName)) { throw new \Exception("Generated file '{$resultFileName}' not found!"); - return; } $expectedDocumentZip = new \ZipArchive(); From 6bedbd8bbe742fa0b66c194b750b9f421dbe77b2 Mon Sep 17 00:00:00 2001 From: troosan Date: Sun, 23 Dec 2018 23:11:25 +0100 Subject: [PATCH 30/35] php 7.3 should not fail anymore --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 881decfe2a..338babad4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,14 +18,9 @@ matrix: env: COVERAGE=1 - php: 5.3 env: COMPOSER_MEMORY_LIMIT=2G - - php: 7.3 - env: DEPENDENCIES="--ignore-platform-reqs" exclude: - php: 5.3 - php: 7.0 - - php: 7.3 - allow_failures: - - php: 7.3 cache: directories: From 3b31a654405659ce1648a34b3352ed88a29a98db Mon Sep 17 00:00:00 2001 From: troosan Date: Sun, 23 Dec 2018 23:21:39 +0100 Subject: [PATCH 31/35] Revert "php 7.3 should not fail anymore" This reverts commit 6bedbd8bbe742fa0b66c194b750b9f421dbe77b2. --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 338babad4a..881decfe2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,14 @@ matrix: env: COVERAGE=1 - php: 5.3 env: COMPOSER_MEMORY_LIMIT=2G + - php: 7.3 + env: DEPENDENCIES="--ignore-platform-reqs" exclude: - php: 5.3 - php: 7.0 + - php: 7.3 + allow_failures: + - php: 7.3 cache: directories: From 3673dd563e8c12bdb84e8f79091f128ec638c494 Mon Sep 17 00:00:00 2001 From: troosan Date: Sun, 23 Dec 2018 23:26:53 +0100 Subject: [PATCH 32/35] ignore dependencies for php 7.3 --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 881decfe2a..1d32cfda34 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,6 @@ matrix: - php: 5.3 - php: 7.0 - php: 7.3 - allow_failures: - - php: 7.3 cache: directories: From ef40717c75da77e8274806e8594e7ffc6a283180 Mon Sep 17 00:00:00 2001 From: troosan Date: Tue, 25 Dec 2018 00:07:20 +0100 Subject: [PATCH 33/35] rewrite test to use phpunit assertions --- tests/PhpWord/TemplateProcessorTest.php | 67 +++++++++++-------------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 3895f5e5a8..b19394909f 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -218,39 +218,36 @@ public function testSetImageValue() $docName = 'header-footer-images-test-result.docx'; $templateProcessor->saveAs($docName); - $docFound = file_exists($docName); - if ($docFound) { - $expectedDocumentZip = new \ZipArchive(); - $expectedDocumentZip->open($docName); - $expectedContentTypesXml = $expectedDocumentZip->getFromName('[Content_Types].xml'); - $expectedDocumentRelationsXml = $expectedDocumentZip->getFromName('word/_rels/document.xml.rels'); - $expectedHeaderRelationsXml = $expectedDocumentZip->getFromName('word/_rels/header1.xml.rels'); - $expectedFooterRelationsXml = $expectedDocumentZip->getFromName('word/_rels/footer1.xml.rels'); - $expectedMainPartXml = $expectedDocumentZip->getFromName('word/document.xml'); - $expectedHeaderPartXml = $expectedDocumentZip->getFromName('word/header1.xml'); - $expectedFooterPartXml = $expectedDocumentZip->getFromName('word/footer1.xml'); - $expectedImage = $expectedDocumentZip->getFromName('word/media/image_rId11_document.jpeg'); - if (false === $expectedDocumentZip->close()) { - throw new \Exception("Could not close zip file \"{$docName}\"."); - } - - $this->assertTrue(!empty($expectedImage), 'Embed image doesn\'t found.'); - $this->assertTrue(strpos($expectedContentTypesXml, '/word/media/image_rId11_document.jpeg') > 0, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); - $this->assertTrue(strpos($expectedContentTypesXml, '/word/_rels/header1.xml.rels') > 0, '[Content_Types].xml missed "/word/_rels/header1.xml.rels"'); - $this->assertTrue(strpos($expectedContentTypesXml, '/word/_rels/footer1.xml.rels') > 0, '[Content_Types].xml missed "/word/_rels/footer1.xml.rels"'); - $this->assertTrue(strpos($expectedMainPartXml, '${documentContent}') === false, 'word/document.xml has no image.'); - $this->assertTrue(strpos($expectedHeaderPartXml, '${headerValue}') === false, 'word/header1.xml has no image.'); - $this->assertTrue(strpos($expectedFooterPartXml, '${footerValue}') === false, 'word/footer1.xml has no image.'); - $this->assertTrue(strpos($expectedDocumentRelationsXml, 'media/image_rId11_document.jpeg') > 0, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); - $this->assertTrue(strpos($expectedHeaderRelationsXml, 'media/image_rId11_document.jpeg') > 0, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); - $this->assertTrue(strpos($expectedFooterRelationsXml, 'media/image_rId11_document.jpeg') > 0, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); - - unlink($docName); - } else { - throw new \Exception("Generated file '{$docName}' not found!"); + $this->assertFileExists($docName, "Generated file '{$docName}' not found!"); + + $expectedDocumentZip = new \ZipArchive(); + $expectedDocumentZip->open($docName); + $expectedContentTypesXml = $expectedDocumentZip->getFromName('[Content_Types].xml'); + $expectedDocumentRelationsXml = $expectedDocumentZip->getFromName('word/_rels/document.xml.rels'); + $expectedHeaderRelationsXml = $expectedDocumentZip->getFromName('word/_rels/header1.xml.rels'); + $expectedFooterRelationsXml = $expectedDocumentZip->getFromName('word/_rels/footer1.xml.rels'); + $expectedMainPartXml = $expectedDocumentZip->getFromName('word/document.xml'); + $expectedHeaderPartXml = $expectedDocumentZip->getFromName('word/header1.xml'); + $expectedFooterPartXml = $expectedDocumentZip->getFromName('word/footer1.xml'); + $expectedImage = $expectedDocumentZip->getFromName('word/media/image_rId11_document.jpeg'); + if (false === $expectedDocumentZip->close()) { + throw new \Exception("Could not close zip file \"{$docName}\"."); } + $this->assertNotEmpty($expectedImage, 'Embed image doesn\'t found.'); + $this->assertStringContainsString('/word/media/image_rId11_document.jpeg', $expectedContentTypesXml, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); + $this->assertStringContainsString('/word/_rels/header1.xml.rels', $expectedContentTypesXml, '[Content_Types].xml missed "/word/_rels/header1.xml.rels"'); + $this->assertStringContainsString('/word/_rels/footer1.xml.rels', $expectedContentTypesXml, '[Content_Types].xml missed "/word/_rels/footer1.xml.rels"'); + $this->assertStringNotContainsString('${documentContent}', $expectedMainPartXml, 'word/document.xml has no image.'); + $this->assertStringNotContainsString('${headerValue}', $expectedHeaderPartXml, 'word/header1.xml has no image.'); + $this->assertStringNotContainsString('${footerValue}', $expectedFooterPartXml, 'word/footer1.xml has no image.'); + $this->assertStringContainsString('media/image_rId11_document.jpeg', $expectedDocumentRelationsXml, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); + $this->assertStringContainsString('media/image_rId11_document.jpeg', $expectedHeaderRelationsXml, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); + $this->assertStringContainsString('media/image_rId11_document.jpeg', $expectedFooterRelationsXml, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); + + unlink($docName); + // dynamic generated doc $testFileName = 'images-test-sample.docx'; $phpWord = new \PhpOffice\PhpWord\PhpWord(); @@ -258,18 +255,14 @@ public function testSetImageValue() $section->addText('${Test} --- ${Test}'); $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); $objWriter->save($testFileName); - if (!file_exists($testFileName)) { - throw new \Exception("Generated file '{$testFileName}' not found!"); - } + $this->assertFileExists($testFileName, "Generated file '{$testFileName}' not found!"); $resultFileName = 'images-test-result.docx'; $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($testFileName); unlink($testFileName); $templateProcessor->setImageValue('Test', $imagePath); $templateProcessor->saveAs($resultFileName); - if (!file_exists($resultFileName)) { - throw new \Exception("Generated file '{$resultFileName}' not found!"); - } + $this->assertFileExists($resultFileName, "Generated file '{$resultFileName}' not found!"); $expectedDocumentZip = new \ZipArchive(); $expectedDocumentZip->open($resultFileName); @@ -279,7 +272,7 @@ public function testSetImageValue() } unlink($resultFileName); - $this->assertTrue(strpos($expectedMainPartXml, '${Test}') === false, 'word/document.xml has no image.'); + $this->assertStringNotContainsString('${Test}', $expectedMainPartXml, 'word/document.xml has no image.'); } /** From 8ba5bfdf6f2a753e93ce3f1b2ca89eb79204011b Mon Sep 17 00:00:00 2001 From: troosan Date: Tue, 25 Dec 2018 00:07:31 +0100 Subject: [PATCH 34/35] improve code coverage --- tests/PhpWord/TemplateProcessorTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index b19394909f..5a09df3ff8 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -252,7 +252,7 @@ public function testSetImageValue() $testFileName = 'images-test-sample.docx'; $phpWord = new \PhpOffice\PhpWord\PhpWord(); $section = $phpWord->addSection(); - $section->addText('${Test} --- ${Test}'); + $section->addText('${Test:width=100:ratio=true}'); $objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, 'Word2007'); $objWriter->save($testFileName); $this->assertFileExists($testFileName, "Generated file '{$testFileName}' not found!"); @@ -261,6 +261,8 @@ public function testSetImageValue() $templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor($testFileName); unlink($testFileName); $templateProcessor->setImageValue('Test', $imagePath); + $templateProcessor->setImageValue('Test1', $imagePath); + $templateProcessor->setImageValue('Test2', $imagePath); $templateProcessor->saveAs($resultFileName); $this->assertFileExists($resultFileName, "Generated file '{$resultFileName}' not found!"); From d7714618b4a12e29238a2c08ec7b4c35d22ac444 Mon Sep 17 00:00:00 2001 From: troosan Date: Wed, 26 Dec 2018 14:01:47 +0100 Subject: [PATCH 35/35] use old phpunit compatible assertions --- tests/PhpWord/TemplateProcessorTest.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 5a09df3ff8..fa84cf3b33 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -236,15 +236,15 @@ public function testSetImageValue() } $this->assertNotEmpty($expectedImage, 'Embed image doesn\'t found.'); - $this->assertStringContainsString('/word/media/image_rId11_document.jpeg', $expectedContentTypesXml, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); - $this->assertStringContainsString('/word/_rels/header1.xml.rels', $expectedContentTypesXml, '[Content_Types].xml missed "/word/_rels/header1.xml.rels"'); - $this->assertStringContainsString('/word/_rels/footer1.xml.rels', $expectedContentTypesXml, '[Content_Types].xml missed "/word/_rels/footer1.xml.rels"'); - $this->assertStringNotContainsString('${documentContent}', $expectedMainPartXml, 'word/document.xml has no image.'); - $this->assertStringNotContainsString('${headerValue}', $expectedHeaderPartXml, 'word/header1.xml has no image.'); - $this->assertStringNotContainsString('${footerValue}', $expectedFooterPartXml, 'word/footer1.xml has no image.'); - $this->assertStringContainsString('media/image_rId11_document.jpeg', $expectedDocumentRelationsXml, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); - $this->assertStringContainsString('media/image_rId11_document.jpeg', $expectedHeaderRelationsXml, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); - $this->assertStringContainsString('media/image_rId11_document.jpeg', $expectedFooterRelationsXml, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); + $this->assertContains('/word/media/image_rId11_document.jpeg', $expectedContentTypesXml, '[Content_Types].xml missed "/word/media/image5_document.jpeg"'); + $this->assertContains('/word/_rels/header1.xml.rels', $expectedContentTypesXml, '[Content_Types].xml missed "/word/_rels/header1.xml.rels"'); + $this->assertContains('/word/_rels/footer1.xml.rels', $expectedContentTypesXml, '[Content_Types].xml missed "/word/_rels/footer1.xml.rels"'); + $this->assertNotContains('${documentContent}', $expectedMainPartXml, 'word/document.xml has no image.'); + $this->assertNotContains('${headerValue}', $expectedHeaderPartXml, 'word/header1.xml has no image.'); + $this->assertNotContains('${footerValue}', $expectedFooterPartXml, 'word/footer1.xml has no image.'); + $this->assertContains('media/image_rId11_document.jpeg', $expectedDocumentRelationsXml, 'word/_rels/document.xml.rels missed "media/image5_document.jpeg"'); + $this->assertContains('media/image_rId11_document.jpeg', $expectedHeaderRelationsXml, 'word/_rels/header1.xml.rels missed "media/image5_document.jpeg"'); + $this->assertContains('media/image_rId11_document.jpeg', $expectedFooterRelationsXml, 'word/_rels/footer1.xml.rels missed "media/image5_document.jpeg"'); unlink($docName); @@ -274,7 +274,7 @@ public function testSetImageValue() } unlink($resultFileName); - $this->assertStringNotContainsString('${Test}', $expectedMainPartXml, 'word/document.xml has no image.'); + $this->assertNotContains('${Test}', $expectedMainPartXml, 'word/document.xml has no image.'); } /**