diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f6ac85..8b81a016 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,11 +19,19 @@ This is the changelog between releases of PHPWord. Releases are listed in revers - ListItem: Ability to add list item in header/footer - @ivanlanin GH-187 - CheckBox: Ability to add checkbox in header/footer - @ivanlanin GH-187 - Link: Ability to add link in header/footer - @ivanlanin GH-187 +- Object: Ability to add object in textrun and footnote - @ivanlanin GH-187 ### Bugfixes - Footnote: Footnote content doesn't show footnote reference number - @ivanlanin GH-170 +### Deprecated + +- `createTextRun` replaced by `addTextRun` +- `createFootnote` replaced by `addFootnote` +- `createHeader` replaced by `addHeader` +- `createFooter` replaced by `addFooter` + ### Miscellaneous - Documentation: Simplify page level docblock - @ivanlanin GH-179 diff --git a/docs/elements.rst b/docs/elements.rst index bded72b8..d1634bf4 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -3,6 +3,51 @@ Elements ======== +Below are the matrix of element availability in each container. The column shows +the containers while the rows lists the elements. + ++-----+---------------+---------+--------+--------+------+----------+----------+ +| Num | Element | Section | Header | Footer | Cell | Text Run | Footnote | ++=====+===============+=========+========+========+======+==========+==========+ +| 1 | Text | v | v | v | v | v | v | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 2 | Text Run | v | v | v | v | \- | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 3 | Link | v | v | v | v | v | v | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 4 | Title | v | ? | ? | ? | ? | ? | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 5 | Preserve Text | ? | v | v | v\* | ? | ? | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 6 | Text Break | v | v | v | v | v | v | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 7 | Page Break | v | \- | \- | \- | \- | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 8 | List | v | v | v | v | \- | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 9 | Table | v | v | v | ? | \- | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 10 | Image | v | v | v | v | v | v | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 11 | Watermark | \- | v | \- | \- | \- | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 12 | Object | v | ? | ? | v | v | v | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 13 | TOC | v | \- | \- | \- | \- | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 14 | Footnote | v | \- | \- | v\*\*| v\*\* | \- | ++-----+---------------+---------+--------+--------+------+----------+----------+ +| 15 | CheckBox | v | v | v | v | ? | ? | ++-----+---------------+---------+--------+--------+------+----------+----------+ + +Legend: + +- ``v`` Available +- ``v*`` Available only when inside header/footer +- ``v**`` Available only when inside section +- ``-`` Not available +- ``?`` Should be available + Texts ----- diff --git a/samples/Sample_04_Textrun.php b/samples/Sample_04_Textrun.php index d289457d..d95297b8 100644 --- a/samples/Sample_04_Textrun.php +++ b/samples/Sample_04_Textrun.php @@ -29,6 +29,8 @@ $textrun->addText(' Sample Link: '); $textrun->addLink('http://www.google.com', null, 'NLink'); $textrun->addText(' Sample Image: '); $textrun->addImage('resources/_earth.jpg', array('width' => 18, 'height' => 18)); +$textrun->addText(' Sample Object: '); +$textrun->addObject('resources/_sheet.xls'); $textrun->addText(' Here is some more text. '); // Save file diff --git a/samples/Sample_06_Footnote.php b/samples/Sample_06_Footnote.php index e35f5d6b..57421eff 100755 --- a/samples/Sample_06_Footnote.php +++ b/samples/Sample_06_Footnote.php @@ -27,8 +27,10 @@ $footnote->addTextBreak(); $footnote->addText('But you can insert a manual text break like above, '); $footnote->addText('links like '); $footnote->addLink('http://www.google.com', null, 'NLink'); -$footnote->addText(', or image like '); +$footnote->addText(', image like '); $footnote->addImage('resources/_earth.jpg', array('width' => 18, 'height' => 18)); +$footnote->addText(', or object like '); +$footnote->addObject('resources/_sheet.xls'); $footnote->addText('But you can only put footnote in section, not in header or footer.'); $section->addText('You can also create the footnote directly from the section making it wrap in a paragraph like the footnote below this paragraph. But is is best used from within a textrun.'); diff --git a/src/PhpWord/Container/Container.php b/src/PhpWord/Container/Container.php index a29e393c..fd88955e 100644 --- a/src/PhpWord/Container/Container.php +++ b/src/PhpWord/Container/Container.php @@ -37,21 +37,21 @@ use PhpOffice\PhpWord\Element\CheckBox; abstract class Container { /** - * Container type section|header|footer + * Container type section|header|footer|cell|textrun|footnote * * @var string */ - protected $containerType; + protected $container; /** * Section Id * * @var int */ - protected $sectionId; + protected $containerId; /** - * Footer Element Collection + * Elements collection * * @var int */ @@ -62,11 +62,11 @@ abstract class Container * * Used by textrun and cell to determine where the element is located * because it will affect the availability of other element, e.g. footnote - * will not be available when $docPartType is header or footer. + * will not be available when $docPart is header or footer. * * @var string */ - protected $docPartType = null; + protected $docPart = null; /** * Document part Id @@ -92,9 +92,13 @@ abstract class Container */ public function addText($text, $fontStyle = null, $paragraphStyle = null) { - if (in_array($this->containerType, array('footnote', 'textrun'))) { + $this->checkValidity('text'); + + // Reset paragraph style for footnote and textrun. They have their own + if (in_array($this->container, array('footnote', 'textrun'))) { $paragraphStyle = null; } + $text = String::toUTF8($text); $element = new Text($text, $fontStyle, $paragraphStyle); $this->elements[] = $element; @@ -102,20 +106,6 @@ abstract class Container return $element; } - /** - * Add text break element - * - * @param int $count - * @param mixed $fontStyle - * @param mixed $paragraphStyle - */ - public function addTextBreak($count = 1, $fontStyle = null, $paragraphStyle = null) - { - for ($i = 1; $i <= $count; $i++) { - $this->elements[] = new TextBreak($fontStyle, $paragraphStyle); - } - } - /** * Add textrun element * @@ -124,18 +114,17 @@ abstract class Container */ public function addTextRun($paragraphStyle = null) { - if (!in_array($this->containerType, array('section', 'header', 'footer', 'cell'))) { - throw new \BadMethodCallException(); - } - if ($this->containerType == 'cell') { - $docPartType = $this->docPartType; + $this->checkValidity('textrun'); + + if ($this->container == 'cell') { + $docPart = $this->docPart; $docPartId = $this->docPartId; } else { - $docPartType = $this->containerType; - $docPartId = $this->sectionId; + $docPart = $this->container; + $docPartId = $this->containerId; } - $textRun = new TextRun($paragraphStyle, $docPartType, $docPartId); + $textRun = new TextRun($paragraphStyle, $docPart, $docPartId); $this->elements[] = $textRun; return $textRun; @@ -152,24 +141,30 @@ abstract class Container */ public function addLink($linkSrc, $linkName = null, $fontStyle = null, $paragraphStyle = null) { - if (!is_null($this->docPartType)) { - $linkContainer = $this->docPartType; - $linkContainerId = $this->docPartId; + $this->checkValidity('link'); + + $inSection = true; + if (!is_null($this->docPart)) { + $container = $this->docPart; + $containerId = $this->docPartId; } else { - $linkContainer = $this->containerType; - $linkContainerId = $this->sectionId; + $container = $this->container; + $containerId = $this->containerId; } - if ($linkContainer == 'header' || $linkContainer == 'footer') { - $linkContainer .= $linkContainerId; + if ($container == 'header' || $container == 'footer') { + $container .= $containerId; + $inSection = false; + } elseif ($container == 'footnote') { + $inSection = false; } $linkSrc = String::toUTF8($linkSrc); $linkName = String::toUTF8($linkName); $link = new Link($linkSrc, $linkName, $fontStyle, $paragraphStyle); - if ($linkContainer == 'section') { + if ($inSection) { $rID = Media::addSectionLinkElement($linkSrc); } else { - $rID = Media::addMediaElement($linkContainer, 'hyperlink', $linkSrc); + $rID = Media::addMediaElement($container, 'hyperlink', $linkSrc); } $link->setRelationId($rID); $this->elements[] = $link; @@ -183,13 +178,11 @@ abstract class Container * @param string $text * @param int $depth * @return Title - * @todo Enable title element in header, footer, footnote, textrun + * @todo Enable title element in other containers */ public function addTitle($text, $depth = 1) { - if (!in_array($this->containerType, array('section'))) { - throw new \BadMethodCallException(); - } + $this->checkValidity('title'); $text = String::toUTF8($text); $styles = Style::getStyles(); @@ -219,12 +212,7 @@ abstract class Container */ public function addPreserveText($text, $fontStyle = null, $paragraphStyle = null) { - if (!in_array($this->containerType, array('header', 'footer', 'cell'))) { - throw new \BadMethodCallException(); - } - if ($this->containerType == 'cell' && $this->docPartType == 'section') { - throw new \BadMethodCallException(); - } + $this->checkValidity('preservetext'); $text = String::toUTF8($text); $ptext = new PreserveText($text, $fontStyle, $paragraphStyle); @@ -233,6 +221,22 @@ abstract class Container return $ptext; } + /** + * Add text break element + * + * @param int $count + * @param mixed $fontStyle + * @param mixed $paragraphStyle + */ + public function addTextBreak($count = 1, $fontStyle = null, $paragraphStyle = null) + { + $this->checkValidity('textbreak'); + + for ($i = 1; $i <= $count; $i++) { + $this->elements[] = new TextBreak($fontStyle, $paragraphStyle); + } + } + /** * Add listitem element * @@ -242,13 +246,10 @@ abstract class Container * @param mixed $styleList * @param mixed $paragraphStyle * @return ListItem - * @todo Enable list item element in header and footer */ public function addListItem($text, $depth = 0, $fontStyle = null, $styleList = null, $paragraphStyle = null) { - if (!in_array($this->containerType, array('section', 'header', 'footer', 'cell'))) { - throw new \BadMethodCallException(); - } + $this->checkValidity('listitem'); $text = String::toUTF8($text); $listItem = new ListItem($text, $depth, $fontStyle, $styleList, $paragraphStyle); @@ -265,11 +266,9 @@ abstract class Container */ public function addTable($style = null) { - if (!in_array($this->containerType, array('section', 'header', 'footer'))) { - throw new \BadMethodCallException(); - } + $this->checkValidity('table'); - $table = new Table($this->containerType, $this->sectionId, $style); + $table = new Table($this->container, $this->containerId, $style); $this->elements[] = $table; return $table; @@ -285,30 +284,31 @@ abstract class Container */ public function addImage($src, $style = null, $isWatermark = false) { - if ($this->containerType == 'cell') { - $imageContainerType = $this->docPartType; - $imageContainerId = $this->docPartId; + $this->checkValidity('image'); + if ($this->container == 'cell' || $this->container == 'textrun') { + $container = $this->docPart; + $containerId = $this->docPartId; } else { - $imageContainerType = $this->containerType; - $imageContainerId = $this->sectionId; + $container = $this->container; + $containerId = $this->containerId; } $image = new Image($src, $style, $isWatermark); if (!is_null($image->getSource())) { $rID = null; - switch ($imageContainerType) { + switch ($container) { case 'textrun': case 'section': $rID = Media::addSectionMediaElement($src, 'image', $image); break; case 'header': - $rID = Media::addHeaderMediaElement($imageContainerId, $src, $image); + $rID = Media::addHeaderMediaElement($containerId, $src, $image); break; case 'footer': - $rID = Media::addFooterMediaElement($imageContainerId, $src, $image); + $rID = Media::addFooterMediaElement($containerId, $src, $image); break; case 'footnote': - $rID = Media::addMediaElement('footnotes', 'image', $src, $image); + $rID = Media::addMediaElement('footnote', 'image', $src, $image); break; } $image->setRelationId($rID); @@ -331,11 +331,19 @@ abstract class Container */ public function addObject($src, $style = null) { - if (!in_array($this->containerType, array('section', 'cell'))) { - throw new \BadMethodCallException(); + $inSection = true; + if (!is_null($this->docPart)) { + $container = $this->docPart; + $containerId = $this->docPartId; + } else { + $container = $this->container; + $containerId = $this->containerId; } - if ($this->containerType == 'cell' && $this->docPartType != 'section') { - throw new \BadMethodCallException(); + if ($container == 'header' || $container == 'footer') { + $container .= $containerId; + $inSection = false; + } elseif ($container == 'footnote') { + $inSection = false; } $object = new Object($src, $style); @@ -345,9 +353,14 @@ abstract class Container if (strlen($ext) == 4 && strtolower(substr($ext, -1)) == 'x') { $ext = substr($ext, 0, -1); } - $icon = __DIR__ . "/../_staticDocParts/_{$ext}.png"; - $rIDimg = Media::addSectionMediaElement($icon, 'image', new Image($icon)); - $data = Media::addSectionMediaElement($src, 'oleObject'); + $icon = realpath(__DIR__ . "/../_staticDocParts/_{$ext}.png"); + if ($inSection) { + $rIDimg = Media::addSectionMediaElement($icon, 'image', new Image($icon)); + $data = Media::addSectionMediaElement($src, 'oleObject'); + } else { + $rIDimg = Media::addMediaElement($container, 'image', $icon, new Image($icon)); + $data = Media::addMediaElement($container, 'embeddings', $src); + } $rID = $data[0]; $objectId = $data[1]; $object->setRelationId($rID); @@ -368,12 +381,7 @@ abstract class Container */ public function addFootnote($paragraphStyle = null) { - if (!in_array($this->containerType, array('section', 'textrun', 'cell'))) { - throw new \BadMethodCallException(); - } - if (!is_null($this->docPartType) && $this->docPartType != 'section') { - throw new \BadMethodCallException(); - } + $this->checkValidity('footnote'); $footnote = new FootnoteElement($paragraphStyle); $refID = FootnoteCollection::addFootnoteElement($footnote); @@ -391,16 +399,10 @@ abstract class Container * @param mixed $fontStyle * @param mixed $paragraphStyle * @return CheckBox - * @todo Enable checkbox element in header and footer */ public function addCheckBox($name, $text, $fontStyle = null, $paragraphStyle = null) { - if (!in_array($this->containerType, array('section', 'header', 'footer', 'cell'))) { - throw new \BadMethodCallException(); - } - if ($this->containerType == 'cell' && $this->docPartType != 'section') { - throw new \BadMethodCallException(); - } + $this->checkValidity('checkbox'); $name = String::toUTF8($name); $text = String::toUTF8($text); @@ -416,7 +418,7 @@ abstract class Container */ public function getSectionId() { - return $this->sectionId; + return $this->containerId; } /** @@ -436,10 +438,7 @@ abstract class Container */ public function getRelationId() { - if (!in_array($this->containerType, array('header', 'footer'))) { - throw new \BadMethodCallException(); - } - + $this->checkValidity('relationid'); return $this->relationId; } @@ -450,10 +449,7 @@ abstract class Container */ public function setRelationId($rId) { - if (!in_array($this->containerType, array('header', 'footer'))) { - throw new \BadMethodCallException(); - } - + $this->checkValidity('relationid'); $this->relationId = $rId; } @@ -490,4 +486,56 @@ abstract class Container { return $this->addFootnote($paragraphStyle); } + + /** + * Check if a method is allowed for the current container + * + * @param string $element + * @return boolean + */ + private function checkValidity($method) + { + $validContainers = array( + 'text' => 'all', + 'link' => 'all', + 'textbreak' => 'all', + 'image' => 'all', + 'textrun' => array('section', 'header', 'footer', 'cell'), + 'listitem' => array('section', 'header', 'footer', 'cell'), + 'checkbox' => array('section', 'header', 'footer', 'cell'), + 'table' => array('section', 'header', 'footer'), + 'object' => array('section', 'textrun', 'cell', 'footnote'), + 'footnote' => array('section', 'textrun', 'cell'), + 'preservetext' => array('header', 'footer', 'cell'), + 'relationid' => array('header', 'footer'), + 'title' => array('section'), + ); + $validContainerInContainers = array( + 'preservetext' => array(array('cell'), array('header', 'footer')), + 'object' => array(array('cell', 'textrun'), array('section')), + 'footnote' => array(array('cell', 'textrun'), array('section')), + ); + + // Check if a method is valid for current container + if (array_key_exists($method, $validContainers)) { + if (is_array($validContainers[$method])) { + if (!in_array($this->container, $validContainers[$method])) { + throw new \BadMethodCallException(); + } + } + } + // Check if a method is valid for current container, located in other container + if (array_key_exists($method, $validContainerInContainers)) { + $rules = $validContainerInContainers[$method]; + $containers = $rules[0]; + $allowedDocParts = $rules[1]; + foreach ($containers as $container) { + if ($this->container == $container && !in_array($this->docPart, $allowedDocParts)) { + throw new \BadMethodCallException(); + } + } + } + + return true; + } } diff --git a/src/PhpWord/Container/Footer.php b/src/PhpWord/Container/Footer.php index 37884b86..56f06383 100755 --- a/src/PhpWord/Container/Footer.php +++ b/src/PhpWord/Container/Footer.php @@ -21,7 +21,7 @@ class Footer extends Container */ public function __construct($sectionId) { - $this->containerType = 'footer'; - $this->sectionId = $sectionId; + $this->container = 'footer'; + $this->containerId = $sectionId; } } diff --git a/src/PhpWord/Container/Header.php b/src/PhpWord/Container/Header.php index 3c702406..126f9198 100755 --- a/src/PhpWord/Container/Header.php +++ b/src/PhpWord/Container/Header.php @@ -40,8 +40,8 @@ class Header extends Container */ public function __construct($sectionId) { - $this->containerType = 'header'; - $this->sectionId = $sectionId; + $this->container = 'header'; + $this->containerId = $sectionId; } /** diff --git a/src/PhpWord/Container/Section.php b/src/PhpWord/Container/Section.php index dcef4707..b549280d 100644 --- a/src/PhpWord/Container/Section.php +++ b/src/PhpWord/Container/Section.php @@ -50,8 +50,8 @@ class Section extends Container */ public function __construct($sectionCount, $settings = null) { - $this->containerType = 'section'; - $this->sectionId = $sectionCount; + $this->container = 'section'; + $this->containerId = $sectionCount; $this->settings = new Settings(); $this->setSettings($settings); } @@ -112,7 +112,7 @@ class Section extends Container */ public function addHeader() { - $header = new Header($this->sectionId); + $header = new Header($this->containerId); $this->headers[] = $header; return $header; } @@ -124,7 +124,7 @@ class Section extends Container */ public function addFooter() { - $footer = new Footer($this->sectionId); + $footer = new Footer($this->containerId); $this->footer = $footer; return $footer; } diff --git a/src/PhpWord/Element/Footnote.php b/src/PhpWord/Element/Footnote.php index aa6af010..3d65a256 100644 --- a/src/PhpWord/Element/Footnote.php +++ b/src/PhpWord/Element/Footnote.php @@ -38,7 +38,7 @@ class Footnote extends Container */ public function __construct($paragraphStyle = null) { - $this->containerType = 'footnote'; + $this->container = 'footnote'; // Set paragraph style if (is_array($paragraphStyle)) { $this->paragraphStyle = new Paragraph(); diff --git a/src/PhpWord/Element/Table/Cell.php b/src/PhpWord/Element/Table/Cell.php index 3f795a28..2a15a263 100755 --- a/src/PhpWord/Element/Table/Cell.php +++ b/src/PhpWord/Element/Table/Cell.php @@ -34,15 +34,15 @@ class Cell extends Container /** * Create new instance * - * @param string $docPartType section|header|footer + * @param string $docPart section|header|footer * @param int $docPartId * @param int $width * @param array|CellStyle $style */ - public function __construct($docPartType, $docPartId, $width = null, $style = null) + public function __construct($docPart, $docPartId, $width = null, $style = null) { - $this->containerType = 'cell'; - $this->docPartType = $docPartType; + $this->container = 'cell'; + $this->docPart = $docPart; $this->docPartId = $docPartId; $this->width = $width; $this->cellStyle = new CellStyle(); diff --git a/src/PhpWord/Element/TextRun.php b/src/PhpWord/Element/TextRun.php index 35701d45..5cf1a0c6 100755 --- a/src/PhpWord/Element/TextRun.php +++ b/src/PhpWord/Element/TextRun.php @@ -31,10 +31,10 @@ class TextRun extends Container * @param string $docPartType section|header|footer * @param int $docPartId */ - public function __construct($paragraphStyle = null, $docPartType = 'section', $docPartId = 1) + public function __construct($paragraphStyle = null, $docPart = 'section', $docPartId = 1) { - $this->containerType = 'textrun'; - $this->docPartType = $docPartType; + $this->container = 'textrun'; + $this->docPart = $docPart; $this->docPartId = $docPartId; // Set paragraph style if (is_array($paragraphStyle)) { diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index dea9e1f3..ccfa1649 100755 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -79,7 +79,7 @@ class Media $isMemImage = false; if (!is_null($image)) { $isMemImage = $image->getIsMemImage(); - $extension = $image->getImageExtension(); + $ext = $image->getImageExtension(); } if ($isMemImage) { $media['isMemImage'] = true; @@ -87,10 +87,10 @@ class Media $media['imagefunction'] = $image->getImageFunction(); } $folder = 'media'; - $file = $type . $cImg . '.' . strtolower($extension); + $file = $type . $cImg . '.' . strtolower($ext); } elseif ($type === 'oleObject') { $cObj++; - $folder = 'embedding'; + $folder = 'embeddings'; $file = $type . $cObj . '.bin'; } $media['source'] = $src; @@ -247,8 +247,8 @@ class Media /** * Add new media element * - * @param string $container section|header|footer|footnotes - * @param string $mediaType image|embedding|hyperlink + * @param string $container section|headerx|footerx|footnote + * @param string $mediaType image|embeddings|hyperlink * @param string $source * @param Image $image * @return int @@ -267,35 +267,49 @@ class Media $mediaTypeCount = self::countMediaElements($container, $mediaType); $mediaData = array(); $relId = $mediaCount + 1; + $target = null; $mediaTypeCount++; + + // Images if ($mediaType == 'image') { $isMemImage = false; if (!is_null($image)) { $isMemImage = $image->getIsMemImage(); - $extension = $image->getImageExtension(); + $ext = $image->getImageExtension(); + $ext = strtolower($ext); } if ($isMemImage) { $mediaData['isMemImage'] = true; $mediaData['createfunction'] = $image->getImageCreateFunction(); $mediaData['imagefunction'] = $image->getImageFunction(); } - $file = 'image' . $mediaTypeCount . '.' . strtolower($extension); - if ($container != 'footnotes') { - $file = $container . '_' . $file; - } - $target = 'media/' . $file; + $target = "media/{$container}_image{$mediaTypeCount}.{$ext}"; + // Objects + } elseif ($mediaType == 'embeddings') { + $file = "oleObject{$mediaTypeCount}.bin"; + $target = "embeddings/{$container}_oleObject{$mediaTypeCount}.bin"; + // Links } elseif ($mediaType == 'hyperlink') { $target = $source; } + $mediaData['source'] = $source; $mediaData['target'] = $target; $mediaData['type'] = $mediaType; $mediaData['rID'] = $relId; self::$media[$container][$mediaId] = $mediaData; - - return $relId; + if ($mediaType === 'embeddings') { + return array($relId, ++self::$objectId); + } else { + return $relId; + } } else { - return self::$media[$container][$mediaId]['rID']; + if ($mediaType === 'embeddings') { + $relId = self::$media[$container][$mediaId]['rID']; + return array($relId, ++self::$objectId); + } else { + return self::$media[$container][$mediaId]['rID']; + } } } diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index dd144da1..298b93cc 100755 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -121,7 +121,7 @@ class Word2007 extends Writer implements IWriter $this->getWriterPart('documentrels')->writeHeaderFooterRels($hdrMedia) ); foreach ($hdrMedia as $element) { - if ($element['type'] == 'image') { + if ($element['type'] != 'hyperlink') { $this->addFileToPackage($objZip, $element); } } @@ -137,7 +137,7 @@ class Word2007 extends Writer implements IWriter $this->getWriterPart('documentrels')->writeHeaderFooterRels($ftrMedia) ); foreach ($ftrMedia as $element) { - if ($element['type'] == 'image') { + if ($element['type'] != 'hyperlink') { $this->addFileToPackage($objZip, $element); } } @@ -155,10 +155,10 @@ class Word2007 extends Writer implements IWriter foreach ($_headers as $index => &$_header) { $_cHdrs++; $_header->setRelationId(++$rID); - $hdrFile = 'header' . $_cHdrs . '.xml'; + $hdrFile = "header{$_cHdrs}.xml"; $sectionElements[] = array('target' => $hdrFile, 'type' => 'header', 'rID' => $rID); $objZip->addFromString( - 'word/' . $hdrFile, + "word/{$hdrFile}", $this->getWriterPart('header')->writeHeader($_header) ); } @@ -167,10 +167,10 @@ class Word2007 extends Writer implements IWriter if (!is_null($_footer)) { $_footer->setRelationId(++$rID); $_footerCount = $_footer->getSectionId(); - $ftrFile = 'footer' . $_footerCount . '.xml'; + $ftrFile = "footer{$_footerCount}.xml"; $sectionElements[] = array('target' => $ftrFile, 'type' => 'footer', 'rID' => $rID); $objZip->addFromString( - 'word/' . $ftrFile, + "word/{$ftrFile}", $this->getWriterPart('footer')->writeFooter($_footer) ); } @@ -181,24 +181,24 @@ class Word2007 extends Writer implements IWriter // Push to document.xml.rels $sectionElements[] = array('target' => 'footnotes.xml', 'type' => 'footnotes', 'rID' => ++$rID); // Add footnote media to package - $footnotesMedia = Media::getMediaElements('footnotes'); - if (!empty($footnotesMedia)) { - foreach ($footnotesMedia as $media) { - if ($media['type'] == 'image') { + $footnoteMedia = Media::getMediaElements('footnote'); + if (!empty($footnoteMedia)) { + foreach ($footnoteMedia as $media) { + if ($media['type'] != 'hyperlink') { $this->addFileToPackage($objZip, $media); } } } // Write footnotes.xml $objZip->addFromString( - "word/footnotes.xml", + 'word/footnotes.xml', $this->getWriterPart('footnotes')->writeFootnotes(Footnote::getFootnoteElements()) ); // Write footnotes.xml.rels - if (!empty($footnotesMedia)) { + if (!empty($footnoteMedia)) { $objZip->addFromString( 'word/_rels/footnotes.xml.rels', - $this->getWriterPart('footnotesrels')->writeFootnotesRels($footnotesMedia) + $this->getWriterPart('footnotesrels')->writeFootnotesRels($footnoteMedia) ); } } diff --git a/src/PhpWord/Writer/Word2007/Base.php b/src/PhpWord/Writer/Word2007/Base.php index 7af96b03..d5c0160a 100644 --- a/src/PhpWord/Writer/Word2007/Base.php +++ b/src/PhpWord/Writer/Word2007/Base.php @@ -44,11 +44,8 @@ class Base extends WriterPart * @param Text $text * @param boolean $withoutP */ - protected function writeText( - XMLWriter $xmlWriter, - Text $text, - $withoutP = false - ) { + protected function writeText(XMLWriter $xmlWriter, Text $text, $withoutP = false) + { $styleFont = $text->getFontStyle(); $styleParagraph = $text->getParagraphStyle(); $strText = htmlspecialchars($text->getText()); @@ -76,10 +73,8 @@ class Base extends WriterPart * @param XMLWriter $xmlWriter * @param TextRun $textrun */ - protected function writeTextRun( - XMLWriter $xmlWriter, - TextRun $textrun - ) { + protected function writeTextRun(XMLWriter $xmlWriter, TextRun $textrun) + { $elements = $textrun->getElements(); $styleParagraph = $textrun->getParagraphStyle(); $xmlWriter->startElement('w:p'); @@ -94,6 +89,8 @@ class Base extends WriterPart $xmlWriter->writeElement('w:br'); } elseif ($element instanceof Image) { $this->writeImage($xmlWriter, $element, true); + } elseif ($element instanceof Object) { + $this->writeObject($xmlWriter, $element, true); } elseif ($element instanceof Footnote) { $this->writeFootnote($xmlWriter, $element, true); } @@ -109,11 +106,8 @@ class Base extends WriterPart * @param Link $link * @param boolean $withoutP */ - protected function writeLink( - XMLWriter $xmlWriter, - Link $link, - $withoutP = false - ) { + protected function writeLink(XMLWriter $xmlWriter, Link $link, $withoutP = false) + { $rID = $link->getRelationId(); $linkName = $link->getLinkName(); if (is_null($linkName)) { @@ -196,10 +190,8 @@ class Base extends WriterPart * @param XMLWriter $xmlWriter * @param PreserveText $textrun */ - protected function writePreserveText( - XMLWriter $xmlWriter, - PreserveText $textrun - ) { + protected function writePreserveText(XMLWriter $xmlWriter, PreserveText $textrun) + { $styleFont = $textrun->getFontStyle(); $styleParagraph = $textrun->getParagraphStyle(); @@ -467,11 +459,8 @@ class Base extends WriterPart * @param Image $image * @param boolean $withoutP */ - protected function writeImage( - XMLWriter $xmlWriter, - Image $image, - $withoutP = false - ) { + protected function writeImage(XMLWriter $xmlWriter, Image $image, $withoutP = false) + { $rId = $image->getRelationId(); $style = $image->getStyle(); @@ -601,8 +590,9 @@ class Base extends WriterPart * * @param XMLWriter $xmlWriter * @param Object $object + * @param boolean $withoutP */ - protected function writeObject(XMLWriter $xmlWriter, Object $object) + protected function writeObject(XMLWriter $xmlWriter, Object $object, $withoutP = false) { $rIdObject = $object->getRelationId(); $rIdImage = $object->getImageRelationId(); @@ -611,7 +601,9 @@ class Base extends WriterPart $style = $object->getStyle(); $align = $style->getAlign(); - $xmlWriter->startElement('w:p'); + if (!$withoutP) { + $xmlWriter->startElement('w:p'); + } if (!is_null($align)) { $xmlWriter->startElement('w:pPr'); $xmlWriter->startElement('w:jc'); @@ -643,7 +635,9 @@ class Base extends WriterPart $xmlWriter->endElement(); // o:OLEObject $xmlWriter->endElement(); // w:object $xmlWriter->endElement(); // w:r - $xmlWriter->endElement(); // w:p + if (!$withoutP) { + $xmlWriter->endElement(); // w:p + } } /** @@ -653,11 +647,8 @@ class Base extends WriterPart * @param Footnote $footnote * @param boolean $withoutP */ - protected function writeFootnote( - XMLWriter $xmlWriter, - Footnote $footnote, - $withoutP = false - ) { + protected function writeFootnote(XMLWriter $xmlWriter, Footnote $footnote, $withoutP = false) + { if (!$withoutP) { $xmlWriter->startElement('w:p'); } @@ -682,12 +673,8 @@ class Base extends WriterPart * @param boolean $withoutP * @param boolean $checkState */ - protected function writeCheckBox( - XMLWriter $xmlWriter, - CheckBox $checkbox, - $withoutP = false, - $checkState = false - ) { + protected function writeCheckBox(XMLWriter $xmlWriter, CheckBox $checkbox, $withoutP = false, $checkState = false) + { $name = htmlspecialchars($checkbox->getName()); $name = String::controlCharacterPHP2OOXML($name); $text = htmlspecialchars($checkbox->getText()); @@ -758,11 +745,8 @@ class Base extends WriterPart * @param Paragraph $style * @param bool $withoutPPR */ - protected function writeParagraphStyle( - XMLWriter $xmlWriter, - Paragraph $style, - $withoutPPR = false - ) { + protected function writeParagraphStyle(XMLWriter $xmlWriter, Paragraph $style, $withoutPPR = false) + { $align = $style->getAlign(); $spacing = $style->getSpacing(); @@ -965,11 +949,8 @@ class Base extends WriterPart * @param TableStyle $style * @param boolean $isFullStyle */ - protected function writeTableStyle( - XMLWriter $xmlWriter, - TableStyle $style, - $isFullStyle = true - ) { + protected function writeTableStyle(XMLWriter $xmlWriter, TableStyle $style, $isFullStyle = true) + { $bgColor = $style->getBgColor(); $brdCol = $style->getBorderColor(); @@ -1094,11 +1075,8 @@ class Base extends WriterPart * @param string $type * @param TableStyle $style */ - protected function writeRowStyle( - XMLWriter $xmlWriter, - $type, - TableStyle $style - ) { + protected function writeRowStyle(XMLWriter $xmlWriter, $type, TableStyle $style) + { $brdSz = $style->getBorderSize(); $brdCol = $style->getBorderColor(); $bgColor = $style->getBgColor(); @@ -1265,6 +1243,26 @@ class Base extends WriterPart } } + /** + * Write media rels (image, embeddings, hyperlink) + * + * @param XMLWriter $xmlWriter + * @param array mediaRels + */ + protected function writeMediaRels(XMLWriter $xmlWriter, $mediaRels) + { + $rTypePrefix = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/'; + foreach ($mediaRels as $mediaRel) { + $rId = $mediaRel['rID']; + $rType = $mediaRel['type']; + $rName = $mediaRel['target']; // file name + $targetMode = ($rType == 'hyperlink') ? 'External' : ''; + $rType = $rTypePrefix . ($rType == 'embeddings' ? 'oleObject' : $rType); + $this->writeRel($xmlWriter, $rId, $rType, $rName, $targetMode); + } + + } + /** * Write individual rels entry * @@ -1274,28 +1272,19 @@ class Base extends WriterPart * @param string $pTarget Relationship target * @param string $pTargetMode Relationship target mode */ - protected function writeRelationship( - XMLWriter $xmlWriter, - $pId = 1, - $pType = '', - $pTarget = '', - $pTargetMode = '' - ) { + protected function writeRel(XMLWriter $xmlWriter, $pId, $pType, $pTarget, $pTargetMode = '') + { if ($pType != '' && $pTarget != '') { if (strpos($pId, 'rId') === false) { $pId = 'rId' . $pId; } - - // Write relationship $xmlWriter->startElement('Relationship'); $xmlWriter->writeAttribute('Id', $pId); $xmlWriter->writeAttribute('Type', $pType); $xmlWriter->writeAttribute('Target', $pTarget); - if ($pTargetMode != '') { $xmlWriter->writeAttribute('TargetMode', $pTargetMode); } - $xmlWriter->endElement(); } else { throw new Exception("Invalid parameters passed."); @@ -1309,11 +1298,8 @@ class Base extends WriterPart * @param Paragraph|string $styleParagraph * @param boolean $withoutPPR */ - protected function writeInlineParagraphStyle( - XMLWriter $xmlWriter, - $styleParagraph = null, - $withoutPPR = false - ) { + protected function writeInlineParagraphStyle(XMLWriter $xmlWriter, $styleParagraph = null, $withoutPPR = false) + { if ($styleParagraph instanceof Paragraph) { $this->writeParagraphStyle($xmlWriter, $styleParagraph, $withoutPPR); } else { @@ -1337,10 +1323,8 @@ class Base extends WriterPart * @param XMLWriter $xmlWriter * @param Font|string $styleFont */ - protected function writeInlineFontStyle( - XMLWriter $xmlWriter, - $styleFont = null - ) { + protected function writeInlineFontStyle(XMLWriter $xmlWriter, $styleFont = null) + { if ($styleFont instanceof Font) { $this->writeFontStyle($xmlWriter, $styleFont); } else { diff --git a/src/PhpWord/Writer/Word2007/ContentTypes.php b/src/PhpWord/Writer/Word2007/ContentTypes.php index 98121133..86689823 100755 --- a/src/PhpWord/Writer/Word2007/ContentTypes.php +++ b/src/PhpWord/Writer/Word2007/ContentTypes.php @@ -151,7 +151,6 @@ class ContentTypes extends WriterPart } } - $xmlWriter->endElement(); // Return diff --git a/src/PhpWord/Writer/Word2007/DocumentRels.php b/src/PhpWord/Writer/Word2007/DocumentRels.php index 0650d46a..d39527a9 100755 --- a/src/PhpWord/Writer/Word2007/DocumentRels.php +++ b/src/PhpWord/Writer/Word2007/DocumentRels.php @@ -35,7 +35,7 @@ class DocumentRels extends Base $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); // Relationship word/document.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles', @@ -43,7 +43,7 @@ class DocumentRels extends Base ); // Relationship word/numbering.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, 2, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering', @@ -51,7 +51,7 @@ class DocumentRels extends Base ); // Relationship word/settings.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, 3, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings', @@ -59,7 +59,7 @@ class DocumentRels extends Base ); // Relationship word/settings.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, 4, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme', @@ -67,7 +67,7 @@ class DocumentRels extends Base ); // Relationship word/settings.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, 5, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings', @@ -75,73 +75,34 @@ class DocumentRels extends Base ); // Relationship word/settings.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, 6, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable', 'fontTable.xml' ); - // Relationships to Images / Embeddings / Headers / Footers - foreach ($_relsCollection as $relation) { - $relationType = $relation['type']; - $relationName = $relation['target']; - $relationId = $relation['rID']; - $targetMode = ($relationType == 'hyperlink') ? 'External' : ''; - - $this->writeRelationship( - $xmlWriter, - $relationId, - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, - $relationName, - $targetMode - ); - } - - - $xmlWriter->endElement(); + $this->writeMediaRels($xmlWriter, $_relsCollection); + $xmlWriter->endElement(); // Relationships // Return return $xmlWriter->getData(); } /** - * Write header footer rels + * Write header footer rels word/_rels/*.xml.rels * * @param array $_relsCollection */ public function writeHeaderFooterRels($_relsCollection) { - // Create XML writer $xmlWriter = $this->getXmlWriter(); - - // XML header $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships $xmlWriter->startElement('Relationships'); $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Relationships to Images / Embeddings / Headers / Footers - foreach ($_relsCollection as $relation) { - $relationType = $relation['type']; - $relationName = $relation['target']; - $relationId = $relation['rID']; - $targetMode = ($relationType == 'hyperlink') ? 'External' : ''; - - $this->writeRelationship( - $xmlWriter, - $relationId, - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, - $relationName, - $targetMode - ); - } - - + $this->writeMediaRels($xmlWriter, $_relsCollection); $xmlWriter->endElement(); - // Return return $xmlWriter->getData(); } } diff --git a/src/PhpWord/Writer/Word2007/Footnotes.php b/src/PhpWord/Writer/Word2007/Footnotes.php index 628c1e35..9ae7473a 100644 --- a/src/PhpWord/Writer/Word2007/Footnotes.php +++ b/src/PhpWord/Writer/Word2007/Footnotes.php @@ -13,6 +13,7 @@ use PhpOffice\PhpWord\Element\Footnote; use PhpOffice\PhpWord\Element\Text; use PhpOffice\PhpWord\Element\Link; use PhpOffice\PhpWord\Element\Image; +use PhpOffice\PhpWord\Element\Object; use PhpOffice\PhpWord\Element\TextBreak; use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\Shared\XMLWriter; @@ -119,6 +120,8 @@ class Footnotes extends Base $this->writeLink($xmlWriter, $element, true); } elseif ($element instanceof Image) { $this->writeImage($xmlWriter, $element, true); + } elseif ($element instanceof Object) { + $this->writeObject($xmlWriter, $element, true); } elseif ($element instanceof TextBreak) { $xmlWriter->writeElement('w:br'); } diff --git a/src/PhpWord/Writer/Word2007/FootnotesRels.php b/src/PhpWord/Writer/Word2007/FootnotesRels.php index 405df69f..49ca2181 100644 --- a/src/PhpWord/Writer/Word2007/FootnotesRels.php +++ b/src/PhpWord/Writer/Word2007/FootnotesRels.php @@ -24,29 +24,13 @@ class FootnotesRels extends Base */ public function writeFootnotesRels($_relsCollection) { - // Create XML writer $xmlWriter = $this->getXmlWriter(); - - // XML header $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); - - // Relationships $xmlWriter->startElement('Relationships'); $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); - - // Relationships to Links - foreach ($_relsCollection as $relation) { - $relationType = $relation['type']; - $relationName = $relation['target']; - $relationId = $relation['rID']; - $targetMode = ($relationType == 'hyperlink') ? 'External' : ''; - - $this->writeRelationship($xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/' . $relationType, $relationName, $targetMode); - } - + $this->writeMediaRels($xmlWriter, $_relsCollection); $xmlWriter->endElement(); - // Return return $xmlWriter->getData(); } } diff --git a/src/PhpWord/Writer/Word2007/Rels.php b/src/PhpWord/Writer/Word2007/Rels.php index a675057f..5619d978 100755 --- a/src/PhpWord/Writer/Word2007/Rels.php +++ b/src/PhpWord/Writer/Word2007/Rels.php @@ -38,7 +38,7 @@ class Rels extends Base $relationId = 1; // Relationship word/document.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, $relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument', @@ -46,7 +46,7 @@ class Rels extends Base ); // Relationship docProps/core.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, ++$relationId, 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties', @@ -54,7 +54,7 @@ class Rels extends Base ); // Relationship docProps/app.xml - $this->writeRelationship( + $this->writeRel( $xmlWriter, ++$relationId, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties',