diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php index 06a94489..dc87cf18 100644 --- a/src/PhpWord/Element/AbstractContainer.php +++ b/src/PhpWord/Element/AbstractContainer.php @@ -56,38 +56,24 @@ abstract class AbstractContainer extends AbstractElement // Get arguments $args = func_get_args(); - $argsCount = func_num_args(); $withoutP = in_array($this->container, array('TextRun', 'Footnote', 'Endnote', 'ListItemRun')); if ($withoutP && ($elementName == 'Text' || $elementName == 'PreserveText')) { - $args[3] = null; + $args[3] = null; // Remove paragraph style for texts in textrun } - // Create element dynamically - + // Create element using reflection + $reflection = new \ReflectionClass($elementClass); + $elementArgs = $args; + array_shift($elementArgs); // Shift an element off the beginning of array: the $elementName /** @var \PhpOffice\PhpWord\Element\AbstractElement $element Type hint */ - if ($argsCount == 2) { // TextRun, TextBox, Table, Footnote, Endnote - $element = new $elementClass($args[1]); - } elseif ($argsCount == 3) { // Object, TextBreak, Title - $element = new $elementClass($args[1], $args[2]); - } elseif ($argsCount == 4) { // PreserveText, Text, Image - $element = new $elementClass($args[1], $args[2], $args[3]); - } elseif ($argsCount == 5) { // CheckBox, Link, ListItemRun, TOC - $element = new $elementClass($args[1], $args[2], $args[3], $args[4]); - } elseif ($argsCount == 6) { // ListItem - $element = new $elementClass($args[1], $args[2], $args[3], $args[4], $args[5]); - } else { // Page Break - $element = new $elementClass(); - } + $element = $reflection->newInstanceArgs($elementArgs); // Set relation Id for media collection $mediaContainer = $this->getMediaContainer(); if (in_array($elementName, array('Link', 'Image', 'Object'))) { - if ($elementName == 'Image') { - /** @var \PhpOffice\PhpWord\Element\Image $element Type hint */ - $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1], $element); - } else { - $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1]); - } + /** @var \PhpOffice\PhpWord\Element\Image $element Type hint */ + $image = ($elementName == 'Image') ? $element : null; + $rId = Media::addElement($mediaContainer, strtolower($elementName), $args[1], $image); $element->setRelationId($rId); } if ($elementName == 'Object') { diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index c38fda2c..de859ad2 100644 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -284,6 +284,7 @@ class Image extends AbstractElement * * @param bool $base64 * @return string|null + * @since 0.11.0 */ public function getImageStringData($base64 = false) { @@ -291,6 +292,7 @@ class Image extends AbstractElement $actualSource = null; $imageBinary = null; $imageData = null; + $isTemp = false; // Get actual source from archive image or other source // Return null if not found @@ -301,6 +303,7 @@ class Image extends AbstractElement $zip = new ZipArchive(); if ($zip->open($zipFilename) !== false) { if ($zip->locateName($imageFilename)) { + $isTemp = true; $zip->extractTo(sys_get_temp_dir(), $imageFilename); $actualSource = sys_get_temp_dir() . DIRECTORY_SEPARATOR . $imageFilename; } @@ -313,7 +316,7 @@ class Image extends AbstractElement return null; } - // Read image binary data and convert to hex + // Read image binary data and convert to hex/base64 string if ($this->sourceType == self::SOURCE_GD) { $imageResource = call_user_func($this->imageCreateFunc, $actualSource); ob_start(); @@ -335,6 +338,11 @@ class Image extends AbstractElement } } + // Delete temporary file if necessary + if ($isTemp === true) { + @unlink($actualSource); + } + return $imageData; } diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index 8cb82013..7f35841c 100644 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -139,37 +139,53 @@ class Media * Get media elements * * @param string $container section|headerx|footerx|footnote|endnote - * @param string $mediaType image|object|link + * @param string $type image|object|link * @return array * @since 0.10.0 */ - public static function getElements($container, $mediaType = null) + public static function getElements($container, $type = null) { - $mediaElements = array(); + $elements = array(); // If header/footer, search for headerx and footerx where x is number if ($container == 'header' || $container == 'footer') { foreach (self::$elements as $key => $val) { if (substr($key, 0, 6) == $container) { - $mediaElements[$key] = $val; + $elements[$key] = $val; } } + return $elements; } else { if (!array_key_exists($container, self::$elements)) { - return $mediaElements; + return $elements; } - foreach (self::$elements[$container] as $mediaKey => $mediaData) { - if (!is_null($mediaType)) { - if ($mediaType == $mediaData['type']) { - $mediaElements[$mediaKey] = $mediaData; - } - } else { - $mediaElements[$mediaKey] = $mediaData; + return self::getElementsByType($container, $type); + } + } + + /** + * Get elements by media type + * + * @param string $container section|footnote|endnote + * @param string $type image|object|link + * @return array + * @since 0.11.0 Splitted from `getElements` to reduce complexity + */ + private static function getElementsByType($container, $type = null) + { + $elements = array(); + + foreach (self::$elements[$container] as $key => $data) { + if ($type !== null) { + if ($type == $data['type']) { + $elements[$key] = $data; } + } else { + $elements[$key] = $data; } } - return $mediaElements; + return $elements; } /** diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index 2936884f..8f14daed 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -453,25 +453,41 @@ abstract class AbstractPart $attribute = ($attribute === null) ? 'w:val' : $attribute; $attributeValue = $xmlReader->getAttribute($attribute, $node); - // Assign style value based on conversion model - if ($method == self::READ_VALUE) { - $styles[$styleProp] = $attributeValue; - } elseif ($method == self::READ_SIZE) { - $styles[$styleProp] = $attributeValue / 2; - } elseif ($method == self::READ_TRUE) { - $styles[$styleProp] = true; - } elseif ($method == self::READ_FALSE) { - $styles[$styleProp] = false; - } elseif ($method == self::READ_EQUAL && $attributeValue == $expected) { - $styles[$styleProp] = true; + $styleValue = $this->readStyleDef($method, $attributeValue, $expected); + if ($styleValue !== null) { + $styles[$styleProp] = $styleValue; } } } - /** @var array $styles Type hint */ return $styles; } + /** + * Return style definition based on conversion method + * + * @param string $method + * @param mixed $attributeValue + * @param mixed $expected + * @return mixed + */ + private function readStyleDef($method, $attributeValue, $expected) + { + $style = $attributeValue; + + if ($method == self::READ_SIZE) { + $style = $attributeValue / 2; + } elseif ($method == self::READ_TRUE) { + $style = true; + } elseif ($method == self::READ_FALSE) { + $style = false; + } elseif ($method == self::READ_EQUAL && $attributeValue == $expected) { + $style = true; + } + + return $style; + } + /** * Returns the target of image, object, or link as stored in ::readMainRels * diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 501d2404..4faacfb1 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -214,7 +214,7 @@ class Html */ case 'li': $cNodes = $node->childNodes; - if (count($cNodes) > 0) { + if ($cNodes->length > 0) { $text = ''; foreach ($cNodes as $cNode) { if ($cNode->nodeName == '#text') { @@ -240,7 +240,7 @@ class Html */ if ($node->nodeName != 'li') { $cNodes = $node->childNodes; - if (count($cNodes) > 0) { + if ($cNodes->length > 0) { foreach ($cNodes as $cNode) { self::parseNode($cNode, $newobject, $styles, $data); } diff --git a/src/PhpWord/Writer/Word2007/Element/TextBreak.php b/src/PhpWord/Writer/Word2007/Element/TextBreak.php index 227b1b30..5f4bd3ce 100644 --- a/src/PhpWord/Writer/Word2007/Element/TextBreak.php +++ b/src/PhpWord/Writer/Word2007/Element/TextBreak.php @@ -37,15 +37,13 @@ class TextBreak extends Text if (!$this->withoutP) { $hasStyle = $element->hasStyle(); + $this->writeOpeningWP(); if ($hasStyle) { - $this->writeOpeningWP(); $xmlWriter->startElement('w:pPr'); $this->writeFontStyle(); $xmlWriter->endElement(); // w:pPr - $this->writeClosingWP(); - } else { - $xmlWriter->writeElement('w:p'); } + $this->writeClosingWP(); } else { $xmlWriter->writeElement('w:br'); } diff --git a/src/PhpWord/Writer/Word2007/Part/Numbering.php b/src/PhpWord/Writer/Word2007/Part/Numbering.php index 2678ac55..df5abd9b 100644 --- a/src/PhpWord/Writer/Word2007/Part/Numbering.php +++ b/src/PhpWord/Writer/Word2007/Part/Numbering.php @@ -24,6 +24,8 @@ use PhpOffice\PhpWord\Style\NumberingLevel; /** * Word2007 numbering part writer: word/numbering.xml + * + * @since 0.10.0 */ class Numbering extends AbstractPart { @@ -97,12 +99,6 @@ class Numbering extends AbstractPart */ private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level) { - $tabPos = $level->getTabPos(); - $left = $level->getLeft(); - $hanging = $level->getHanging(); - $font = $level->getFont(); - $hint = $level->getHint(); - $xmlWriter->startElement('w:lvl'); $xmlWriter->writeAttribute('w:ilvl', $level->getLevel()); @@ -124,48 +120,64 @@ class Numbering extends AbstractPart } } - // Paragraph styles - if (!is_null($tabPos) || !is_null($left) || !is_null($hanging)) { - $xmlWriter->startElement('w:pPr'); - if (!is_null($tabPos)) { - $xmlWriter->startElement('w:tabs'); - $xmlWriter->startElement('w:tab'); - $xmlWriter->writeAttribute('w:val', 'num'); - $xmlWriter->writeAttribute('w:pos', $tabPos); - $xmlWriter->endElement(); // w:tab - $xmlWriter->endElement(); // w:tabs - } - if (!is_null($left) || !is_null($hanging)) { - $xmlWriter->startElement('w:ind'); - if (!is_null($left)) { - $xmlWriter->writeAttribute('w:left', $left); - } - if (!is_null($hanging)) { - $xmlWriter->writeAttribute('w:hanging', $hanging); - } - $xmlWriter->endElement(); // w:ind - } - $xmlWriter->endElement(); // w:pPr - } + // Paragraph & font styles + $this->writeParagraph($xmlWriter, $level); + $this->writeFont($xmlWriter, $level); - // Font styles - if (!is_null($font) || !is_null($hint)) { - $xmlWriter->startElement('w:rPr'); - $xmlWriter->startElement('w:rFonts'); - if (!is_null($font)) { - $xmlWriter->writeAttribute('w:ascii', $font); - $xmlWriter->writeAttribute('w:hAnsi', $font); - $xmlWriter->writeAttribute('w:cs', $font); - } - if (!is_null($hint)) { - $xmlWriter->writeAttribute('w:hint', $hint); - } - $xmlWriter->endElement(); // w:rFonts - $xmlWriter->endElement(); // w:rPr - } $xmlWriter->endElement(); // w:lvl } + /** + * Write level paragraph + * + * @since 0.11.0 + * @todo Use paragraph style writer + */ + private function writeParagraph(XMLWriter $xmlWriter, NumberingLevel $level) + { + $tabPos = $level->getTabPos(); + $left = $level->getLeft(); + $hanging = $level->getHanging(); + + $xmlWriter->startElement('w:pPr'); + + $xmlWriter->startElement('w:tabs'); + $xmlWriter->startElement('w:tab'); + $xmlWriter->writeAttribute('w:val', 'num'); + $xmlWriter->writeAttributeIf($tabPos !== null, 'w:pos', $tabPos); + $xmlWriter->writeAttribute('w:pos', $tabPos); + $xmlWriter->endElement(); // w:tab + $xmlWriter->endElement(); // w:tabs + + $xmlWriter->startElement('w:ind'); + $xmlWriter->writeAttributeIf($left !== null, 'w:left', $left); + $xmlWriter->writeAttributeIf($hanging !== null, 'w:hanging', $hanging); + $xmlWriter->endElement(); // w:ind + + $xmlWriter->endElement(); // w:pPr + } + + /** + * Write level font + * + * @since 0.11.0 + * @todo Use font style writer + */ + private function writeFont(XMLWriter $xmlWriter, NumberingLevel $level) + { + $font = $level->getFont(); + $hint = $level->getHint(); + + $xmlWriter->startElement('w:rPr'); + $xmlWriter->startElement('w:rFonts'); + $xmlWriter->writeAttributeIf($font !== null, 'w:ascii', $font); + $xmlWriter->writeAttributeIf($font !== null, 'w:hAnsi', $font); + $xmlWriter->writeAttributeIf($font !== null, 'w:cs', $font); + $xmlWriter->writeAttributeIf($hint !== null, 'w:hint', $hint); + $xmlWriter->endElement(); // w:rFonts + $xmlWriter->endElement(); // w:rPr + } + /** * Get random hexadecimal number value * diff --git a/src/PhpWord/Writer/Word2007/Part/Rels.php b/src/PhpWord/Writer/Word2007/Part/Rels.php index c1405258..562deb35 100644 --- a/src/PhpWord/Writer/Word2007/Part/Rels.php +++ b/src/PhpWord/Writer/Word2007/Part/Rels.php @@ -50,43 +50,51 @@ class Rels extends AbstractPart * Write relationships * * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param null|array $xmlRels - * @param null|array $mediaRels - * @param integer $relId + * @param array $xmlRels + * @param array $mediaRels + * @param int $relId */ - protected function writeRels(XMLWriter $xmlWriter, $xmlRels = null, $mediaRels = null, $relId = 1) + protected function writeRels(XMLWriter $xmlWriter, $xmlRels = array(), $mediaRels = array(), $relId = 1) { $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->startElement('Relationships'); $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); // XML files relationships - if (is_array($xmlRels)) { - foreach ($xmlRels as $target => $type) { - $this->writeRel($xmlWriter, $relId++, $type, $target); - } + foreach ($xmlRels as $target => $type) { + $this->writeRel($xmlWriter, $relId++, $type, $target); } // Media relationships - if (is_array($mediaRels)) { - $typePrefix = 'officeDocument/2006/relationships/'; - $typeMapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink'); - $targetPaths = array('image' => 'media/', 'object' => 'embeddings/'); - - foreach ($mediaRels as $mediaRel) { - $mediaType = $mediaRel['type']; - $type = array_key_exists($mediaType, $typeMapping) ? $typeMapping[$mediaType] : $mediaType; - $target = array_key_exists($mediaType, $targetPaths) ? $targetPaths[$mediaType] : ''; - $target .= $mediaRel['target']; - $targetMode = ($type == 'hyperlink') ? 'External' : ''; - - $this->writeRel($xmlWriter, $relId++, $typePrefix . $type, $target, $targetMode); - } + foreach ($mediaRels as $mediaRel) { + $this->writeMediaRel($xmlWriter, $relId++, $mediaRel); } $xmlWriter->endElement(); // Relationships } + /** + * Write media relationships + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param int $relId + * @param array $mediaRel + */ + private function writeMediaRel(XMLWriter $xmlWriter, $relId, $mediaRel) + { + $typePrefix = 'officeDocument/2006/relationships/'; + $typeMapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink'); + $targetMapping = array('image' => 'media/', 'object' => 'embeddings/'); + + $mediaType = $mediaRel['type']; + $type = array_key_exists($mediaType, $typeMapping) ? $typeMapping[$mediaType] : $mediaType; + $targetPrefix = array_key_exists($mediaType, $targetMapping) ? $targetMapping[$mediaType] : ''; + $target = $mediaRel['target']; + $targetMode = ($type == 'hyperlink') ? 'External' : ''; + + $this->writeRel($xmlWriter, $relId, $typePrefix . $type, $targetPrefix . $target, $targetMode); + } + /** * Write individual rels entry * diff --git a/src/PhpWord/Writer/Word2007/Part/RelsPart.php b/src/PhpWord/Writer/Word2007/Part/RelsPart.php index a3697834..627a2bcd 100644 --- a/src/PhpWord/Writer/Word2007/Part/RelsPart.php +++ b/src/PhpWord/Writer/Word2007/Part/RelsPart.php @@ -39,7 +39,7 @@ class RelsPart extends Rels public function write() { $xmlWriter = $this->getXmlWriter(); - $this->writeRels($xmlWriter, null, $this->media); + $this->writeRels($xmlWriter, array(), $this->media); return $xmlWriter->getData(); }