diff --git a/CHANGELOG.md b/CHANGELOG.md index fea9e22c..548ef026 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,8 @@ 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` +- `Footnote::addFootnoteLinkElement` replaced by `Media::addElement` +- `Footnote::getFootnoteLinkElements` replaced by `Media::getElements` - All current methods on `Media` ### Miscellaneous diff --git a/src/PhpWord/Container/Container.php b/src/PhpWord/Container/Container.php index ec66a5a0..2584a1f7 100644 --- a/src/PhpWord/Container/Container.php +++ b/src/PhpWord/Container/Container.php @@ -49,7 +49,7 @@ abstract class Container extends Element * * @var int */ - protected $containerId; + protected $sectionId; /** * Elements collection @@ -123,7 +123,7 @@ abstract class Container extends Element $link = new Link(String::toUTF8($linkSrc), String::toUTF8($linkName), $fontStyle, $paragraphStyle); $link->setDocPart($this->getDocPart(), $this->getDocPartId()); - $rID = Media::addMediaElement($elementDocPart, 'link', $linkSrc); + $rID = Media::addElement($elementDocPart, 'link', $linkSrc); $link->setRelationId($rID); $this->elements[] = $link; @@ -250,14 +250,10 @@ abstract class Container extends Element $image = new Image($src, $style, $isWatermark); $image->setDocPart($this->getDocPart(), $this->getDocPartId()); - if (!is_null($image->getSource())) { - $rID = Media::addMediaElement($elementDocPart, 'image', $src, $image); - $image->setRelationId($rID); - $this->elements[] = $image; - return $image; - } else { - throw new InvalidImageException; - } + $rID = Media::addElement($elementDocPart, 'image', $src, $image); + $image->setRelationId($rID); + $this->elements[] = $image; + return $image; } /** @@ -284,9 +280,9 @@ abstract class Container extends Element $ext = substr($ext, 0, -1); } $icon = realpath(__DIR__ . "/../_staticDocParts/_{$ext}.png"); - $rID = Media::addMediaElement($elementDocPart, 'object', $src); + $rID = Media::addElement($elementDocPart, 'object', $src); $object->setRelationId($rID); - $rIDimg = Media::addMediaElement($elementDocPart, 'image', $icon, new Image($icon)); + $rIDimg = Media::addElement($elementDocPart, 'image', $icon, new Image($icon)); $object->setImageRelationId($rIDimg); $this->elements[] = $object; return $object; @@ -307,7 +303,7 @@ abstract class Container extends Element $footnote = new FootnoteElement($paragraphStyle); $refID = FootnoteCollection::addFootnoteElement($footnote); - $footnote->setDocPart($this->getDocPart(), $this->getDocPartId()); + $footnote->setDocPart('footnote', $this->getDocPartId()); $footnote->setRelationId($refID); $this->elements[] = $footnote; @@ -337,11 +333,11 @@ abstract class Container extends Element /** * Get section number * - * @return array + * @return integer */ public function getSectionId() { - return $this->containerId; + return $this->sectionId; } /** @@ -474,7 +470,7 @@ abstract class Container extends Element { $isCellTextrun = in_array($this->container, array('cell', 'textrun')); $docPart = $isCellTextrun ? $this->getDocPart() : $this->container; - $docPartId = $isCellTextrun ? $this->getDocPartId() : $this->containerId; + $docPartId = $isCellTextrun ? $this->getDocPartId() : $this->sectionId; $inHeaderFooter = ($docPart == 'header' || $docPart == 'footer'); return $inHeaderFooter ? $docPart . $docPartId : $docPart; diff --git a/src/PhpWord/Container/Footer.php b/src/PhpWord/Container/Footer.php index 440b3b4e..47287c8b 100755 --- a/src/PhpWord/Container/Footer.php +++ b/src/PhpWord/Container/Footer.php @@ -14,15 +14,51 @@ namespace PhpOffice\PhpWord\Container; */ class Footer extends Container { + const AUTO = 'default'; // default and odd pages + const FIRST = 'first'; + const EVEN = 'even'; + + /** + * Header type + * + * @var string + */ + private $type = self::AUTO; + /** * Create new instance * * @param int $sectionId + * @param int $footerId + * @param string $type */ - public function __construct($sectionId) + public function __construct($sectionId, $footerId = 1, $type = self::AUTO) { $this->container = 'footer'; - $this->containerId = $sectionId; - $this->setDocPart($this->container, $this->containerId); + $this->sectionId = $sectionId; + $this->setType($type); + $this->setDocPart($this->container, ($sectionId - 1) * 3 + $footerId); + } + + /** + * Set type + * + * @param string $value + * @since 0.9.2 + */ + public function setType($value = self::AUTO) + { + $this->type = $value; + } + + /** + * Get type + * + * @return string + * @since 0.9.2 + */ + public function getType() + { + return $this->type; } } diff --git a/src/PhpWord/Container/Header.php b/src/PhpWord/Container/Header.php index 39c6ca98..2920e3cf 100755 --- a/src/PhpWord/Container/Header.php +++ b/src/PhpWord/Container/Header.php @@ -20,29 +20,32 @@ class Header extends Container * Header types constants * * @var string - * @link http://www.schemacentral.com/sc/ooxml/a-wheaderType-4.html Header or Footer Type + * @link http://www.schemacentral.com/sc/ooxml/a-wtype-4.html Header or Footer Type */ - const AUTO = 'default'; // Did not use DEFAULT because it is a PHP keyword - const EVEN = 'even'; + const AUTO = 'default'; // default and odd pages const FIRST = 'first'; + const EVEN = 'even'; /** * Header type * * @var string */ - private $headerType = self::AUTO; + private $type = self::AUTO; /** * Create new instance * * @param int $sectionId + * @param int $headerId + * @param string $type */ - public function __construct($sectionId) + public function __construct($sectionId, $headerId = 1, $type = self::AUTO) { $this->container = 'header'; - $this->containerId = $sectionId; - $this->setDocPart($this->container, $this->containerId); + $this->sectionId = $sectionId; + $this->setType($type); + $this->setDocPart($this->container, ($sectionId - 1) * 3 + $headerId); } /** @@ -57,35 +60,57 @@ class Header extends Container return $this->addImage($src, $style, true); } + /** + * Set header type + * + * @param string $value + * @since 0.9.2 + */ + public function setType($value = self::AUTO) + { + if (!in_array($value, array(self::AUTO, self::FIRST, self::EVEN))) { + $value = self::AUTO; + } + $this->type = $value; + } + /** * Get header type + * + * @return string */ public function getType() { - return $this->headerType; + return $this->type; } /** * Reset type to default + * + * @return string */ public function resetType() { - return $this->headerType = self::AUTO; + return $this->type = self::AUTO; } /** * First page only header + * + * @return string */ public function firstPage() { - return $this->headerType = self::FIRST; + return $this->type = self::FIRST; } /** * Even numbered pages only + * + * @return string */ public function evenPage() { - return $this->headerType = self::EVEN; + return $this->type = self::EVEN; } } diff --git a/src/PhpWord/Container/Section.php b/src/PhpWord/Container/Section.php index 69edec41..46f2d44e 100644 --- a/src/PhpWord/Container/Section.php +++ b/src/PhpWord/Container/Section.php @@ -9,6 +9,7 @@ namespace PhpOffice\PhpWord\Container; +use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\TOC; use PhpOffice\PhpWord\Container\Footer; use PhpOffice\PhpWord\Container\Header; @@ -28,19 +29,18 @@ class Section extends Container private $settings; /** - * Section headers + * Section headers, indexed from 1, not zero * * @var Header[] */ private $headers = array(); /** - * Section footer + * Section footers, indexed from 1, not zero * - * @var Footer + * @var Footer[] */ - private $footer = null; - + private $footers = array(); /** * Create new instance @@ -51,8 +51,8 @@ class Section extends Container public function __construct($sectionCount, $settings = null) { $this->container = 'section'; - $this->containerId = $sectionCount; - $this->setDocPart($this->container, $this->containerId); + $this->sectionId = $sectionCount; + $this->setDocPart($this->container, $this->sectionId); $this->settings = new Settings(); $this->setSettings($settings); } @@ -109,29 +109,29 @@ class Section extends Container /** * Add header * + * @param string $type * @return Header + * @since 0.9.2 */ - public function addHeader() + public function addHeader($type = Header::AUTO) { - $header = new Header($this->containerId); - $this->headers[] = $header; - return $header; + return $this->addHeaderFooter($type, true); } /** * Add footer * + * @param string $type * @return Footer + * @since 0.9.2 */ - public function addFooter() + public function addFooter($type = Header::AUTO) { - $footer = new Footer($this->containerId); - $this->footer = $footer; - return $footer; + return $this->addHeaderFooter($type, false); } /** - * Get Headers + * Get header elements * * @return Header[] */ @@ -141,13 +141,13 @@ class Section extends Container } /** - * Get footer element + * Get footer elements * - * @return Footer + * @return Footer[] */ - public function getFooter() + public function getFooters() { - return $this->footer; + return $this->footers; } /** @@ -160,10 +160,38 @@ class Section extends Container */ public function hasDifferentFirstPage() { - $value = array_filter($this->headers, function (Header &$header) { - return $header->getType() == Header::FIRST; - }); - return count($value) > 0; + foreach ($this->headers as $header) { + if ($header->getType() == Header::FIRST) { + return true; + } + } + return false; + } + + /** + * Add header/footer + * + * @param string $type + * @param string $header + * @return Header|Footer + * @since 0.9.2 + */ + private function addHeaderFooter($type = Header::AUTO, $header = true) + { + $collectionArray = $header ? 'headers' : 'footers'; + $containerClass = 'PhpOffice\\PhpWord\\Container\\'; + $containerClass .= ($header ? 'Header' : 'Footer'); + $collection = &$this->$collectionArray; + + if (in_array($type, array(Header::AUTO, Header::FIRST, Header::EVEN))) { + $index = count($collection); + $container = new $containerClass($this->sectionId, ++$index, $type); + $collection[$index] = $container; + return $container; + } else { + throw new Exception('Invalid header/footer type.'); + } + } /** @@ -189,4 +217,20 @@ class Section extends Container { return $this->addFooter(); } + + /** + * Get footer + * + * @return Footer + * @deprecated 0.9.2 + * @codeCoverageIgnore + */ + public function getFooter() + { + if (empty($this->footers)) { + return null; + } else { + return $this->footers[1]; + } + } } diff --git a/src/PhpWord/Element/Element.php b/src/PhpWord/Element/Element.php index b3565775..ec0dbc5c 100644 --- a/src/PhpWord/Element/Element.php +++ b/src/PhpWord/Element/Element.php @@ -30,6 +30,10 @@ abstract class Element /** * Document part Id * + * For header and footer, this will be = ($sectionId - 1) * 3 + $index + * because the max number of header/footer in every page is 3, i.e. + * AUTO, FIRST, and EVEN (AUTO = ODD) + * * @var integer */ private $docPartId = 1; diff --git a/src/PhpWord/Element/TextRun.php b/src/PhpWord/Element/TextRun.php index 794ad230..6be08fc4 100755 --- a/src/PhpWord/Element/TextRun.php +++ b/src/PhpWord/Element/TextRun.php @@ -28,8 +28,6 @@ class TextRun extends Container * Create new instance * * @param string|array|Paragraph $paragraphStyle - * @param string $docPart section|header|footer - * @param int $docPartId */ public function __construct($paragraphStyle = null) { diff --git a/src/PhpWord/Footnote.php b/src/PhpWord/Footnote.php index 1c11bbff..9b11518b 100644 --- a/src/PhpWord/Footnote.php +++ b/src/PhpWord/Footnote.php @@ -77,7 +77,7 @@ class Footnote */ public static function addFootnoteLinkElement($linkSrc) { - return Media::addMediaElement('footnotes', 'hyperlink', $linkSrc); + return Media::addElement('footnotes', 'link', $linkSrc); } /** @@ -89,6 +89,6 @@ class Footnote */ public static function getFootnoteLinkElements() { - return Media::getMediaElements('footnotes', 'hyperlink'); + return Media::getElements('footnotes', 'link'); } } diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index 63c88361..921fc154 100755 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -9,6 +9,7 @@ namespace PhpOffice\PhpWord; +use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Element\Image; /** @@ -21,7 +22,7 @@ class Media * * @var array */ - private static $media = array(); + private static $elements = array(); /** * Add new media element @@ -31,19 +32,20 @@ class Media * @param string $source * @param Image $image * @return integer + * @since 0.9.2 */ - public static function addMediaElement($container, $mediaType, $source, Image $image = null) + public static function addElement($container, $mediaType, $source, Image $image = null) { // 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(); + if (!array_key_exists($container, self::$elements)) { + self::$elements[$container]= array(); } // Add media if not exists or point to existing media - if (!array_key_exists($mediaId, self::$media[$container])) { - $mediaCount = self::countMediaElements($container); - $mediaTypeCount = self::countMediaElements($container, $mediaType); + if (!array_key_exists($mediaId, self::$elements[$container])) { + $mediaCount = self::countElements($container); + $mediaTypeCount = self::countElements($container, $mediaType); $mediaData = array(); $relId = ++$mediaCount; $target = null; @@ -51,16 +53,17 @@ class Media // Images if ($mediaType == 'image') { - $isMemImage = false; - if (!is_null($image)) { - $isMemImage = $image->getIsMemImage(); - $ext = $image->getImageExtension(); - $ext = strtolower($ext); + if (is_null($image)) { + throw new Exception('Image object not assigned.'); } + $isMemImage = $image->getIsMemImage(); + $ext = $image->getImageExtension(); + $mediaData['imageExtension'] = $ext; + $mediaData['imageType'] = $image->getImageType(); if ($isMemImage) { $mediaData['isMemImage'] = true; - $mediaData['createfunction'] = $image->getImageCreateFunction(); - $mediaData['imagefunction'] = $image->getImageFunction(); + $mediaData['createFunction'] = $image->getImageCreateFunction(); + $mediaData['imageFunction'] = $image->getImageFunction(); } $target = "media/{$container}_image{$mediaTypeCount}.{$ext}"; // Objects @@ -76,10 +79,10 @@ class Media $mediaData['target'] = $target; $mediaData['type'] = $mediaType; $mediaData['rID'] = $relId; - self::$media[$container][$mediaId] = $mediaData; + self::$elements[$container][$mediaId] = $mediaData; return $relId; } else { - return self::$media[$container][$mediaId]['rID']; + return self::$elements[$container][$mediaId]['rID']; } } @@ -89,13 +92,14 @@ class Media * @param string $container section|headerx|footerx|footnote * @param string $mediaType image|object|link * @return integer + * @since 0.9.2 */ - public static function countMediaElements($container, $mediaType = null) + public static function countElements($container, $mediaType = null) { $mediaCount = 0; - if (array_key_exists($container, self::$media)) { - foreach (self::$media[$container] as $mediaKey => $mediaData) { + if (array_key_exists($container, self::$elements)) { + foreach (self::$elements[$container] as $mediaKey => $mediaData) { if (!is_null($mediaType)) { if ($mediaType == $mediaData['type']) { $mediaCount++; @@ -115,23 +119,24 @@ class Media * @param string $container section|headerx|footerx|footnote * @param string $mediaType image|object|link * @return array + * @since 0.9.2 */ - public static function getMediaElements($container, $mediaType = null) + public static function getElements($container, $mediaType = null) { $mediaElements = array(); // If header/footer, search for headerx and footerx where x is number if ($container == 'header' || $container == 'footer') { - foreach (self::$media as $key => $val) { + foreach (self::$elements as $key => $val) { if (substr($key, 0, 6) == $container) { $mediaElements[$key] = $val; } } } else { - if (!array_key_exists($container, self::$media)) { + if (!array_key_exists($container, self::$elements)) { return $mediaElements; } - foreach (self::$media[$container] as $mediaKey => $mediaData) { + foreach (self::$elements[$container] as $mediaKey => $mediaData) { if (!is_null($mediaType)) { if ($mediaType == $mediaData['type']) { $mediaElements[$mediaKey] = $mediaData; @@ -150,7 +155,7 @@ class Media */ public static function reset() { - self::$media = array(); + self::$elements = array(); } /** @@ -159,13 +164,13 @@ class Media * @param string $src * @param string $type * @param Image $image - * @return integer|array + * @return integer * @deprecated 0.9.2 * @codeCoverageIgnore */ public static function addSectionMediaElement($src, $type, Image $image = null) { - return self::addMediaElement("section", $type, $src, $image); + return self::addElement('section', $type, $src, $image); } /** @@ -178,7 +183,7 @@ class Media */ public static function addSectionLinkElement($linkSrc) { - return self::addMediaElement('section', 'link', $linkSrc); + return self::addElement('section', 'link', $linkSrc); } /** @@ -191,7 +196,7 @@ class Media */ public static function getSectionMediaElements($key = null) { - return self::getMediaElements('section', $key); + return self::getElements('section', $key); } /** @@ -204,7 +209,7 @@ class Media */ public static function countSectionMediaElements($key = null) { - return self::countMediaElements('section', $key); + return self::countElements('section', $key); } /** @@ -219,7 +224,7 @@ class Media */ public static function addHeaderMediaElement($headerCount, $src, Image $image = null) { - return self::addMediaElement("header{$headerCount}", 'image', $src, $image); + return self::addElement("header{$headerCount}", 'image', $src, $image); } /** @@ -232,7 +237,7 @@ class Media */ public static function countHeaderMediaElements($key) { - return self::countMediaElements($key); + return self::countElements($key); } /** @@ -244,7 +249,7 @@ class Media */ public static function getHeaderMediaElements() { - return self::getMediaElements('header'); + return self::getElements('header'); } /** @@ -259,7 +264,7 @@ class Media */ public static function addFooterMediaElement($footerCount, $src, Image $image = null) { - return self::addMediaElement("footer{$footerCount}", 'image', $src, $image); + return self::addElement("footer{$footerCount}", 'image', $src, $image); } /** @@ -272,7 +277,7 @@ class Media */ public static function countFooterMediaElements($key) { - return self::countMediaElements($key); + return self::countElements($key); } /** @@ -284,6 +289,6 @@ class Media */ public static function getFooterMediaElements() { - return self::getMediaElements('footer'); + return self::getElements('footer'); } } diff --git a/src/PhpWord/Writer/ODText/Manifest.php b/src/PhpWord/Writer/ODText/Manifest.php index 3712cf66..ecda1918 100755 --- a/src/PhpWord/Writer/ODText/Manifest.php +++ b/src/PhpWord/Writer/ODText/Manifest.php @@ -63,22 +63,4 @@ class Manifest extends WriterPart // Return return $xmlWriter->getData(); } - - - /** - * Get image mime type - * - * @param string $pFile Filename - * @return string Mime Type - * @throws Exception - */ - private function getImageMimeType($pFile = '') - { - if (file_exists($pFile)) { - $image = getimagesize($pFile); - return image_type_to_mime_type($image[2]); - } else { - throw new Exception("File $pFile does not exist"); - } - } } diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 084deb8d..ba887f01 100755 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -29,18 +29,18 @@ use PhpOffice\PhpWord\Writer\Word2007\Styles; class Word2007 extends Writer implements IWriter { /** - * Types of images + * Content types values * * @var array */ - private $imageTypes = array(); + private $cTypes = array('default' => array(), 'override' => array()); /** - * Types of objects + * Document relationship * * @var array */ - private $objectTypes = array(); + private $docRels = array(); /** * Create new Word2007 writer @@ -98,14 +98,18 @@ class Word2007 extends Writer implements IWriter } } - // Add section media files - $sectionElements = array(); + // Content types + $this->cTypes['default'] = array( + 'rels' => 'application/vnd.openxmlformats-package.relationships+xml', + 'xml' => 'application/xml', + ); - $secElements = Media::getMediaElements('section'); - if (!empty($secElements)) { - $this->addFilesToPackage($objZip, $secElements); - foreach ($secElements as $element) { - $sectionElements[] = $element; + // Add section media files + $sectionMedia = Media::getElements('section'); + if (!empty($sectionMedia)) { + $this->addFilesToPackage($objZip, $sectionMedia); + foreach ($sectionMedia as $element) { + $this->docRels[] = $element; } } @@ -114,70 +118,32 @@ class Word2007 extends Writer implements IWriter $this->addHeaderFooterMedia($objZip, 'footer'); // Add header/footer contents - $cHdrs = 0; - $cFtrs = 0; - $rID = Media::countMediaElements('section') + 6; // @see Rels::writeDocRels for 6 first elements + $overrides = array(); + $rID = Media::countElements('section') + 6; // @see Rels::writeDocRels for 6 first elements $sections = $this->phpWord->getSections(); - $footers = array(); foreach ($sections as $section) { - $headers = $section->getHeaders(); - 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; - if (!is_null($footer)) { - $footer->setRelationId(++$rID); - $footerCount = $footer->getSectionId(); - $ftrFile = "footer{$footerCount}.xml"; - $sectionElements[] = array('target' => $ftrFile, 'type' => 'footer', 'rID' => $rID); - $objZip->addFromString( - "word/{$ftrFile}", - $this->getWriterPart('footer')->writeFooter($footer) - ); - } + $this->addHeaderFooterContent($section, $objZip, 'header', $rID); + $this->addHeaderFooterContent($section, $objZip, 'footer', $rID); } // Add footnotes media files, relations, and contents if (Footnote::countFootnoteElements() > 0) { - $sectionElements[] = array('target' => 'footnotes.xml', 'type' => 'footnotes', 'rID' => ++$rID); - $footnoteMedia = Media::getMediaElements('footnote'); + $footnoteMedia = Media::getElements('footnote'); $this->addFilesToPackage($objZip, $footnoteMedia); if (!empty($footnoteMedia)) { - $objZip->addFromString( - 'word/_rels/footnotes.xml.rels', - $this->getWriterPart('rels')->writeMediaRels($footnoteMedia) - ); + $objZip->addFromString('word/_rels/footnotes.xml.rels', $this->getWriterPart('rels')->writeMediaRels($footnoteMedia)); } - $objZip->addFromString( - 'word/footnotes.xml', - $this->getWriterPart('footnotes')->writeFootnotes(Footnote::getFootnoteElements()) - ); + $objZip->addFromString('word/footnotes.xml', $this->getWriterPart('footnotes')->writeFootnotes(Footnote::getFootnoteElements())); + $this->cTypes['override']["/word/footnotes.xml"] = 'footnotes'; + $this->docRels[] = array('target' => 'footnotes.xml', 'type' => 'footnotes', 'rID' => ++$rID); } // Write dynamic files - $objZip->addFromString( - '[Content_Types].xml', - $this->getWriterPart('contenttypes')->writeContentTypes( - $this->imageTypes, - $this->objectTypes, - $cHdrs, - $footers - ) - ); + $objZip->addFromString('[Content_Types].xml', $this->getWriterPart('contenttypes')->writeContentTypes($this->cTypes)); $objZip->addFromString('_rels/.rels', $this->getWriterPart('rels')->writeMainRels()); $objZip->addFromString('docProps/app.xml', $this->getWriterPart('docprops')->writeDocPropsApp($this->phpWord)); $objZip->addFromString('docProps/core.xml', $this->getWriterPart('docprops')->writeDocPropsCore($this->phpWord)); - $objZip->addFromString('word/_rels/document.xml.rels', $this->getWriterPart('rels')->writeDocRels($sectionElements)); + $objZip->addFromString('word/_rels/document.xml.rels', $this->getWriterPart('rels')->writeDocRels($this->docRels)); $objZip->addFromString('word/document.xml', $this->getWriterPart('document')->writeDocument($this->phpWord)); $objZip->addFromString('word/styles.xml', $this->getWriterPart('styles')->writeStyles($this->phpWord)); @@ -200,54 +166,7 @@ class Word2007 extends Writer implements IWriter } /** - * Check content types - * - * @param string $src - */ - private function checkContentTypes($src) - { - $extension = null; - if (stripos(strrev($src), strrev('.php')) === 0) { - $extension = 'php'; - } else { - if (function_exists('exif_imagetype')) { - $imageType = exif_imagetype($src); - } else { - $tmp = getimagesize($src); - $imageType = $tmp[2]; - } - if ($imageType === \IMAGETYPE_JPEG) { - $extension = 'jpg'; - } elseif ($imageType === \IMAGETYPE_GIF) { - $extension = 'gif'; - } elseif ($imageType === \IMAGETYPE_PNG) { - $extension = 'png'; - } elseif ($imageType === \IMAGETYPE_BMP) { - $extension = 'bmp'; - } elseif ($imageType === \IMAGETYPE_TIFF_II || $imageType === \IMAGETYPE_TIFF_MM) { - $extension = 'tif'; - } - } - - if (isset($extension)) { - $imageData = getimagesize($src); - $imageType = image_type_to_mime_type($imageData[2]); - $imageExtension = str_replace('.', '', image_type_to_extension($imageData[2])); - if ($imageExtension === 'jpeg') { - $imageExtension = 'jpg'; - } - if (!in_array($imageType, $this->imageTypes)) { - $this->imageTypes[$imageExtension] = $imageType; - } - } else { - if (!in_array($extension, $this->objectTypes)) { - $this->objectTypes[] = $extension; - } - } - } - - /** - * Check content types + * Add section files to package * * @param mixed $objZip * @param mixed $elements @@ -255,47 +174,78 @@ class Word2007 extends Writer implements IWriter private function addFilesToPackage($objZip, $elements) { foreach ($elements as $element) { + // Do not add link if ($element['type'] == 'link') { continue; } + // Retrieve remote image if (isset($element['isMemImage']) && $element['isMemImage']) { - $image = call_user_func($element['createfunction'], $element['source']); + $image = call_user_func($element['createFunction'], $element['source']); ob_start(); - call_user_func($element['imagefunction'], $image); + 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']); + } + // Register content types + if ($element['type'] == 'image') { + $imageExtension = $element['imageExtension']; + $imageType = $element['imageType']; + if (!array_key_exists($imageExtension, $this->cTypes['default'])) { + $this->cTypes['default'][$imageExtension] = $imageType; + } + } else { + if (!array_key_exists('bin', $this->cTypes['default'])) { + $this->cTypes['default']['bin'] = 'application/vnd.openxmlformats-officedocument.oleObject'; + } } } } /** - * Add header/footer media elements + * Add header/footer media files * * @param mixed $objZip * @param string $docPart */ private function addHeaderFooterMedia($objZip, $docPart) { - $elements = Media::getMediaElements($docPart); + $elements = Media::getElements($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); } + $objZip->addFromString("word/_rels/{$file}.xml.rels", $this->getWriterPart('rels')->writeMediaRels($media)); } } } } + + /** + * Add header/footer content + * + * @param Section $section + * @param string $elmType + * @param integer $rID + */ + private function addHeaderFooterContent(&$section, $objZip, $elmType, &$rID) + { + $getFunction = $elmType == 'header' ? 'getHeaders' : 'getFooters'; + $writeFunction = $elmType == 'header' ? 'writeHeader' : 'writeFooter'; + $elmCount = ($section->getSectionId() - 1) * 3; + $elmObjects = $section->$getFunction(); + foreach ($elmObjects as $index => &$elmObject) { + $elmCount++; + $elmObject->setRelationId(++$rID); + $elmFile = "{$elmType}{$elmCount}.xml"; + $objZip->addFromString("word/$elmFile", $this->getWriterPart($elmType)->$writeFunction($elmObject)); + $this->cTypes['override']["/word/$elmFile"] = $elmType; + $this->docRels[] = array('target' => $elmFile, 'type' => $elmType, 'rID' => $rID); + } + } } diff --git a/src/PhpWord/Writer/Word2007/ContentTypes.php b/src/PhpWord/Writer/Word2007/ContentTypes.php index 9a383e9b..7b371b07 100755 --- a/src/PhpWord/Writer/Word2007/ContentTypes.php +++ b/src/PhpWord/Writer/Word2007/ContentTypes.php @@ -19,28 +19,13 @@ class ContentTypes extends WriterPart { /** * Write [Content_Types].xml - * @param array $imageTypes - * @param array $objectTypes - * @param int $cHdrs - * @param array $footers + * + * @param array $contentTypes */ - public function writeContentTypes($imageTypes, $objectTypes, $cHdrs, $footers) + public function writeContentTypes($contentTypes) { - $OpenXMLPrefix = 'application/vnd.openxmlformats-'; - $WordMLPrefix = $OpenXMLPrefix . 'officedocument.wordprocessingml.'; - - $defaults = array( - 'rels' => $OpenXMLPrefix . 'package.relationships+xml', - 'xml' => 'application/xml', - - ); - if (is_array($imageTypes)) { - $defaults = array_merge($defaults, $imageTypes); - } - if (count($objectTypes) > 0) { - $defaults['bin'] = $OpenXMLPrefix . 'officedocument.oleObject'; - } + $WordMLPrefix = $OpenXMLPrefix . 'officedocument.wordprocessingml.'; $overrides = array( '/docProps/core.xml' => $OpenXMLPrefix . 'package.core-properties+xml', '/docProps/app.xml' => $OpenXMLPrefix . 'officedocument.extended-properties+xml', @@ -51,14 +36,12 @@ class ContentTypes extends WriterPart '/word/theme/theme1.xml' => $OpenXMLPrefix . 'officedocument.theme+xml', '/word/webSettings.xml' => $WordMLPrefix . 'webSettings+xml', '/word/fontTable.xml' => $WordMLPrefix . 'fontTable+xml', - '/word/footnotes.xml' => $WordMLPrefix . 'footnotes+xml', ); - for ($i = 1; $i <= $cHdrs; $i++) { - $overrides["/word/header{$i}.xml"] = $WordMLPrefix . 'header+xml'; - } - for ($i = 1; $i <= count($footers); $i++) { - if (!is_null($footers[$i])) { - $overrides["/word/footer{$i}.xml"] = $WordMLPrefix . 'footer+xml'; + + $defaults = $contentTypes['default']; + if (!empty($contentTypes['override'])) { + foreach ($contentTypes['override'] as $key => $val) { + $overrides[$key] = $WordMLPrefix . $val . '+xml'; } } @@ -66,12 +49,8 @@ class ContentTypes extends WriterPart $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->startElement('Types'); $xmlWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/content-types'); - foreach ($defaults as $key => $value) { - $this->writeContentType($xmlWriter, true, $key, $value); - } - foreach ($overrides as $key => $value) { - $this->writeContentType($xmlWriter, false, $key, $value); - } + $this->writeContentType($xmlWriter, $defaults, true); + $this->writeContentType($xmlWriter, $overrides, false); $xmlWriter->endElement(); return $xmlWriter->getData(); @@ -86,62 +65,19 @@ class ContentTypes extends WriterPart * @param string $contentType Content type * @throws Exception */ - private function writeContentType(XMLWriter $xmlWriter, $isDefault, $partName = '', $contentType = '') + private function writeContentType(XMLWriter $xmlWriter, $parts, $isDefault) { - if ($partName != '' && $contentType != '') { - $element = $isDefault ? 'Default' : 'Override'; - $partAttribute = $isDefault ? 'Extension' : 'PartName'; - $xmlWriter->startElement($element); - $xmlWriter->writeAttribute($partAttribute, $partName); - $xmlWriter->writeAttribute('ContentType', $contentType); - $xmlWriter->endElement(); - } else { - throw new Exception("Invalid parameters passed."); + foreach ($parts as $partName => $contentType) { + if ($partName != '' && $contentType != '') { + $partType = $isDefault ? 'Default' : 'Override'; + $partAttribute = $isDefault ? 'Extension' : 'PartName'; + $xmlWriter->startElement($partType); + $xmlWriter->writeAttribute($partAttribute, $partName); + $xmlWriter->writeAttribute('ContentType', $contentType); + $xmlWriter->endElement(); + } else { + throw new Exception("Invalid parameters passed."); + } } } - - /** - * Get image mime type - * - * @param string $pFile Filename - * @return string Mime Type - * @throws Exception - */ - private function getImageMimeType($pFile = '') - { - if (file_exists($pFile)) { - $image = getimagesize($pFile); - return image_type_to_mime_type($image[2]); - } else { - throw new Exception("File $pFile does not exist"); - } - } - - /** - * Write Default XML element - * - * @param XMLWriter $xmlWriter - * @param string $partName Part name - * @param string $contentType Content type - * @deprecated 0.9.2 - * @codeCoverageIgnore - */ - private function writeDefaultContentType(XMLWriter $xmlWriter, $partName = '', $contentType = '') - { - $this->writeContentType($xmlWriter, true, $partName, $contentType); - } - - /** - * Write Override XML element - * - * @param XMLWriter $xmlWriter - * @param string $partName Part name - * @param string $contentType Content type - * @deprecated 0.9.2 - * @codeCoverageIgnore - */ - private function writeOverrideContentType(XMLWriter $xmlWriter, $partName = '', $contentType = '') - { - $this->writeContentType($xmlWriter, false, $partName, $contentType); - } } diff --git a/src/PhpWord/Writer/Word2007/Document.php b/src/PhpWord/Writer/Word2007/Document.php index 499aad19..98f158fc 100644 --- a/src/PhpWord/Writer/Word2007/Document.php +++ b/src/PhpWord/Writer/Word2007/Document.php @@ -103,7 +103,7 @@ class Document extends Base { $settings = $section->getSettings(); $headers = $section->getHeaders(); - $footer = $section->getFooter(); + $footers = $section->getFooters(); $pgSzW = $settings->getPageSizeW(); $pgSzH = $settings->getPageSizeH(); $orientation = $settings->getOrientation(); @@ -139,19 +139,19 @@ class Document extends Base $xmlWriter->writeAttribute('r:id', 'rId' . $rId); $xmlWriter->endElement(); } - if ($section->hasDifferentFirstPage()) { - $xmlWriter->startElement('w:titlePg'); - $xmlWriter->endElement(); - } - // Footer reference - if (!is_null($footer)) { + foreach ($footers as &$footer) { $rId = $footer->getRelationId(); $xmlWriter->startElement('w:footerReference'); - $xmlWriter->writeAttribute('w:type', 'default'); + $xmlWriter->writeAttribute('w:type', $footer->getType()); $xmlWriter->writeAttribute('r:id', 'rId' . $rId); $xmlWriter->endElement(); } + // Different first page + if ($section->hasDifferentFirstPage()) { + $xmlWriter->startElement('w:titlePg'); + $xmlWriter->endElement(); + } // Page size & orientation $xmlWriter->startElement('w:pgSz'); diff --git a/src/PhpWord/Writer/Word2007/Rels.php b/src/PhpWord/Writer/Word2007/Rels.php index bbf0634c..d7552410 100755 --- a/src/PhpWord/Writer/Word2007/Rels.php +++ b/src/PhpWord/Writer/Word2007/Rels.php @@ -29,13 +29,13 @@ class Rels extends WriterPart */ public function writeMainRels() { - $rels = array( - 'word/document.xml' => 'officeDocument/2006/relationships/officeDocument', + $xmlRels = array( 'docProps/core.xml' => 'package/2006/relationships/metadata/core-properties', 'docProps/app.xml' => 'officeDocument/2006/relationships/extended-properties', + 'word/document.xml' => 'officeDocument/2006/relationships/officeDocument', ); $xmlWriter = $this->getXmlWriter(); - $this->writeRels($xmlWriter, $rels); + $this->writeRels($xmlWriter, $xmlRels); return $xmlWriter->getData(); } @@ -47,7 +47,7 @@ class Rels extends WriterPart */ public function writeDocRels($mediaRels) { - $rels = array( + $xmlRels = array( 'styles.xml' => 'officeDocument/2006/relationships/styles', 'numbering.xml' => 'officeDocument/2006/relationships/numbering', 'settings.xml' => 'officeDocument/2006/relationships/settings', @@ -56,7 +56,7 @@ class Rels extends WriterPart 'fontTable.xml' => 'officeDocument/2006/relationships/fontTable', ); $xmlWriter = $this->getXmlWriter(); - $this->writeRels($xmlWriter, $rels, $mediaRels); + $this->writeRels($xmlWriter, $xmlRels, $mediaRels); return $xmlWriter->getData(); } @@ -79,20 +79,24 @@ class Rels extends WriterPart * Write relationships * * @param XMLWriter $xmlWriter - * @param null|array $rels + * @param null|array $xmlRels * @param null|array $mediaRels * @param integer $id */ - private function writeRels(XMLWriter $xmlWriter, $rels = null, $mediaRels = null, $id = 1) + private function writeRels(XMLWriter $xmlWriter, $xmlRels = null, $mediaRels = null, $id = 1) { $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->startElement('Relationships'); $xmlWriter->writeAttribute('xmlns', self::RELS_BASE . 'package/2006/relationships'); - if (is_array($rels)) { - foreach ($rels as $target => $type) { + + // XML files relationships + if (is_array($xmlRels)) { + foreach ($xmlRels as $target => $type) { $this->writeRel($xmlWriter, $id++, $type, $target); } } + + // Media relationships if (!is_null($mediaRels) && is_array($mediaRels)) { $mapping = array('image' => 'image', 'object' => 'oleObject', 'link' => 'hyperlink'); foreach ($mediaRels as $mediaRel) { diff --git a/src/PhpWord/Writer/Word2007/Styles.php b/src/PhpWord/Writer/Word2007/Styles.php index c7b35faf..f0305f7f 100644 --- a/src/PhpWord/Writer/Word2007/Styles.php +++ b/src/PhpWord/Writer/Word2007/Styles.php @@ -28,6 +28,10 @@ class Styles extends Base */ public function writeStyles(PhpWord $phpWord = null) { + if (is_null($phpWord)) { + throw new Exception("No PhpWord assigned."); + } + // Create XML writer $xmlWriter = $this->getXmlWriter(); diff --git a/tests/PhpWord/Tests/Container/SectionTest.php b/tests/PhpWord/Tests/Container/SectionTest.php index 268f66e8..49b59b20 100644 --- a/tests/PhpWord/Tests/Container/SectionTest.php +++ b/tests/PhpWord/Tests/Container/SectionTest.php @@ -40,10 +40,10 @@ class SectionTest extends \PHPUnit_Framework_TestCase /** * Get footer */ - public function testGetFooter() + public function testGetFooters() { $oSection = new Section(0); - $this->assertAttributeEquals($oSection->getFooter(), 'footer', new Section(0)); + $this->assertAttributeEquals($oSection->getFooters(), 'footers', new Section(0)); } /** diff --git a/tests/PhpWord/Tests/MediaTest.php b/tests/PhpWord/Tests/MediaTest.php index 3df52e4a..14a38c2d 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::getMediaElements('section'), array()); + $this->assertEquals(Media::getElements('section'), array()); } /** @@ -33,7 +33,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase */ public function testCountSectionMediaElementsWithNull() { - $this->assertEquals(Media::countMediaElements('section'), 0); + $this->assertEquals(Media::countElements('section'), 0); } /** @@ -44,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::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); + Media::addElement('section', 'image', $local, new Image($local)); + Media::addElement('section', 'image', $local, new Image($local)); + Media::addElement('section', 'image', $remote, new Image($local)); + Media::addElement('section', 'object', $object); + Media::addElement('section', 'object', $object); - $this->assertEquals(3, Media::countMediaElements('section')); + $this->assertEquals(3, Media::countElements('section')); } /** @@ -58,12 +58,12 @@ class MediaTest extends \PHPUnit_Framework_TestCase */ public function testAddSectionLinkElement() { - $expected = Media::countMediaElements('section') + 1; - $actual = Media::addMediaElement('section', 'link', 'http://test.com'); + $expected = Media::countElements('section') + 1; + $actual = Media::addElement('section', 'link', 'http://test.com'); $this->assertEquals($expected, $actual); - $this->assertEquals(1, Media::countMediaElements('section', 'link')); - $this->assertEquals(1, count(Media::getMediaElements('section', 'link'))); + $this->assertEquals(1, Media::countElements('section', 'link')); + $this->assertEquals(1, count(Media::getElements('section', 'link'))); } /** @@ -73,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::addMediaElement('header1', 'image', $local, new Image($local)); - Media::addMediaElement('header1', 'image', $local, new Image($local)); - Media::addMediaElement('header1', 'image', $remote, new Image($remote)); + Media::addElement('header1', 'image', $local, new Image($local)); + Media::addElement('header1', 'image', $local, new Image($local)); + Media::addElement('header1', 'image', $remote, new Image($remote)); - $this->assertEquals(2, Media::countMediaElements('header1')); - $this->assertEquals(2, count(Media::getMediaElements('header1'))); - $this->assertEmpty(Media::getMediaElements('header2')); + $this->assertEquals(2, Media::countElements('header1')); + $this->assertEquals(2, count(Media::getElements('header1'))); + $this->assertEmpty(Media::getElements('header2')); } /** @@ -89,13 +89,24 @@ class MediaTest extends \PHPUnit_Framework_TestCase { $local = __DIR__ . "/_files/images/mars.jpg"; $remote = 'http://php.net/images/logos/php-med-trans-light.gif'; - Media::addMediaElement('footer1', 'image', $local, new Image($local)); - Media::addMediaElement('footer1', 'image', $local, new Image($local)); - Media::addMediaElement('footer1', 'image', $remote, new Image($remote)); + Media::addElement('footer1', 'image', $local, new Image($local)); + Media::addElement('footer1', 'image', $local, new Image($local)); + Media::addElement('footer1', 'image', $remote, new Image($remote)); - $this->assertEquals(2, Media::countMediaElements('footer1')); + $this->assertEquals(2, Media::countElements('footer1')); Media::reset(); - $this->assertEquals(0, Media::countMediaElements('footer1')); + $this->assertEquals(0, Media::countElements('footer1')); + } + + /** + * Add image element exception + * + * @expectedException Exception + * @expectedExceptionMessage Image object not assigned. + */ + public function testAddElementImageException() + { + Media::addElement('section', 'image', __DIR__ . "/_files/images/mars.jpg"); } }