diff --git a/CHANGELOG.md b/CHANGELOG.md index 26c9f071..80a6253a 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,7 +20,7 @@ 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 +- Object: Ability to add object in header, footer, textrun, and footnote - @ivanlanin GH-187 ### Bugfixes @@ -35,6 +35,9 @@ This is the changelog between releases of PHPWord. Releases are listed in revers - `createSection` replaced by `addSection` - `Element\Footnote::getReferenceId` replaced by `Container\Container::getRelationId` - `Element\Footnote::setReferenceId` replaced by `Container\Container::setRelationId` +- `Footnote::addFootnoteLinkElement` replaced by `Media::addMediaElement` +- `Footnote::getFootnoteLinkElements` replaced by `Media::getMediaElements` +- All current methods on `Media` ### Miscellaneous @@ -45,6 +48,7 @@ This is the changelog between releases of PHPWord. Releases are listed in revers - General: Remove legacy HashTable and ZipStreamWrapper and all related properties/methods - @ivanlanin GH-187 - Container: Create new Container abstract class - @ivanlanin GH-187 - Element: Create new Element abstract class - @ivanlanin GH-187 +- Media: Refactor media class to use one method for all docPart (section, header, footer, footnote) - @ivanlanin GH-187 ## 0.9.1 - 27 Mar 2014 diff --git a/docs/elements.rst b/docs/elements.rst index da2038e5..abe3f7ba 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -31,7 +31,7 @@ the containers while the rows lists the elements. +-----+---------------+---------+--------+--------+------+----------+----------+ | 11 | Watermark | \- | v | \- | \- | \- | \- | +-----+---------------+---------+--------+--------+------+----------+----------+ -| 12 | Object | v | ? | ? | v | v | v | +| 12 | Object | v | v | v | v | v | v | +-----+---------------+---------+--------+--------+------+----------+----------+ | 13 | TOC | v | \- | \- | \- | \- | \- | +-----+---------------+---------+--------+--------+------+----------+----------+ diff --git a/samples/index.php b/samples/index.php index f4446ad4..451cf381 100644 --- a/samples/index.php +++ b/samples/index.php @@ -3,7 +3,7 @@ include_once 'Sample_Header.php'; if (!CLI) { ?>
-

Welcome to PHPWord, a library written in pure PHP that provides a set of classes to write to and read from different document file formats, i.e. Word (.docx), WordPad (.rtf), and Libre/OpenOffice Writer (.odt).

+

Welcome to PHPWord, a pure PHP library for reading and writing word processing documents, i.e. Word (.docx), WordPad (.rtf), and Libre/OpenOffice Writer (.odt).

Please use the menu above to browse PHPWord samples.

Fork us on Github! diff --git a/src/PhpWord/Container/Container.php b/src/PhpWord/Container/Container.php index 0209b4c5..fd08785e 100644 --- a/src/PhpWord/Container/Container.php +++ b/src/PhpWord/Container/Container.php @@ -143,30 +143,12 @@ abstract class Container extends Element public function addLink($linkSrc, $linkName = null, $fontStyle = null, $paragraphStyle = null) { $this->checkValidity('link'); - - $inSection = true; - if (!is_null($this->docPart)) { - $container = $this->docPart; - $containerId = $this->docPartId; - } else { - $container = $this->container; - $containerId = $this->containerId; - } - if ($container == 'header' || $container == 'footer') { - $container .= $containerId; - $inSection = false; - } elseif ($container == 'footnote') { - $inSection = false; - } + $elementDocPart = $this->checkElementDocPart(); $linkSrc = String::toUTF8($linkSrc); $linkName = String::toUTF8($linkName); $link = new Link($linkSrc, $linkName, $fontStyle, $paragraphStyle); - if ($inSection) { - $rID = Media::addSectionLinkElement($linkSrc); - } else { - $rID = Media::addMediaElement($container, 'hyperlink', $linkSrc); - } + $rID = Media::addMediaElement($elementDocPart, 'link', $linkSrc); $link->setRelationId($rID); $this->elements[] = $link; @@ -286,32 +268,11 @@ abstract class Container extends Element public function addImage($src, $style = null, $isWatermark = false) { $this->checkValidity('image'); - if ($this->container == 'cell' || $this->container == 'textrun') { - $container = $this->docPart; - $containerId = $this->docPartId; - } else { - $container = $this->container; - $containerId = $this->containerId; - } + $elementDocPart = $this->checkElementDocPart(); $image = new Image($src, $style, $isWatermark); if (!is_null($image->getSource())) { - $rID = null; - switch ($container) { - case 'textrun': - case 'section': - $rID = Media::addSectionMediaElement($src, 'image', $image); - break; - case 'header': - $rID = Media::addHeaderMediaElement($containerId, $src, $image); - break; - case 'footer': - $rID = Media::addFooterMediaElement($containerId, $src, $image); - break; - case 'footnote': - $rID = Media::addMediaElement('footnote', 'image', $src, $image); - break; - } + $rID = Media::addMediaElement($elementDocPart, 'image', $src, $image); if (is_int($rID)) { $image->setRelationId($rID); } @@ -334,20 +295,8 @@ abstract class Container extends Element */ public function addObject($src, $style = null) { - $inSection = true; - if (!is_null($this->docPart)) { - $container = $this->docPart; - $containerId = $this->docPartId; - } else { - $container = $this->container; - $containerId = $this->containerId; - } - if ($container == 'header' || $container == 'footer') { - $container .= $containerId; - $inSection = false; - } elseif ($container == 'footnote') { - $inSection = false; - } + $this->checkValidity('object'); + $elementDocPart = $this->checkElementDocPart(); $object = new Object($src, $style); if (!is_null($object->getSource())) { @@ -357,13 +306,8 @@ abstract class Container extends Element $ext = substr($ext, 0, -1); } $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); - } + $rIDimg = Media::addMediaElement($elementDocPart, 'image', $icon, new Image($icon)); + $data = Media::addMediaElement($elementDocPart, 'object', $src); $rID = $data[0]; $objectId = $data[1]; $object->setRelationId($rID); @@ -509,11 +453,11 @@ abstract class Container extends Element 'link' => array(), 'textbreak' => array(), 'image' => array(), + 'object' => array(), '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', 'footnote'), @@ -523,7 +467,6 @@ abstract class Container extends Element // the cell is located in header or footer $validContainerInContainers = array( 'preservetext' => array(array('cell'), array('header', 'footer')), - 'object' => array(array('cell', 'textrun'), array('section')), 'footnote' => array(array('cell', 'textrun'), array('section')), ); @@ -549,4 +492,17 @@ abstract class Container extends Element return true; } + + /** + * Return element location in document: section, headerx, or footerx + */ + private function checkElementDocPart() + { + $isCellTextrun = in_array($this->container, array('cell', 'textrun')); + $docPart = $isCellTextrun ? $this->docPart : $this->container; + $docPartId = $isCellTextrun ? $this->docPartId : $this->containerId; + $inHeaderFooter = ($docPart == 'header' || $docPart == 'footer'); + + return $inHeaderFooter ? $docPart . $docPartId : $docPart; + } } diff --git a/src/PhpWord/Element/Object.php b/src/PhpWord/Element/Object.php index 26207787..d885bfed 100644 --- a/src/PhpWord/Element/Object.php +++ b/src/PhpWord/Element/Object.php @@ -51,7 +51,6 @@ class Object extends Element */ private $objectId; - /** * Create a new Ole-Object Element * diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index a026304d..0a24d12a 100755 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -16,31 +16,6 @@ use PhpOffice\PhpWord\Element\Image; */ class Media { - /** - * Section Media Elements - * - * @var array - */ - private static $sectionMedia = array( - 'images' => array(), - 'embeddings' => array(), - 'links' => array() - ); - - /** - * Header Media Elements - * - * @var array - */ - private static $headerMedia = array(); - - /** - * Footer Media Elements - * - * @var array - */ - private static $footerMedia = array(); - /** * Media elements * @@ -51,213 +26,23 @@ class Media /** * ObjectID Counter * - * @var int + * @var integer */ private static $objectId = 1325353440; - /** - * Add new Section Media Element - * - * @param string $src - * @param string $type - * @param Image $image - * @return integer|array - */ - public static function addSectionMediaElement($src, $type, Image $image = null) - { - $mediaId = md5($src); - $key = ($type === 'image') ? 'images' : 'embeddings'; - if (!array_key_exists($mediaId, self::$sectionMedia[$key])) { - $cImg = self::countSectionMediaElements('images'); - $cObj = self::countSectionMediaElements('embeddings'); - $rID = self::countSectionMediaElements() + 7; - $media = array(); - $folder = null; - $file = null; - if ($type === 'image') { - $cImg++; - $isMemImage = false; - if (!is_null($image)) { - $isMemImage = $image->getIsMemImage(); - $ext = $image->getImageExtension(); - } - if ($isMemImage) { - $media['isMemImage'] = true; - $media['createfunction'] = $image->getImageCreateFunction(); - $media['imagefunction'] = $image->getImageFunction(); - } - $folder = 'media'; - $file = $type . $cImg . '.' . strtolower($ext); - } elseif ($type === 'oleObject') { - $cObj++; - $folder = 'embeddings'; - $file = $type . $cObj . '.bin'; - } - $media['source'] = $src; - $media['target'] = "$folder/section_$file"; - $media['type'] = $type; - $media['rID'] = $rID; - self::$sectionMedia[$key][$mediaId] = $media; - if ($type === 'oleObject') { - return array($rID, ++self::$objectId); - } - return $rID; - } else { - if ($type === 'oleObject') { - $rID = self::$sectionMedia[$key][$mediaId]['rID']; - return array($rID, ++self::$objectId); - } - return self::$sectionMedia[$key][$mediaId]['rID']; - } - } - - /** - * Add new Section Link Element - * - * @param string $linkSrc - * @return integer - */ - public static function addSectionLinkElement($linkSrc) - { - $rID = self::countSectionMediaElements() + 7; - - $link = array(); - $link['target'] = $linkSrc; - $link['rID'] = $rID; - $link['type'] = 'hyperlink'; - - self::$sectionMedia['links'][] = $link; - - return $rID; - } - - /** - * Get Section Media Elements - * - * @param string $key - * @return array - */ - public static function getSectionMediaElements($key = null) - { - if (!is_null($key)) { - return self::$sectionMedia[$key]; - } - - $arrImages = self::$sectionMedia['images']; - $arrObjects = self::$sectionMedia['embeddings']; - $arrLinks = self::$sectionMedia['links']; - return array_merge($arrImages, $arrObjects, $arrLinks); - } - - /** - * Get Section Media Elements Count - * - * @param string $key - * @return int - */ - public static function countSectionMediaElements($key = null) - { - if (!is_null($key)) { - return count(self::$sectionMedia[$key]); - } - - $cImages = count(self::$sectionMedia['images']); - $cObjects = count(self::$sectionMedia['embeddings']); - $cLinks = count(self::$sectionMedia['links']); - return ($cImages + $cObjects + $cLinks); - } - - /** - * Add new Header Media Element - * - * @param int $headerCount - * @param string $src - * @param Image $image - * @return int - */ - public static function addHeaderMediaElement($headerCount, $src, Image $image = null) - { - return self::addMediaElement("header{$headerCount}", 'image', $src, $image); - } - - /** - * Get Header Media Elements Count - * - * @param string $key - * @return int - */ - public static function countHeaderMediaElements($key) - { - return self::countMediaElements($key); - } - - /** - * Get Header Media Elements - * - * @param string $prefix header|footer - * @return array - */ - public static function getHeaderMediaElements($prefix = 'header') - { - $mediaCollection = array(); - if (!empty(self::$media)) { - foreach (self::$media as $key => $val) { - if (substr($key, 0, 6) == $prefix) { - $mediaCollection[$key] = $val; - } - } - } - - return $mediaCollection; - } - - /** - * Add new Footer Media Element - * - * @param int $footerCount - * @param string $src - * @param Image $image - * @return int - */ - public static function addFooterMediaElement($footerCount, $src, Image $image = null) - { - return self::addMediaElement("footer{$footerCount}", 'image', $src, $image); - } - - /** - * Get Footer Media Elements Count - * - * @param string $key - * @return int - */ - public static function countFooterMediaElements($key) - { - return self::countMediaElements($key); - } - - /** - * Get Footer Media Elements - * - * @return array - */ - public static function getFooterMediaElements() - { - return self::getHeaderMediaElements('footer'); - } - /** * Add new media element * * @param string $container section|headerx|footerx|footnote - * @param string $mediaType image|embeddings|hyperlink + * @param string $mediaType image|object|link * @param string $source * @param Image $image - * @return int + * @return integer|array */ public static function addMediaElement($container, $mediaType, $source, Image $image = null) { - // Assign media Id and initiate media container if none exists - $mediaId = md5($source); + // Assign unique media Id and initiate media container if none exists + $mediaId = md5($container . $source); if (!array_key_exists($container, self::$media)) { self::$media[$container]= array(); } @@ -267,7 +52,7 @@ class Media $mediaCount = self::countMediaElements($container); $mediaTypeCount = self::countMediaElements($container, $mediaType); $mediaData = array(); - $relId = $mediaCount + 1; + $relId = ++$mediaCount; $target = null; $mediaTypeCount++; @@ -286,11 +71,11 @@ class Media } $target = "media/{$container}_image{$mediaTypeCount}.{$ext}"; // Objects - } elseif ($mediaType == 'embeddings') { + } elseif ($mediaType == 'object') { $file = "oleObject{$mediaTypeCount}.bin"; $target = "embeddings/{$container}_oleObject{$mediaTypeCount}.bin"; // Links - } elseif ($mediaType == 'hyperlink') { + } elseif ($mediaType == 'link') { $target = $source; } @@ -299,13 +84,13 @@ class Media $mediaData['type'] = $mediaType; $mediaData['rID'] = $relId; self::$media[$container][$mediaId] = $mediaData; - if ($mediaType === 'embeddings') { + if ($mediaType === 'object') { return array($relId, ++self::$objectId); } else { return $relId; } } else { - if ($mediaType === 'embeddings') { + if ($mediaType === 'object') { $relId = self::$media[$container][$mediaId]['rID']; return array($relId, ++self::$objectId); } else { @@ -317,20 +102,23 @@ class Media /** * Get media elements count * - * @param string $container - * @param string $mediaType - * @return int + * @param string $container section|headerx|footerx|footnote + * @param string $mediaType image|object|link + * @return integer */ public static function countMediaElements($container, $mediaType = null) { $mediaCount = 0; - foreach (self::$media[$container] as $mediaKey => $mediaData) { - if (!is_null($mediaType)) { - if ($mediaType == $mediaData['type']) { + + if (array_key_exists($container, self::$media)) { + foreach (self::$media[$container] as $mediaKey => $mediaData) { + if (!is_null($mediaType)) { + if ($mediaType == $mediaData['type']) { + $mediaCount++; + } + } else { $mediaCount++; } - } else { - $mediaCount++; } } @@ -340,27 +128,171 @@ class Media /** * Get media elements * - * @param string $container - * @param string $mediaType - * @return int + * @param string $container section|headerx|footerx|footnote + * @param string $mediaType image|object|link + * @return array */ public static function getMediaElements($container, $mediaType = null) { - if (!array_key_exists($container, self::$media)) { - return false; - } - $mediaElements = array(); - foreach (self::$media[$container] as $mediaKey => $mediaData) { - if (!is_null($mediaType)) { - if ($mediaType == $mediaData['type']) { + + // If header/footer, search for headerx and footerx where x is number + if ($container == 'header' || $container == 'footer') { + foreach (self::$media as $key => $val) { + if (substr($key, 0, 6) == $container) { + $mediaElements[$key] = $val; + } + } + } else { + if (!array_key_exists($container, self::$media)) { + return $mediaElements; + } + foreach (self::$media[$container] as $mediaKey => $mediaData) { + if (!is_null($mediaType)) { + if ($mediaType == $mediaData['type']) { + $mediaElements[$mediaKey] = $mediaData; + } + } else { $mediaElements[$mediaKey] = $mediaData; } - } else { - $mediaElements[$mediaKey] = $mediaData; } } return $mediaElements; } + + /** + * Add new Section Media Element + * + * @param string $src + * @param string $type + * @param Image $image + * @return integer|array + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function addSectionMediaElement($src, $type, Image $image = null) + { + return self::addMediaElement("section", $type, $src, $image); + } + + /** + * Add new Section Link Element + * + * @param string $linkSrc + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function addSectionLinkElement($linkSrc) + { + return self::addMediaElement('section', 'link', $linkSrc); + } + + /** + * Get Section Media Elements + * + * @param string $key + * @return array + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function getSectionMediaElements($key = null) + { + return self::getMediaElements('section', $key); + } + + /** + * Get Section Media Elements Count + * + * @param string $key + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function countSectionMediaElements($key = null) + { + return self::countMediaElements('section', $key); + } + + /** + * Add new Header Media Element + * + * @param integer $headerCount + * @param string $src + * @param Image $image + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function addHeaderMediaElement($headerCount, $src, Image $image = null) + { + return self::addMediaElement("header{$headerCount}", 'image', $src, $image); + } + + /** + * Get Header Media Elements Count + * + * @param string $key + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function countHeaderMediaElements($key) + { + return self::countMediaElements($key); + } + + /** + * Get Header Media Elements + * + * @param string $prefix header|footer + * @return array + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function getHeaderMediaElements() + { + return self::getMediaElements('header'); + } + + /** + * Add new Footer Media Element + * + * @param integer $footerCount + * @param string $src + * @param Image $image + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function addFooterMediaElement($footerCount, $src, Image $image = null) + { + return self::addMediaElement("footer{$footerCount}", 'image', $src, $image); + } + + /** + * Get Footer Media Elements Count + * + * @param string $key + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function countFooterMediaElements($key) + { + return self::countMediaElements($key); + } + + /** + * Get Footer Media Elements + * + * @return array + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public static function getFooterMediaElements() + { + return self::getMediaElements('footer'); + } } diff --git a/src/PhpWord/Shared/Drawing.php b/src/PhpWord/Shared/Drawing.php index 4c04a5fc..58a6ee1a 100644 --- a/src/PhpWord/Shared/Drawing.php +++ b/src/PhpWord/Shared/Drawing.php @@ -17,8 +17,8 @@ class Drawing /** * Convert pixels to EMU * - * @param int $pValue Value in pixels - * @return int Value in EMU + * @param integer $pValue Value in pixels + * @return double Value in EMU */ public static function pixelsToEMU($pValue = 0) { @@ -28,8 +28,8 @@ class Drawing /** * Convert EMU to pixels * - * @param int $pValue Value in EMU - * @return int Value in pixels + * @param integer $pValue Value in EMU + * @return integer Value in pixels */ public static function EMUToPixels($pValue = 0) { @@ -43,8 +43,8 @@ class Drawing /** * Convert pixels to points * - * @param int $pValue Value in pixels - * @return int Value in points + * @param integer $pValue Value in pixels + * @return double Value in points */ public static function pixelsToPoints($pValue = 0) { @@ -54,8 +54,8 @@ class Drawing /** * Convert points width to pixels * - * @param int $pValue Value in points - * @return int Value in pixels + * @param integer $pValue Value in points + * @return integer Value in pixels */ public static function pointsToPixels($pValue = 0) { @@ -69,19 +69,19 @@ class Drawing /** * Convert degrees to angle * - * @param int $pValue Degrees - * @return int Angle + * @param integer $pValue Degrees + * @return integer Angle */ public static function degreesToAngle($pValue = 0) { - return (int)round($pValue * 60000); + return (integer)round($pValue * 60000); } /** * Convert angle to degrees * - * @param int $pValue Angle - * @return int Degrees + * @param integer $pValue Angle + * @return integer Degrees */ public static function angleToDegrees($pValue = 0) { @@ -95,8 +95,8 @@ class Drawing /** * Convert pixels to centimeters * - * @param int $pValue Value in pixels - * @return int Value in centimeters + * @param integer $pValue Value in pixels + * @return double Value in centimeters */ public static function pixelsToCentimeters($pValue = 0) { @@ -106,8 +106,8 @@ class Drawing /** * Convert centimeters width to pixels * - * @param int $pValue Value in centimeters - * @return int Value in pixels + * @param integer $pValue Value in centimeters + * @return integer Value in pixels */ public static function centimetersToPixels($pValue = 0) { @@ -121,8 +121,8 @@ class Drawing /** * Convert HTML hexadecimal to RGB * - * @param str $pValue HTML Color in hexadecimal - * @return array Value in RGB + * @param string $pValue HTML Color in hexadecimal + * @return array Value in RGB */ public static function htmlToRGB($pValue) { diff --git a/src/PhpWord/Shared/String.php b/src/PhpWord/Shared/String.php index 9d298a66..e603f034 100644 --- a/src/PhpWord/Shared/String.php +++ b/src/PhpWord/Shared/String.php @@ -19,66 +19,36 @@ class String * * @var string[] */ - private static $_controlCharacters = array(); - - /** - * Build control characters array - */ - private static function _buildControlCharacters() - { - for ($i = 0; $i <= 19; ++$i) { - if ($i != 9 && $i != 10 && $i != 13) { - $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_'; - $replace = chr($i); - self::$_controlCharacters[$find] = $replace; - } - } - } + private static $controlCharacters = array(); /** * Convert from OpenXML escaped control character to PHP control character * - * Excel 2007 team: - * ---------------- - * That's correct, control characters are stored directly in the shared-strings table. - * We do encode characters that cannot be represented in XML using the following escape sequence: - * _xHHHH_ where H represents a hexadecimal character in the character's value... - * So you could end up with something like _x0008_ in a string (either in a cell value () - * element or in the shared string element. - * - * @param string $value Value to unescape - * @return string + * @param string $value Value to unescape + * @return string */ public static function controlCharacterOOXML2PHP($value = '') { - if (empty(self::$_controlCharacters)) { - self::_buildControlCharacters(); + if (empty(self::$controlCharacters)) { + self::buildControlCharacters(); } - return str_replace(array_keys(self::$_controlCharacters), array_values(self::$_controlCharacters), $value); + return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $value); } /** * Convert from PHP control character to OpenXML escaped control character * - * Excel 2007 team: - * ---------------- - * That's correct, control characters are stored directly in the shared-strings table. - * We do encode characters that cannot be represented in XML using the following escape sequence: - * _xHHHH_ where H represents a hexadecimal character in the character's value... - * So you could end up with something like _x0008_ in a string (either in a cell value () - * element or in the shared string element. - * - * @param string $value Value to escape - * @return string + * @param string $value Value to escape + * @return string */ public static function controlCharacterPHP2OOXML($value = '') { - if (empty(self::$_controlCharacters)) { - self::_buildControlCharacters(); + if (empty(self::$controlCharacters)) { + self::buildControlCharacters(); } - return str_replace(array_values(self::$_controlCharacters), array_keys(self::$_controlCharacters), $value); + return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $value); } /** @@ -106,4 +76,18 @@ class String return $value; } + + /** + * Build control characters array + */ + private static function buildControlCharacters() + { + for ($i = 0; $i <= 19; ++$i) { + if ($i != 9 && $i != 10 && $i != 13) { + $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_'; + $replace = chr($i); + self::$controlCharacters[$find] = $replace; + } + } + } } diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php index 16bb8334..d4e959bc 100644 --- a/src/PhpWord/Shared/XMLWriter.php +++ b/src/PhpWord/Shared/XMLWriter.php @@ -38,14 +38,14 @@ class XMLWriter * * @var \XMLWriter */ - private $_xmlWriter; + private $xmlWriter; /** * Temporary filename * * @var string */ - private $_tempFileName = ''; + private $tempFile = ''; /** * Create new XMLWriter @@ -56,30 +56,30 @@ class XMLWriter public function __construct($pTemporaryStorage = self::STORAGE_MEMORY, $pTemporaryStorageFolder = './') { // Create internal XMLWriter - $this->_xmlWriter = new \XMLWriter(); + $this->xmlWriter = new \XMLWriter(); // Open temporary storage if ($pTemporaryStorage == self::STORAGE_MEMORY) { - $this->_xmlWriter->openMemory(); + $this->xmlWriter->openMemory(); } else { // Create temporary filename - $this->_tempFileName = @tempnam($pTemporaryStorageFolder, 'xml'); + $this->tempFile = @tempnam($pTemporaryStorageFolder, 'xml'); // Open storage - if ($this->_xmlWriter->openUri($this->_tempFileName) === false) { + if ($this->xmlWriter->openUri($this->tempFile) === false) { // Fallback to memory... - $this->_xmlWriter->openMemory(); + $this->xmlWriter->openMemory(); } } // Set xml Compatibility $compatibility = Settings::getCompatibility(); if ($compatibility) { - $this->_xmlWriter->setIndent(false); - $this->_xmlWriter->setIndentString(''); + $this->xmlWriter->setIndent(false); + $this->xmlWriter->setIndentString(''); } else { - $this->_xmlWriter->setIndent(true); - $this->_xmlWriter->setIndentString(' '); + $this->xmlWriter->setIndent(true); + $this->xmlWriter->setIndentString(' '); } } @@ -89,26 +89,11 @@ class XMLWriter public function __destruct() { // Desctruct XMLWriter - unset($this->_xmlWriter); + unset($this->xmlWriter); // Unlink temporary files - if ($this->_tempFileName != '') { - @unlink($this->_tempFileName); - } - } - - /** - * Get written data - * - * @return string XML data - */ - public function getData() - { - if ($this->_tempFileName == '') { - return $this->_xmlWriter->outputMemory(true); - } else { - $this->_xmlWriter->flush(); - return file_get_contents($this->_tempFileName); + if ($this->tempFile != '') { + @unlink($this->tempFile); } } @@ -121,22 +106,37 @@ class XMLWriter public function __call($function, $args) { try { - @call_user_func_array(array($this->_xmlWriter, $function), $args); + @call_user_func_array(array($this->xmlWriter, $function), $args); } catch (\Exception $ex) { // Do nothing! } } + /** + * Get written data + * + * @return string XML data + */ + public function getData() + { + if ($this->tempFile == '') { + return $this->xmlWriter->outputMemory(true); + } else { + $this->xmlWriter->flush(); + return file_get_contents($this->tempFile); + } + } + /** * Fallback method for writeRaw, introduced in PHP 5.2 * * @param string $text - * @return string + * @return bool */ public function writeRaw($text) { - if (isset($this->_xmlWriter) && is_object($this->_xmlWriter) && (method_exists($this->_xmlWriter, 'writeRaw'))) { - return $this->_xmlWriter->writeRaw($text); + if (isset($this->xmlWriter) && is_object($this->xmlWriter) && (method_exists($this->xmlWriter, 'writeRaw'))) { + return $this->xmlWriter->writeRaw($text); } return $this->text($text); diff --git a/src/PhpWord/Writer/ODText/Content.php b/src/PhpWord/Writer/ODText/Content.php index 2a96e103..0669bc41 100644 --- a/src/PhpWord/Writer/ODText/Content.php +++ b/src/PhpWord/Writer/ODText/Content.php @@ -10,7 +10,6 @@ namespace PhpOffice\PhpWord\Writer\ODText; use PhpOffice\PhpWord\PhpWord; -use PhpOffice\PhpWord\Container\Section; use PhpOffice\PhpWord\Element\Image; use PhpOffice\PhpWord\Element\Link; use PhpOffice\PhpWord\Element\ListItem; diff --git a/src/PhpWord/Writer/RTF.php b/src/PhpWord/Writer/RTF.php index a990575c..8698365f 100755 --- a/src/PhpWord/Writer/RTF.php +++ b/src/PhpWord/Writer/RTF.php @@ -24,7 +24,6 @@ use PhpOffice\PhpWord\Element\Title; use PhpOffice\PhpWord\Shared\Drawing; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; -use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\TOC; /** diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 7c87c2d4..352bb7ca 100755 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -98,65 +98,40 @@ class Word2007 extends Writer implements IWriter } } - // Add section elements + // Add section media files $sectionElements = array(); - $secElements = Media::getSectionMediaElements(); - foreach ($secElements as $element) { // loop through section media elements - if ($element['type'] != 'hyperlink') { - $this->addFileToPackage($objZip, $element); - } - $sectionElements[] = $element; - } - // Add header relations & elements - $hdrElements = Media::getHeaderMediaElements(); - foreach ($hdrElements as $hdrFile => $hdrMedia) { - if (count($hdrMedia) > 0) { - $objZip->addFromString( - 'word/_rels/' . $hdrFile . '.xml.rels', - $this->getWriterPart('rels')->writeMediaRels($hdrMedia) - ); - foreach ($hdrMedia as $element) { - if ($element['type'] != 'hyperlink') { - $this->addFileToPackage($objZip, $element); - } - } + $secElements = Media::getMediaElements('section'); + if (!empty($secElements)) { + $this->addFilesToPackage($objZip, $secElements); + foreach ($secElements as $element) { + $sectionElements[] = $element; } } - // Add footer relations & elements - $ftrElements = Media::getFooterMediaElements(); - foreach ($ftrElements as $ftrFile => $ftrMedia) { - if (count($ftrMedia) > 0) { - $objZip->addFromString( - 'word/_rels/' . $ftrFile . '.xml.rels', - $this->getWriterPart('rels')->writeMediaRels($ftrMedia) - ); - foreach ($ftrMedia as $element) { - if ($element['type'] != 'hyperlink') { - $this->addFileToPackage($objZip, $element); - } - } - } - } + // Add header/footer media files & relations + $this->addHeaderFooterMedia($objZip, 'header'); + $this->addHeaderFooterMedia($objZip, 'footer'); - // Process header/footer xml files + // Add header/footer contents $cHdrs = 0; $cFtrs = 0; - $rID = Media::countSectionMediaElements() + 6; + $rID = Media::countMediaElements('section') + 6; // @see Rels::writeDocRels for 6 first elements $sections = $this->phpWord->getSections(); $footers = array(); foreach ($sections as $section) { $headers = $section->getHeaders(); - foreach ($headers as $index => &$header) { - $cHdrs++; - $header->setRelationId(++$rID); - $hdrFile = "header{$cHdrs}.xml"; - $sectionElements[] = array('target' => $hdrFile, 'type' => 'header', 'rID' => $rID); - $objZip->addFromString( - "word/{$hdrFile}", - $this->getWriterPart('header')->writeHeader($header) - ); + if (!empty($headers)) { + foreach ($headers as $index => &$header) { + $cHdrs++; + $header->setRelationId(++$rID); + $hdrFile = "header{$cHdrs}.xml"; + $sectionElements[] = array('target' => $hdrFile, 'type' => 'header', 'rID' => $rID); + $objZip->addFromString( + "word/{$hdrFile}", + $this->getWriterPart('header')->writeHeader($header) + ); + } } $footer = $section->getFooter(); $footers[++$cFtrs] = $footer; @@ -172,34 +147,23 @@ class Word2007 extends Writer implements IWriter } } - // Process footnotes + // Add footnotes media files, relations, and contents if (Footnote::countFootnoteElements() > 0) { - // Push to document.xml.rels $sectionElements[] = array('target' => 'footnotes.xml', 'type' => 'footnotes', 'rID' => ++$rID); - // Add footnote media to package $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', - $this->getWriterPart('footnotes')->writeFootnotes(Footnote::getFootnoteElements()) - ); - // Write footnotes.xml.rels + $this->addFilesToPackage($objZip, $footnoteMedia); if (!empty($footnoteMedia)) { $objZip->addFromString( 'word/_rels/footnotes.xml.rels', $this->getWriterPart('rels')->writeMediaRels($footnoteMedia) ); } + $objZip->addFromString( + 'word/footnotes.xml', + $this->getWriterPart('footnotes')->writeFootnotes(Footnote::getFootnoteElements()) + ); } - // build docx file // Write dynamic files $objZip->addFromString( '[Content_Types].xml', @@ -288,21 +252,47 @@ class Word2007 extends Writer implements IWriter * @param mixed $objZip * @param mixed $element */ - private function addFileToPackage($objZip, $element) + private function addFilesToPackage($objZip, $elements) { - if (isset($element['isMemImage']) && $element['isMemImage']) { - $image = call_user_func($element['createfunction'], $element['source']); - ob_start(); - call_user_func($element['imagefunction'], $image); - $imageContents = ob_get_contents(); - ob_end_clean(); - $objZip->addFromString('word/' . $element['target'], $imageContents); - imagedestroy($image); + foreach ($elements as $element) { + if ($element['type'] == 'link') { + continue; + } + if (isset($element['isMemImage']) && $element['isMemImage']) { + $image = call_user_func($element['createfunction'], $element['source']); + ob_start(); + call_user_func($element['imagefunction'], $image); + $imageContents = ob_get_contents(); + ob_end_clean(); + $objZip->addFromString('word/' . $element['target'], $imageContents); + imagedestroy($image); - $this->checkContentTypes($element['source']); - } else { - $objZip->addFile($element['source'], 'word/' . $element['target']); - $this->checkContentTypes($element['source']); + $this->checkContentTypes($element['source']); + } else { + $objZip->addFile($element['source'], 'word/' . $element['target']); + $this->checkContentTypes($element['source']); + } + } + } + + /** + * Add header/footer media elements + */ + private function addHeaderFooterMedia($objZip, $docPart) + { + $elements = Media::getMediaElements($docPart); + if (!empty($elements)) { + foreach ($elements as $file => $media) { + if (count($media) > 0) { + $objZip->addFromString( + 'word/_rels/' . $file . '.xml.rels', + $this->getWriterPart('rels')->writeMediaRels($media) + ); + if (!empty($media)) { + $this->addFilesToPackage($objZip, $media); + } + } + } } } } diff --git a/src/PhpWord/Writer/Word2007/Base.php b/src/PhpWord/Writer/Word2007/Base.php index f8873152..10cabe49 100644 --- a/src/PhpWord/Writer/Word2007/Base.php +++ b/src/PhpWord/Writer/Word2007/Base.php @@ -1126,15 +1126,15 @@ class Base extends WriterPart protected function writeContainerElements(XMLWriter $xmlWriter, Container $container) { // Check allowed elements - $elmCommon = array('Text', 'Link', 'TextBreak', 'Image'); + $elmCommon = array('Text', 'Link', 'TextBreak', 'Image', 'Object'); $elmMainCell = array_merge($elmCommon, array('TextRun', 'ListItem', 'CheckBox')); $allowedElements = array( - 'Section' => array_merge($elmMainCell, array('Table', 'Footnote', 'Object', 'Title', 'PageBreak', 'TOC')), + 'Section' => array_merge($elmMainCell, array('Table', 'Footnote', 'Title', 'PageBreak', 'TOC')), 'Header' => array_merge($elmMainCell, array('Table', 'PreserveText')), 'Footer' => array_merge($elmMainCell, array('Table', 'PreserveText')), - 'Cell' => array_merge($elmMainCell, array('Object', 'PreserveText', 'Footnote')), - 'TextRun' => array_merge($elmCommon, array('Object', 'Footnote')), - 'Footnote' => array_merge($elmCommon, array('Object')), + 'Cell' => array_merge($elmMainCell, array('PreserveText', 'Footnote')), + 'TextRun' => array_merge($elmCommon, array('Footnote')), + 'Footnote' => $elmCommon, ); $containerName = get_class($container); $containerName = substr($containerName, strrpos($containerName, '\\') + 1); diff --git a/src/PhpWord/Writer/Word2007/Rels.php b/src/PhpWord/Writer/Word2007/Rels.php index 8f684c47..bbf0634c 100755 --- a/src/PhpWord/Writer/Word2007/Rels.php +++ b/src/PhpWord/Writer/Word2007/Rels.php @@ -26,8 +26,6 @@ class Rels extends WriterPart /** * Write _rels/.rels - * - * @param PhpWord $phpWord */ public function writeMainRels() { @@ -95,15 +93,14 @@ class Rels extends WriterPart $this->writeRel($xmlWriter, $id++, $type, $target); } } - if (is_array($mediaRels)) { - $typePrefix = 'officeDocument/2006/relationships/'; + if (!is_null($mediaRels) && is_array($mediaRels)) { + $mapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink'); foreach ($mediaRels as $mediaRel) { - $id = $mediaRel['rID']; $type = $mediaRel['type']; - $target = $mediaRel['target']; // file name + $type = array_key_exists($type, $mapping) ? $mapping[$type] : $type; + $target = $mediaRel['target']; $targetMode = ($type == 'hyperlink') ? 'External' : ''; - $type = $typePrefix . ($type == 'embeddings' ? 'oleObject' : $type); - $this->writeRel($xmlWriter, $id, $type, $target, $targetMode); + $this->writeRel($xmlWriter, $id++, "officeDocument/2006/relationships/{$type}", $target, $targetMode); } } $xmlWriter->endElement(); diff --git a/tests/PhpWord/Tests/MediaTest.php b/tests/PhpWord/Tests/MediaTest.php index a3e6ead5..b1a49059 100644 --- a/tests/PhpWord/Tests/MediaTest.php +++ b/tests/PhpWord/Tests/MediaTest.php @@ -25,7 +25,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase */ public function testGetSectionMediaElementsWithNull() { - $this->assertEquals(Media::getSectionMediaElements(), array()); + $this->assertEquals(Media::getMediaElements('section'), array()); } /** @@ -33,31 +33,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase */ public function testCountSectionMediaElementsWithNull() { - $this->assertEquals(Media::countSectionMediaElements(), 0); - } - - /** - * Get header media elements - */ - public function testGetHeaderMediaElements() - { - $this->assertAttributeEquals( - Media::getHeaderMediaElements(), - 'headerMedia', - 'PhpOffice\\PhpWord\\Media' - ); - } - - /** - * Get footer media elements - */ - public function testGetFooterMediaElements() - { - $this->assertAttributeEquals( - Media::getFooterMediaElements(), - 'footerMedia', - 'PhpOffice\\PhpWord\\Media' - ); + $this->assertEquals(Media::countMediaElements('section'), 0); } /** @@ -68,13 +44,13 @@ class MediaTest extends \PHPUnit_Framework_TestCase $local = __DIR__ . "/_files/images/mars.jpg"; $object = __DIR__ . "/_files/documents/sheet.xls"; $remote = 'http://php.net/images/logos/php-med-trans-light.gif'; - Media::addSectionMediaElement($local, 'image', new Image($local)); - Media::addSectionMediaElement($local, 'image', new Image($local)); - Media::addSectionMediaElement($remote, 'image', new Image($remote)); - Media::addSectionMediaElement($object, 'oleObject'); - Media::addSectionMediaElement($object, 'oleObject'); + Media::addMediaElement('section', 'image', $local, new Image($local)); + Media::addMediaElement('section', 'image', $local, new Image($local)); + Media::addMediaElement('section', 'image', $remote, new Image($local)); + Media::addMediaElement('section', 'object', $object); + Media::addMediaElement('section', 'object', $object); - $this->assertEquals(3, Media::countSectionMediaElements()); + $this->assertEquals(3, Media::countMediaElements('section')); } /** @@ -82,12 +58,12 @@ class MediaTest extends \PHPUnit_Framework_TestCase */ public function testAddSectionLinkElement() { - $expected = Media::countSectionMediaElements() + 7; - $actual = Media::addSectionLinkElement('http://test.com'); + $expected = Media::countMediaElements('section') + 1; + $actual = Media::addMediaElement('section', 'link', 'http://test.com'); $this->assertEquals($expected, $actual); - $this->assertEquals(1, Media::countSectionMediaElements('links')); - $this->assertEquals(1, count(Media::getSectionMediaElements('links'))); + $this->assertEquals(1, Media::countMediaElements('section', 'link')); + $this->assertEquals(1, count(Media::getMediaElements('section', 'link'))); } /** @@ -97,13 +73,13 @@ class MediaTest extends \PHPUnit_Framework_TestCase { $local = __DIR__ . "/_files/images/mars.jpg"; $remote = 'http://php.net/images/logos/php-med-trans-light.gif'; - Media::addHeaderMediaElement(1, $local, new Image($local)); - Media::addHeaderMediaElement(1, $local, new Image($local)); - Media::addHeaderMediaElement(1, $remote, new Image($remote)); + Media::addMediaElement('header1', 'image', $local, new Image($local)); + Media::addMediaElement('header1', 'image', $local, new Image($local)); + Media::addMediaElement('header1', 'image', $remote, new Image($remote)); - $this->assertEquals(2, Media::countHeaderMediaElements('header1')); + $this->assertEquals(2, Media::countMediaElements('header1')); $this->assertEquals(2, count(Media::getMediaElements('header1'))); - $this->assertFalse(Media::getMediaElements('header2')); + $this->assertEmpty(Media::getMediaElements('header2')); } /** @@ -113,10 +89,10 @@ class MediaTest extends \PHPUnit_Framework_TestCase { $local = __DIR__ . "/_files/images/mars.jpg"; $remote = 'http://php.net/images/logos/php-med-trans-light.gif'; - Media::addFooterMediaElement(1, $local, new Image($local)); - Media::addFooterMediaElement(1, $local, new Image($local)); - Media::addFooterMediaElement(1, $remote, new Image($remote)); + Media::addMediaElement('footer1', 'image', $local, new Image($local)); + Media::addMediaElement('footer1', 'image', $local, new Image($local)); + Media::addMediaElement('footer1', 'image', $remote, new Image($remote)); - $this->assertEquals(2, Media::countFooterMediaElements('footer1')); + $this->assertEquals(2, Media::countMediaElements('footer1')); } }