From 2cdad4b247db870058fa6081e05f0ebc9cb92c20 Mon Sep 17 00:00:00 2001 From: Ivan Lanin Date: Tue, 8 Apr 2014 16:09:31 +0700 Subject: [PATCH] Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table --- CHANGELOG.md | 7 +- samples/resources/Sample_11_ReadWord2007.docx | Bin 22104 -> 69796 bytes src/PhpWord/DocumentProperties.php | 24 +- src/PhpWord/Element/AbstractElement.php | 27 +- src/PhpWord/Element/Footnote.php | 6 +- src/PhpWord/Element/Image.php | 4 +- src/PhpWord/Element/ListItem.php | 3 +- src/PhpWord/Element/Object.php | 4 +- src/PhpWord/Element/Table.php | 3 +- src/PhpWord/Footnote.php | 108 ++- src/PhpWord/IOFactory.php | 10 +- src/PhpWord/Media.php | 18 +- src/PhpWord/PhpWord.php | 6 +- src/PhpWord/Reader/AbstractReader.php | 4 +- src/PhpWord/Reader/Word2007.php | 907 ++++++++++-------- src/PhpWord/Shared/XMLReader.php | 54 +- src/PhpWord/Shared/ZipArchive.php | 57 +- src/PhpWord/Style.php | 6 +- src/PhpWord/TOC.php | 4 +- src/PhpWord/Writer/Word2007.php | 18 +- src/PhpWord/Writer/Word2007/Base.php | 4 +- tests/PhpWord/Tests/FootnoteTest.php | 2 +- tests/PhpWord/Tests/MediaTest.php | 2 +- tests/PhpWord/Tests/StyleTest.php | 2 +- tests/PhpWord/Tests/TOCTest.php | 2 +- 25 files changed, 762 insertions(+), 520 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc43822b..d5e043b5 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,10 +25,9 @@ This release marked heavy refactorings on internal code structure with the creat - 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 header, footer, textrun, and footnote - @ivanlanin GH-187 -- Media: Add `Media::reset()` to reset all media data - @juzi GH-19 -- Style: Add `Style::reset()` to reset all styles -- Footnote: Add `Footnote::reset()` to reset all footnotes -- TOC: Add `TOC::reset()` to reset all TOC +- Media: Add `Media::resetElements()` to reset all media data - @juzi GH-19 +- General: Add `Style::resetStyles()`, `Footnote::resetElements()`, and `TOC::resetTitles()` - @ivanlanin GH-187 +- Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table - @ivanlanin ### Bugfixes diff --git a/samples/resources/Sample_11_ReadWord2007.docx b/samples/resources/Sample_11_ReadWord2007.docx index fe8ec7acae8a53e80cf6219fed8b439465c7cea8..2143c628c5132f5948c339538bef2c2fabdab545 100644 GIT binary patch delta 62691 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�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 XOY%Ym$3;d0Ljn{1Yy3^&AO8OV3dA8r diff --git a/src/PhpWord/DocumentProperties.php b/src/PhpWord/DocumentProperties.php index b1f02275..f41ae421 100644 --- a/src/PhpWord/DocumentProperties.php +++ b/src/PhpWord/DocumentProperties.php @@ -138,7 +138,7 @@ class DocumentProperties * Set Creator * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setCreator($pValue = '') { @@ -160,7 +160,7 @@ class DocumentProperties * Set Last Modified By * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setLastModifiedBy($pValue = '') { @@ -182,7 +182,7 @@ class DocumentProperties * Set Created * * @param int $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setCreated($pValue = null) { @@ -207,7 +207,7 @@ class DocumentProperties * Set Modified * * @param int $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setModified($pValue = null) { @@ -232,7 +232,7 @@ class DocumentProperties * Set Title * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setTitle($pValue = '') { @@ -254,7 +254,7 @@ class DocumentProperties * Set Description * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setDescription($pValue = '') { @@ -276,7 +276,7 @@ class DocumentProperties * Set Subject * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setSubject($pValue = '') { @@ -298,7 +298,7 @@ class DocumentProperties * Set Keywords * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setKeywords($pValue = '') { @@ -320,7 +320,7 @@ class DocumentProperties * Set Category * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setCategory($pValue = '') { @@ -342,7 +342,7 @@ class DocumentProperties * Set Company * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setCompany($pValue = '') { @@ -364,7 +364,7 @@ class DocumentProperties * Set Manager * * @param string $pValue - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setManager($pValue = '') { @@ -432,7 +432,7 @@ class DocumentProperties * 's': String * 'd': Date/Time * 'b': Boolean - * @return \PhpOffice\PhpWord\DocumentProperties + * @return self */ public function setCustomProperty($propertyName, $propertyValue = '', $propertyType = null) { diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php index d9f9984d..8ee75b90 100644 --- a/src/PhpWord/Element/AbstractElement.php +++ b/src/PhpWord/Element/AbstractElement.php @@ -59,7 +59,7 @@ abstract class AbstractElement * * @var string */ - private $docPart = 'section'; + protected $docPart = 'section'; /** * Document part Id @@ -70,7 +70,7 @@ abstract class AbstractElement * * @var integer */ - private $docPartId = 1; + protected $docPartId = 1; /** * Elements collection @@ -84,7 +84,7 @@ abstract class AbstractElement * * @var int */ - private $relationId; + protected $relationId; /** * Add text element @@ -144,8 +144,8 @@ abstract class AbstractElement $link = new Link(String::toUTF8($linkSrc), String::toUTF8($linkName), $fontStyle, $paragraphStyle); $link->setDocPart($this->getDocPart(), $this->getDocPartId()); - $rID = Media::addElement($elementDocPart, 'link', $linkSrc); - $link->setRelationId($rID); + $rId = Media::addElement($elementDocPart, 'link', $linkSrc); + $link->setRelationId($rId); $this->elements[] = $link; return $link; @@ -271,8 +271,8 @@ abstract class AbstractElement $image = new Image($src, $style, $isWatermark); $image->setDocPart($this->getDocPart(), $this->getDocPartId()); - $rID = Media::addElement($elementDocPart, 'image', $src, $image); - $image->setRelationId($rID); + $rId = Media::addElement($elementDocPart, 'image', $src, $image); + $image->setRelationId($rId); $this->elements[] = $image; return $image; } @@ -301,10 +301,10 @@ abstract class AbstractElement $ext = substr($ext, 0, -1); } $icon = realpath(__DIR__ . "/../_staticDocParts/_{$ext}.png"); - $rID = Media::addElement($elementDocPart, 'object', $src); - $object->setRelationId($rID); - $rIDimg = Media::addElement($elementDocPart, 'image', $icon, new Image($icon)); - $object->setImageRelationId($rIDimg); + $rId = Media::addElement($elementDocPart, 'object', $src); + $object->setRelationId($rId); + $rIdimg = Media::addElement($elementDocPart, 'image', $icon, new Image($icon)); + $object->setImageRelationId($rIdimg); $this->elements[] = $object; return $object; } else { @@ -323,9 +323,10 @@ abstract class AbstractElement $this->checkValidity('footnote'); $footnote = new FootnoteElement($paragraphStyle); - $refID = FootnoteCollection::addFootnoteElement($footnote); + $rId = FootnoteCollection::addFootnoteElement($footnote); + $footnote->setDocPart('footnote', $this->getDocPartId()); - $footnote->setRelationId($refID); + $footnote->setRelationId($rId); $this->elements[] = $footnote; return $footnote; diff --git a/src/PhpWord/Element/Footnote.php b/src/PhpWord/Element/Footnote.php index e79e9c79..d35dd548 100644 --- a/src/PhpWord/Element/Footnote.php +++ b/src/PhpWord/Element/Footnote.php @@ -59,12 +59,12 @@ class Footnote extends AbstractElement /** * Set Footnote Reference ID * - * @param int $refId + * @param int $rId * @deprecated 0.9.2 * @codeCoverageIgnore */ - public function setReferenceId($refId) + public function setReferenceId($rId) { - $this->setRelationId($refId); + $this->setRelationId($rId); } } diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index d49972d2..92e93f1f 100755 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -80,8 +80,8 @@ class Image extends AbstractElement * @param string $source * @param mixed $style * @param boolean $isWatermark - * @throws \PhpOffice\PhpWord\Exception\InvalidImageException - * @throws \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException + * @throws InvalidImageException + * @throws UnsupportedImageTypeException */ public function __construct($source, $style = null, $isWatermark = false) { diff --git a/src/PhpWord/Element/ListItem.php b/src/PhpWord/Element/ListItem.php index 64ac149d..5d19097e 100644 --- a/src/PhpWord/Element/ListItem.php +++ b/src/PhpWord/Element/ListItem.php @@ -9,6 +9,7 @@ namespace PhpOffice\PhpWord\Element; +use PhpOffice\PhpWord\Element\Text; use PhpOffice\PhpWord\Style\ListItem as ListItemStyle; /** @@ -26,7 +27,7 @@ class ListItem extends AbstractElement /** * Textrun * - * @var \PhpOffice\PhpWord\Element\Text + * @var Text */ private $textObject; diff --git a/src/PhpWord/Element/Object.php b/src/PhpWord/Element/Object.php index 058117c9..49a0ca8a 100644 --- a/src/PhpWord/Element/Object.php +++ b/src/PhpWord/Element/Object.php @@ -26,7 +26,7 @@ class Object extends AbstractElement /** * Image Style * - * @var \PhpOffice\PhpWord\Style\Image + * @var ImageStyle */ private $style; @@ -60,7 +60,7 @@ class Object extends AbstractElement /** * Get Image style * - * @return \PhpOffice\PhpWord\Style\Image + * @return ImageStyle */ public function getStyle() { diff --git a/src/PhpWord/Element/Table.php b/src/PhpWord/Element/Table.php index 0c8d494d..90913582 100644 --- a/src/PhpWord/Element/Table.php +++ b/src/PhpWord/Element/Table.php @@ -10,6 +10,7 @@ namespace PhpOffice\PhpWord\Element; use PhpOffice\PhpWord\Element\Row; +use PhpOffice\PhpWord\Element\Cell; use PhpOffice\PhpWord\Style\Table as TableStyle; /** @@ -70,7 +71,7 @@ class Table extends AbstractElement * * @param int $width * @param mixed $style - * @return \PhpOffice\PhpWord\Element\Cell + * @return Cell */ public function addCell($width = null, $style = null) { diff --git a/src/PhpWord/Footnote.php b/src/PhpWord/Footnote.php index 9b11518b..2e38a962 100644 --- a/src/PhpWord/Footnote.php +++ b/src/PhpWord/Footnote.php @@ -13,65 +13,133 @@ use PhpOffice\PhpWord\Media; use PhpOffice\PhpWord\Element\Footnote as FootnoteElement; /** - * Footnote + * Footnote collection */ class Footnote { /** - * Footnote elements + * Elements * * @var array */ private static $elements = array(); + /** + * Add new element + * + * @param FootnoteElement $footnote + * @return integer Reference ID + * @since 0.9.2 + */ + public static function addElement(FootnoteElement $footnote) + { + $rId = self::countElements() + 1; + self::$elements[$rId] = $footnote; + + return $rId; + } + + /** + * Set element + * + * @param integer $index + * @param FootnoteElement $footnote + * @since 0.9.2 + */ + public static function setElement($index, FootnoteElement $footnote) + { + self::$elements[$index] = $footnote; + } + + /** + * Get element by index + * + * @return FootnoteElement + * @since 0.9.2 + */ + public static function getElement($index) + { + if (array_key_exists($index, self::$elements)) { + return self::$elements[$index]; + } else { + return null; + } + } + + /** + * Get elements + * + * @return array + * @since 0.9.2 + */ + public static function getElements() + { + return self::$elements; + } + + /** + * Get element count + * + * @return integer + * @since 0.9.2 + */ + public static function countElements() + { + return count(self::$elements); + } + + /** + * Reset elements + * + * @since 0.9.2 + */ + public static function resetElements() + { + self::$elements = array(); + } + /** * Add new footnote * * @param FootnoteElement $footnote - * @return int Reference ID + * @return integer Reference ID + * @deprecated 0.9.2 + * @codeCoverageIgnore */ public static function addFootnoteElement(FootnoteElement $footnote) { - $refID = self::countFootnoteElements() + 1; - - self::$elements[] = $footnote; - - return $refID; + return self::addElement($footnote); } /** * Get Footnote Elements * * @return array + * @deprecated 0.9.2 + * @codeCoverageIgnore */ public static function getFootnoteElements() { - return self::$elements; + return self::getElements(); } /** * Get Footnote Elements Count * - * @return int + * @return integer + * @deprecated 0.9.2 + * @codeCoverageIgnore */ public static function countFootnoteElements() { - return count(self::$elements); - } - - /** - * Reset footer elements - */ - public static function reset() - { - self::$elements = array(); + return self::countElements(); } /** * Add new Footnote Link Element * * @param string $linkSrc - * @return int Reference ID + * @return integer Reference ID * @deprecated 0.9.2 * @codeCoverageIgnore */ diff --git a/src/PhpWord/IOFactory.php b/src/PhpWord/IOFactory.php index 7b32ef44..ade4398e 100644 --- a/src/PhpWord/IOFactory.php +++ b/src/PhpWord/IOFactory.php @@ -10,6 +10,8 @@ namespace PhpOffice\PhpWord; use PhpOffice\PhpWord\Exception\Exception; +use PhpOffice\PhpWord\Writer\WriterInterface; +use PhpOffice\PhpWord\Reader\ReaderInterface; /** * IO factory @@ -19,9 +21,9 @@ abstract class IOFactory /** * Create new writer * - * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param PhpWord $phpWord * @param string $name - * @return \PhpOffice\PhpWord\Writer\WriterInterface + * @return WriterInterface * @throws Exception */ public static function createWriter(PhpWord $phpWord, $name = 'Word2007') @@ -38,7 +40,7 @@ abstract class IOFactory * Create new reader * * @param string $name - * @return \PhpOffice\PhpWord\Reader\ReaderInterface + * @return ReaderInterface * @throws Exception */ public static function createReader($name = 'Word2007') @@ -56,7 +58,7 @@ abstract class IOFactory * * @param string $filename The name of the file * @param string $readerName - * @return \PhpOffice\PhpWord + * @return PhpWord */ public static function load($filename, $readerName = 'Word2007') { diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index 921fc154..a8822970 100755 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -13,7 +13,7 @@ use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Element\Image; /** - * Media + * Media collection */ class Media { @@ -39,7 +39,7 @@ class Media // Assign unique media Id and initiate media container if none exists $mediaId = md5($container . $source); if (!array_key_exists($container, self::$elements)) { - self::$elements[$container]= array(); + self::$elements[$container] = array(); } // Add media if not exists or point to existing media @@ -47,7 +47,7 @@ class Media $mediaCount = self::countElements($container); $mediaTypeCount = self::countElements($container, $mediaType); $mediaData = array(); - $relId = ++$mediaCount; + $rId = ++$mediaCount; $target = null; $mediaTypeCount++; @@ -57,15 +57,15 @@ class Media throw new Exception('Image object not assigned.'); } $isMemImage = $image->getIsMemImage(); - $ext = $image->getImageExtension(); - $mediaData['imageExtension'] = $ext; + $extension = $image->getImageExtension(); + $mediaData['imageExtension'] = $extension; $mediaData['imageType'] = $image->getImageType(); if ($isMemImage) { $mediaData['isMemImage'] = true; $mediaData['createFunction'] = $image->getImageCreateFunction(); $mediaData['imageFunction'] = $image->getImageFunction(); } - $target = "media/{$container}_image{$mediaTypeCount}.{$ext}"; + $target = "media/{$container}_image{$mediaTypeCount}.{$extension}"; // Objects } elseif ($mediaType == 'object') { $file = "oleObject{$mediaTypeCount}.bin"; @@ -78,9 +78,9 @@ class Media $mediaData['source'] = $source; $mediaData['target'] = $target; $mediaData['type'] = $mediaType; - $mediaData['rID'] = $relId; + $mediaData['rID'] = $rId; self::$elements[$container][$mediaId] = $mediaData; - return $relId; + return $rId; } else { return self::$elements[$container][$mediaId]['rID']; } @@ -153,7 +153,7 @@ class Media /** * Reset media elements */ - public static function reset() + public static function resetElements() { self::$elements = array(); } diff --git a/src/PhpWord/PhpWord.php b/src/PhpWord/PhpWord.php index 63d26a09..29b6472f 100644 --- a/src/PhpWord/PhpWord.php +++ b/src/PhpWord/PhpWord.php @@ -82,8 +82,8 @@ class PhpWord /** * Set document properties object * - * @param \PhpOffice\PhpWord\DocumentProperties $documentProperties - * @return \PhpOffice\PhpWord\PhpWord + * @param DocumentProperties $documentProperties + * @return self */ public function setDocumentProperties(DocumentProperties $documentProperties) { @@ -217,7 +217,7 @@ class PhpWord /** * Get all sections * - * @return \PhpOffice\PhpWord\Element\Section[] + * @return Section[] */ public function getSections() { diff --git a/src/PhpWord/Reader/AbstractReader.php b/src/PhpWord/Reader/AbstractReader.php index cf43a858..99db598c 100644 --- a/src/PhpWord/Reader/AbstractReader.php +++ b/src/PhpWord/Reader/AbstractReader.php @@ -47,7 +47,7 @@ abstract class AbstractReader implements ReaderInterface * Set read data only * * @param bool $pValue - * @return \PhpOffice\PhpWord\Reader\ReaderInterface + * @return self */ public function setReadDataOnly($pValue = true) { @@ -60,7 +60,7 @@ abstract class AbstractReader implements ReaderInterface * * @param string $pFilename * @return resource - * @throws \PhpOffice\PhpWord\Exception\Exception + * @throws Exception */ protected function openFile($pFilename) { diff --git a/src/PhpWord/Reader/Word2007.php b/src/PhpWord/Reader/Word2007.php index afd68eda..422258f2 100644 --- a/src/PhpWord/Reader/Word2007.php +++ b/src/PhpWord/Reader/Word2007.php @@ -11,6 +11,7 @@ namespace PhpOffice\PhpWord\Reader; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Footnote; use PhpOffice\PhpWord\DocumentProperties; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Shared\XMLReader; @@ -18,6 +19,10 @@ use PhpOffice\PhpWord\Element\Section; /** * Reader for Word2007 + * + * @since 0.9.2 + * @todo title, list, watermark, checkbox, toc + * @todo Partly done: image, object */ class Word2007 extends AbstractReader implements ReaderInterface { @@ -33,239 +38,154 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @var array */ - private $partRels = array('document' => array(), 'footnotes' => array()); - - /** - * Current active part document|footnotes|headerx|footerx - * - * @var string - */ - private $activePart = 'document'; - - /** - * Can the current ReaderInterface read the file? - * - * @param string $fileName - * @return bool - * @throws Exception - */ - public function canRead($fileName) - { - // Check if file exists - if (!file_exists($fileName)) { - throw new Exception("Could not open {$fileName} for reading! File does not exist."); - } - - $return = false; - // Load file - $zipClass = Settings::getZipClass(); - $zip = new $zipClass(); - if ($zip->open($fileName) === true) { - // check if it is an OOXML archive - $rels = simplexml_load_string($this->getFromZipArchive($zip, "_rels/.rels")); - if ($rels !== false) { - foreach ($rels->Relationship as $rel) { - switch ($rel["Type"]) { - case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument": - if (basename($rel["Target"]) == 'document.xml') { - $return = true; - } - break; - - } - } - } - $zip->close(); - } - - return $return; - } - - /** - * Get zip content - * - * @param mixed $archive - * @param string $fileName - * @param bool $removeNamespace - * @return string - */ - public function getFromZipArchive($archive, $fileName = '', $removeNamespace = false) - { - // Root-relative paths - // if (strpos($fileName, '//') !== false) { - // $fileName = substr($fileName, strpos($fileName, '//') + 1); - // } - // $fileName = realpath($fileName); - - // Apache POI fixes - $contents = $archive->getFromName($fileName); - if ($contents === false) { - $contents = $archive->getFromName(substr($fileName, 1)); - } - - // Remove namespaces from elements and attributes name - if ($removeNamespace) { - $contents = preg_replace('~( array(), 'document' => array()); /** * Loads PhpWord from file * - * @param string $fileName + * @param string $filename * @return PhpWord|null */ - public function load($fileName) + public function load($filename) { - // Check if file exists and can be read - if (!$this->canRead($fileName)) { - return null; - } - - // Initialisations $this->phpWord = new PhpWord(); - $zipClass = Settings::getZipClass(); - $zip = new $zipClass(); - $zip->open($fileName); - // Read document relationships - $this->readPartRels($fileName, 'document'); + $this->readRelationships($filename); - // Read properties and documents - $rels = simplexml_load_string($this->getFromZipArchive($zip, "_rels/.rels")); - foreach ($rels->Relationship as $rel) { - switch ($rel["Type"]) { - // Core properties - case "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties": - $xmlCore = simplexml_load_string($this->getFromZipArchive($zip, "{$rel['Target']}")); - if (is_object($xmlCore)) { - $xmlCore->registerXPathNamespace("dc", "http://purl.org/dc/elements/1.1/"); - $xmlCore->registerXPathNamespace("dcterms", "http://purl.org/dc/terms/"); - $xmlCore->registerXPathNamespace("cp", "http://schemas.openxmlformats.org/package/2006/metadata/core-properties"); - $docProps = $this->phpWord->getDocumentProperties(); - $docProps->setCreator((string)self::arrayItem($xmlCore->xpath("dc:creator"))); - $docProps->setLastModifiedBy((string)self::arrayItem($xmlCore->xpath("cp:lastModifiedBy"))); - $docProps->setCreated(strtotime(self::arrayItem($xmlCore->xpath("dcterms:created")))); - $docProps->setModified(strtotime(self::arrayItem($xmlCore->xpath("dcterms:modified")))); - $docProps->setTitle((string)self::arrayItem($xmlCore->xpath("dc:title"))); - $docProps->setDescription((string)self::arrayItem($xmlCore->xpath("dc:description"))); - $docProps->setSubject((string)self::arrayItem($xmlCore->xpath("dc:subject"))); - $docProps->setKeywords((string)self::arrayItem($xmlCore->xpath("cp:keywords"))); - $docProps->setCategory((string)self::arrayItem($xmlCore->xpath("cp:category"))); - } + // Read main relationship + foreach ($this->rels['main'] as $rId => $rel) { + switch ($rel['type']) { + + case 'officeDocument': + $this->readDocument($filename, $rel['target']); break; - // Extended properties - case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties": - $xmlCore = simplexml_load_string($this->getFromZipArchive($zip, "{$rel['Target']}")); - if (is_object($xmlCore)) { - $docProps = $this->phpWord->getDocumentProperties(); - if (isset($xmlCore->Company)) { - $docProps->setCompany((string)$xmlCore->Company); - } - if (isset($xmlCore->Manager)) { - $docProps->setManager((string)$xmlCore->Manager); - } - } + + case 'core-properties': + $mapping = array( + 'dc:creator' => 'setCreator', + 'dc:title' => 'setTitle', + 'dc:description' => 'setDescription', + 'dc:subject' => 'setSubject', + 'cp:keywords' => 'setKeywords', + 'cp:category' => 'setCategory', + 'cp:lastModifiedBy' => 'setLastModifiedBy', + 'dcterms:created' => 'setCreated', + 'dcterms:modified' => 'setModified', + ); + $callbacks = array('dcterms:created' => 'strtotime', 'dcterms:modified' => 'strtotime'); + $this->readDocProps($filename, $rel['target'], $mapping, $callbacks); break; - // Custom properties - case "http://schemas.openxmlformats.org/officeDocument/2006/relationships/custom-properties": - $xmlCore = simplexml_load_string($this->getFromZipArchive($zip, "{$rel['Target']}")); - if (is_object($xmlCore)) { - $docProps = $this->phpWord->getDocumentProperties(); - foreach ($xmlCore as $xmlProperty) { - $cellDataOfficeAttributes = $xmlProperty->attributes(); - if (isset($cellDataOfficeAttributes['name'])) { - $propertyName = (string)$cellDataOfficeAttributes['name']; - $cellDataOfficeChildren = $xmlProperty->children("http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"); - $attributeType = $cellDataOfficeChildren->getName(); - $attributeValue = (string)$cellDataOfficeChildren->{$attributeType}; - $attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType); - $attributeType = DocumentProperties::convertPropertyType($attributeType); - $docProps->setCustomProperty($propertyName, $attributeValue, $attributeType); - } - } - } + + case 'extended-properties': + $mapping = array('Company' => 'setCompany', 'Manager' => 'setManager'); + $this->readDocProps($filename, $rel['target'], $mapping); + break; + + case 'custom-properties': + $this->readDocPropsCustom($filename, $rel['target']); break; } } - // Read document - $this->readDocument($fileName, 'word/document.xml'); - // Read document relationships - foreach ($this->partRels['document'] as $rId => $rel) { - if ($rel['type'] == 'styles') { - $this->readStyles($fileName, 'word/' . $rel['target']); + foreach ($this->rels['document'] as $rId => $rel) { + switch ($rel['type']) { + + case 'styles': + $this->readStyles($filename, $rel['target']); + break; + + case 'footnotes': + $this->readFootnotes($filename, $rel['target']); + break; } } - $zip->close(); return $this->phpWord; } /** - * Read _rels/$partName.xml.rels + * Read all relationship files * - * @param string $fileName - * @param string $partName document|footnotes|headerx|footerx + * @param string $filename */ - private function readPartRels($fileName, $partName) + private function readRelationships($filename) + { + // _rels/.rels + $this->rels['main'] = $this->getRels($filename, '_rels/.rels'); + + // word/_rels/*.xml.rels + $wordRelsPath = 'word/_rels/'; + $zipClass = Settings::getZipClass(); + $zip = new $zipClass(); + if ($zip->open($filename) === true) { + for ($i = 0; $i < $zip->numFiles; $i++) { + $xmlFile = $zip->getNameIndex($i); + if ((substr($xmlFile, 0, strlen($wordRelsPath))) == $wordRelsPath) { + $docPart = str_replace('.xml.rels', '', str_replace($wordRelsPath, '', $xmlFile)); + $this->rels[$docPart] = $this->getRels($filename, $xmlFile, 'word/'); + } + } + $zip->close(); + } + } + + /** + * Read core and extended document properties + * + * @param string $filename + * @param string $xmlFile + * @param array $mapping + * @param array $callbacks + */ + private function readDocProps($filename, $xmlFile, $mapping, $callbacks = array()) { - $relPrefix = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/'; $xmlReader = new XMLReader(); - $response = $xmlReader->getDomFromZip($fileName, "word/_rels/{$partName}.xml.rels"); - if ($response) { - $rels = $xmlReader->getElements('*'); - foreach ($rels as $rel) { - $this->partRels[$partName][$rel->getAttribute('Id')] = array( - 'type' => str_replace($relPrefix, '', $rel->getAttribute('Type')), - 'target' => $rel->getAttribute('Target'), - ); + $xmlReader->getDomFromZip($filename, $xmlFile); + $docProps = $this->phpWord->getDocumentProperties(); + + $nodes = $xmlReader->getElements('*'); + if ($nodes->length > 0) { + foreach ($nodes as $node) { + $nodeName = $node->nodeName; + if (!array_key_exists($nodeName, $mapping)) { + continue; + } + $method = $mapping[$nodeName]; + $value = $node->nodeValue == '' ? null : $node->nodeValue; + if (array_key_exists($nodeName, $callbacks)) { + $value = $callbacks[$nodeName]($value); + } + if (method_exists($docProps, $method)) { + $docProps->$method($value); + } } } } /** - * Read styles.xml + * Read custom document properties * - * @param string $fileName + * @param string $filename * @param string $xmlFile */ - private function readStyles($fileName, $xmlFile) + private function readDocPropsCustom($filename, $xmlFile) { $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($fileName, $xmlFile); + $xmlReader->getDomFromZip($filename, $xmlFile); + $docProps = $this->phpWord->getDocumentProperties(); - $nodes = $xmlReader->getElements('w:style'); + $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { - $type = $xmlReader->getAttribute($node, 'w:type'); - $name = $xmlReader->getAttribute($node, 'w:styleId'); - if (is_null($name)) { - $name = $xmlReader->getAttribute('w:name', 'w:val', $node); - } - $default = ($xmlReader->getAttribute($node, 'w:default') == 1); - if ($type == 'paragraph') { - $pStyle = $this->readWpPr($xmlReader, $node); - $fStyle = $this->readWrPr($xmlReader, $node); - if (empty($fStyle)) { - $this->phpWord->addParagraphStyle($name, $pStyle); - } else { - $this->phpWord->addFontStyle($name, $fStyle, $pStyle); - } - } elseif ($type == 'character') { - $fStyle = $this->readWrPr($xmlReader, $node); - if (!empty($fStyle)) { - $this->phpWord->addFontStyle($name, $fStyle); - } - } elseif ($type == 'table') { - $tStyle = $this->readWtblPr($xmlReader, $node); - if (!empty($tStyle)) { - $this->phpWord->addTableStyle($name, $tStyle); - } - } + $nodeName = $node->nodeName; + $propertyName = $xmlReader->getAttribute('name', $node); + $attributeNode = $xmlReader->getElement('*', $node); + $attributeType = $attributeNode->nodeName; + $attributeValue = $attributeNode->nodeValue; + $attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType); + $attributeType = DocumentProperties::convertPropertyType($attributeType); + $docProps->setCustomProperty($propertyName, $attributeValue, $attributeType); } } } @@ -273,38 +193,89 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read document.xml * - * @param string $fileName + * @param string $filename * @param string $xmlFile */ - private function readDocument($fileName, $xmlFile) + private function readDocument($filename, $xmlFile) { $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($fileName, $xmlFile); + $xmlReader->getDomFromZip($filename, $xmlFile); $nodes = $xmlReader->getElements('w:body/*'); if ($nodes->length > 0) { $section = $this->phpWord->addSection(); foreach ($nodes as $node) { - if ($node->nodeName == 'w:p') { // Paragraph - if ($xmlReader->getAttribute('w:r/w:br', 'w:type', $node) == 'page') { - $section->addPageBreak(); // PageBreak - } else { - $this->readWp($xmlReader, $node, $section); - } - // Section properties - if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) { - $settingsNode = $xmlReader->getElement('w:pPr/w:sectPr', $node); - $settings = $this->readWsectPr($xmlReader, $settingsNode); + switch ($node->nodeName) { + case 'w:p': // Paragraph + if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') { + $section->addPageBreak(); // PageBreak + } else { + $this->readParagraph($xmlReader, $node, $section, 'document'); + } + // Section properties + if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) { + $settingsNode = $xmlReader->getElement('w:pPr/w:sectPr', $node); + $settings = $this->readSectionStyle($xmlReader, $settingsNode); + $section->setSettings($settings); + $this->readHeaderFooter($filename, $settings, $section); + $section = $this->phpWord->addSection(); + } + break; + case 'w:tbl': // Table + $this->readTable($xmlReader, $node, $section, 'document'); + break; + case 'w:sectPr': // Last section + $settings = $this->readSectionStyle($xmlReader, $node); $section->setSettings($settings); - $this->readHeaderFooter($fileName, $settings, $section); - $section = $this->phpWord->addSection(); - } - } elseif ($node->nodeName == 'w:tbl') { // Table - $this->readWtbl($xmlReader, $node, $section); - } elseif ($node->nodeName == 'w:sectPr') { // Last section - $settings = $this->readWsectPr($xmlReader, $node); - $section->setSettings($settings); - $this->readHeaderFooter($fileName, $settings, $section); + $this->readHeaderFooter($filename, $settings, $section); + break; + } + } + } + } + + /** + * Read styles.xml + * + * @param string $filename + * @param string $xmlFile + */ + private function readStyles($filename, $xmlFile) + { + $xmlReader = new XMLReader(); + $xmlReader->getDomFromZip($filename, $xmlFile); + + $nodes = $xmlReader->getElements('w:style'); + if ($nodes->length > 0) { + foreach ($nodes as $node) { + $type = $xmlReader->getAttribute('w:type', $node); + $name = $xmlReader->getAttribute('w:styleId', $node); + if (is_null($name)) { + $name = $xmlReader->getAttribute('w:val', $node, 'w:name'); + } + $default = ($xmlReader->getAttribute('w:default', $node) == 1); + switch ($type) { + case 'paragraph': + $pStyle = $this->readParagraphStyle($xmlReader, $node); + $fStyle = $this->readFontStyle($xmlReader, $node); + if (empty($fStyle)) { + $this->phpWord->addParagraphStyle($name, $pStyle); + } else { + $this->phpWord->addFontStyle($name, $fStyle, $pStyle); + } + break; + case 'character': + $fStyle = $this->readFontStyle($xmlReader, $node); + if (!empty($fStyle)) { + $this->phpWord->addFontStyle($name, $fStyle); + } + break; + case 'table': + $tStyle = $this->readTableStyle($xmlReader, $node); + if (!empty($tStyle)) { + $this->phpWord->addTableStyle($name, $tStyle); + } + break; } } } @@ -313,86 +284,132 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read header footer * - * @param string $fileName + * @param string $filename * @param array $settings * @param Section $section */ - private function readHeaderFooter($fileName, $settings, &$section) + private function readHeaderFooter($filename, $settings, &$section) { - if (is_array($settings) && array_key_exists('headerFooter', $settings)) { - foreach ($settings['headerFooter'] as $rId => $headerFooter) { - if (array_key_exists($rId, $this->partRels['document'])) { - $target = $this->partRels['document'][$rId]['target']; - $xmlFile = 'word/' . $target; - $method = 'add' . $headerFooter['method']; - $type = $headerFooter['type']; - $object = $section->$method($type); - - $this->activePart = str_replace('.xml', '', $target); - $this->readPartRels($fileName, $this->activePart); + if (is_array($settings) && array_key_exists('hf', $settings)) { + foreach ($settings['hf'] as $rId => $hfSetting) { + if (array_key_exists($rId, $this->rels['document'])) { + list($hfType, $xmlFile, $docPart) = array_values($this->rels['document'][$rId]); + $method = "add{$hfType}"; + $hfObject = $section->$method($hfSetting['type']); + // Read header/footer content $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($fileName, $xmlFile); + $xmlReader->getDomFromZip($filename, $xmlFile); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { - if ($node->nodeName == 'w:p') { // Paragraph - $this->readWp($xmlReader, $node, $object); - } elseif ($node->nodeName == 'w:tbl') { // Table - $this->readWtbl($xmlReader, $node, $object); + switch ($node->nodeName) { + + case 'w:p': // Paragraph + $this->readParagraph($xmlReader, $node, $hfObject, $docPart); + break; + + case 'w:tbl': // Table + $this->readTable($xmlReader, $node, $hfObject, $docPart); + break; } } } } } } - $this->activePart = 'document'; + } + + /** + * Read footnotes.xml + * + * @param string $filename + * @param string $xmlFile + */ + private function readFootnotes($filename, $xmlFile) + { + $footnotes = Footnote::getElements(); + + $xmlReader = new XMLReader(); + $xmlReader->getDomFromZip($filename, $xmlFile); + $nodes = $xmlReader->getElements('*'); + if ($nodes->length > 0) { + foreach ($nodes as $node) { + $id = $xmlReader->getAttribute('w:id', $node); + $type = $xmlReader->getAttribute('w:type', $node); + + // Avoid w:type "separator" and "continuationSeparator" + // Only look for without w:type attribute + if (is_null($type) && array_key_exists($id, $footnotes)) { + $footnote = $footnotes[$id]; + $pNodes = $xmlReader->getElements('w:p/*', $node); + foreach ($pNodes as $pNode) { + $this->readRun($xmlReader, $pNode, $footnote, 'footnotes'); + } + Footnote::setElement($id, $footnote); + } + } + } } /** * Read w:p * - * @param mixed $container + * @param mixed $parent + * @param string $docPart + * * @todo Get font style for preserve text */ - private function readWp(XMLReader $xmlReader, \DOMNode $domNode, &$container) + private function readParagraph(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart) { // Paragraph style $pStyle = null; if ($xmlReader->elementExists('w:pPr', $domNode)) { - $pStyle = $this->readWpPr($xmlReader, $domNode); + $pStyle = $this->readParagraphStyle($xmlReader, $domNode); } - // Content - if ($xmlReader->elementExists('w:r/w:instrText', $domNode)) { // Preserve text + // PreserveText + if ($xmlReader->elementExists('w:r/w:instrText', $domNode)) { + $ignoreText = false; $textContent = ''; - $fStyle = $this->readWrPr($xmlReader, $domNode); + $fStyle = $this->readFontStyle($xmlReader, $domNode); $nodes = $xmlReader->getElements('w:r', $domNode); foreach ($nodes as $node) { $instrText = $xmlReader->getValue('w:instrText', $node); + if ($xmlReader->elementExists('w:fldChar', $node)) { + $fldCharType = $xmlReader->getAttribute('w:fldCharType', $node, 'w:fldChar'); + if ($fldCharType == 'begin') { + $ignoreText = true; + } elseif ($fldCharType == 'end') { + $ignoreText = false; + } + } if (!is_null($instrText)) { $textContent .= '{' . $instrText . '}'; } else { - $textContent .= $xmlReader->getValue('w:t', $node); + if ($ignoreText === false) { + $textContent .= $xmlReader->getValue('w:t', $node); + } } } - $container->addPreserveText($textContent, $fStyle, $pStyle); - } else { // Text and TextRun + $parent->addPreserveText($textContent, $fStyle, $pStyle); + + // Text and TextRun + } else { $runCount = $xmlReader->countElements('w:r', $domNode); $linkCount = $xmlReader->countElements('w:hyperlink', $domNode); $runLinkCount = $runCount + $linkCount; if ($runLinkCount == 0) { - $container->addTextBreak(null, $pStyle); + $parent->addTextBreak(null, $pStyle); } else { if ($runLinkCount > 1) { - $textContainer = &$container->addTextRun($pStyle); - $pStyle = null; + $textContainer = &$parent->addTextRun($pStyle); } else { - $textContainer = &$container; + $textContainer = &$parent; } $nodes = $xmlReader->getElements('*', $domNode); foreach ($nodes as $node) { - $this->readWr($xmlReader, $node, $textContainer, $pStyle); + $this->readRun($xmlReader, $node, $textContainer, $docPart, $pStyle); } } } @@ -401,52 +418,83 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read w:r * - * @param mixed $container + * @param mixed $parent + * @param string $docPart * @param mixed $pStyle + * + * @todo Footnote paragraph style */ - private function readWr(XMLReader $xmlReader, \DOMNode $domNode, &$container, $pStyle = null) + private function readRun(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart, $pStyle = null) { if (!in_array($domNode->nodeName, array('w:r', 'w:hyperlink'))) { return; } - $fStyle = $this->readWrPr($xmlReader, $domNode); + $fStyle = $this->readFontStyle($xmlReader, $domNode); + + // Link if ($domNode->nodeName == 'w:hyperlink') { - $rId = $xmlReader->getAttribute($domNode, 'r:id'); + $rId = $xmlReader->getAttribute('r:id', $domNode); $textContent = $xmlReader->getValue('w:r/w:t', $domNode); - if (array_key_exists($this->activePart, $this->partRels)) { - if (array_key_exists($rId, $this->partRels[$this->activePart])) { - $linkSource = $this->partRels[$this->activePart][$rId]['target']; - } + $target = $this->getMediaTarget($docPart, $rId); + if (!is_null($target)) { + $parent->addLink($target, $textContent, $fStyle, $pStyle); } - $container->addLink($linkSource, $textContent, $fStyle, $pStyle); } else { - $textContent = $xmlReader->getValue('w:t', $domNode); - $container->addText($textContent, $fStyle, $pStyle); + // Footnote + if ($xmlReader->elementExists('w:footnoteReference', $domNode)) { + $parent->addFootnote(); + + // Image + } elseif ($xmlReader->elementExists('w:pict', $domNode)) { + $rId = $xmlReader->getAttribute('r:id', $domNode, 'w:pict/v:shape/v:imagedata'); + $target = $this->getMediaTarget($docPart, $rId); + if (!is_null($target)) { + $textContent = ""; + $parent->addText($textContent, $fStyle, $pStyle); + } + + // Object + } elseif ($xmlReader->elementExists('w:object', $domNode)) { + $rId = $xmlReader->getAttribute('r:id', $domNode, 'w:object/o:OLEObject'); + $rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata'); + $target = $this->getMediaTarget($docPart, $rId); + if (!is_null($target)) { + $textContent = ""; + $parent->addText($textContent, $fStyle, $pStyle); + } + + // TextRun + } else { + $textContent = $xmlReader->getValue('w:t', $domNode); + $parent->addText($textContent, $fStyle, $pStyle); + } } } /** * Read w:tbl * - * @param mixed $container + * @param mixed $parent + * @param string $docPart */ - private function readWtbl(XMLReader $xmlReader, \DOMNode $domNode, &$container) + private function readTable(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart) { // Table style $tblStyle = null; if ($xmlReader->elementExists('w:tblPr', $domNode)) { - $tblStyle = $this->readWtblPr($xmlReader, $domNode); + $tblStyle = $this->readTableStyle($xmlReader, $domNode); } - $table = $container->addTable($tblStyle); + $table = $parent->addTable($tblStyle); $tblNodes = $xmlReader->getElements('*', $domNode); foreach ($tblNodes as $tblNode) { $tblNodeName = $tblNode->nodeName; if ($tblNode->nodeName == 'w:tblGrid') { // Column // @todo Do something with table columns + } elseif ($tblNode->nodeName == 'w:tr') { // Row - $rowHeight = $xmlReader->getAttribute('w:trPr/w:trHeight', 'w:val', $tblNode); - $rowHRule = $xmlReader->getAttribute('w:trPr/w:trHeight', 'w:hRule', $tblNode); + $rowHeight = $xmlReader->getAttribute('w:val', $tblNode, 'w:trPr/w:trHeight'); + $rowHRule = $xmlReader->getAttribute('w:hRule', $tblNode, 'w:trPr/w:trHeight'); $rowHRule = $rowHRule == 'exact' ? true : false; $rowStyle = array( 'tblHeader' => $xmlReader->elementExists('w:trPr/w:tblHeader', $tblNode), @@ -459,21 +507,19 @@ class Word2007 extends AbstractReader implements ReaderInterface foreach ($rowNodes as $rowNode) { if ($rowNode->nodeName == 'w:trPr') { // Row style // @todo Do something with row style + } elseif ($rowNode->nodeName == 'w:tc') { // Cell - $cellWidth = $xmlReader->getAttribute('w:tcPr/w:tcW', 'w:w', $rowNode); + $cellWidth = $xmlReader->getAttribute('w:w', $rowNode, 'w:tcPr/w:tcW'); $cellStyle = null; if ($xmlReader->elementExists('w:tcPr', $rowNode)) { - $cellStyle = $this->readWtcPr( - $xmlReader, - $xmlReader->getElement('w:tcPr', $rowNode) - ); + $cellStyle = $this->readCellStyle($xmlReader, $xmlReader->getElement('w:tcPr', $rowNode)); } $cell = $row->addCell($cellWidth, $cellStyle); $cellNodes = $xmlReader->getElements('*', $rowNode); foreach ($cellNodes as $cellNode) { if ($cellNode->nodeName == 'w:p') { // Paragraph - $this->readWp($xmlReader, $cellNode, $cell); + $this->readParagraph($xmlReader, $cellNode, $cell, $docPart); } } } @@ -487,7 +533,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return array|null */ - private function readWsectPr(XMLReader $xmlReader, \DOMNode $domNode) + private function readSectionStyle(XMLReader $xmlReader, \DOMNode $domNode) { $ret = null; $mapping = array( @@ -497,34 +543,44 @@ class Word2007 extends AbstractReader implements ReaderInterface ); $nodes = $xmlReader->getElements('*', $domNode); foreach ($nodes as $node) { - $nodeName = $node->nodeName; - if (!array_key_exists($nodeName, $mapping)) { + if (!array_key_exists($node->nodeName, $mapping)) { continue; } - $retKey = $mapping[$nodeName]; - if ($nodeName == 'w:type') { - $ret['breakType'] = $xmlReader->getAttribute($node, 'w:val'); - } elseif ($nodeName == 'w:pgSz') { - $ret['pageSizeW'] = $xmlReader->getAttribute($node, 'w:w'); - $ret['pageSizeH'] = $xmlReader->getAttribute($node, 'w:h'); - $ret['orientation'] = $xmlReader->getAttribute($node, 'w:orient'); - } elseif ($nodeName == 'w:pgMar') { - $ret['topMargin'] = $xmlReader->getAttribute($node, 'w:top'); - $ret['leftMargin'] = $xmlReader->getAttribute($node, 'w:left'); - $ret['bottomMargin'] = $xmlReader->getAttribute($node, 'w:bottom'); - $ret['rightMargin'] = $xmlReader->getAttribute($node, 'w:right'); - $ret['headerHeight'] = $xmlReader->getAttribute($node, 'w:header'); - $ret['footerHeight'] = $xmlReader->getAttribute($node, 'w:footer'); - $ret['gutter'] = $xmlReader->getAttribute($node, 'w:gutter'); - } elseif ($nodeName == 'w:cols') { - $ret['colsNum'] = $xmlReader->getAttribute($node, 'w:num'); - $ret['colsSpace'] = $xmlReader->getAttribute($node, 'w:space'); - } elseif (in_array($nodeName, array('w:headerReference', 'w:footerReference'))) { - $id = $xmlReader->getAttribute($node, 'r:id'); - $ret['headerFooter'][$id] = array( - 'method' => $retKey, - 'type' => $xmlReader->getAttribute($node, 'w:type'), - ); + $property = $mapping[$node->nodeName]; + switch ($node->nodeName) { + case 'w:type': + $ret['breakType'] = $xmlReader->getAttribute('w:val', $node); + break; + + case 'w:pgSz': + $ret['pageSizeW'] = $xmlReader->getAttribute('w:w', $node); + $ret['pageSizeH'] = $xmlReader->getAttribute('w:h', $node); + $ret['orientation'] = $xmlReader->getAttribute('w:orient', $node); + break; + + case 'w:pgMar': + $ret['topMargin'] = $xmlReader->getAttribute('w:top', $node); + $ret['leftMargin'] = $xmlReader->getAttribute('w:left', $node); + $ret['bottomMargin'] = $xmlReader->getAttribute('w:bottom', $node); + $ret['rightMargin'] = $xmlReader->getAttribute('w:right', $node); + $ret['headerHeight'] = $xmlReader->getAttribute('w:header', $node); + $ret['footerHeight'] = $xmlReader->getAttribute('w:footer', $node); + $ret['gutter'] = $xmlReader->getAttribute('w:gutter', $node); + break; + + case 'w:cols': + $ret['colsNum'] = $xmlReader->getAttribute('w:num', $node); + $ret['colsSpace'] = $xmlReader->getAttribute('w:space', $node); + break; + + case 'w:headerReference': + case 'w:footerReference': + $id = $xmlReader->getAttribute('r:id', $node); + $ret['hf'][$id] = array( + 'method' => $property, + 'type' => $xmlReader->getAttribute('w:type', $node), + ); + break; } } @@ -536,46 +592,61 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return string|array|null */ - private function readWpPr(XMLReader $xmlReader, \DOMNode $domNode) + private function readParagraphStyle(XMLReader $xmlReader, \DOMNode $domNode) { - $ret = null; + $style = null; if ($xmlReader->elementExists('w:pPr', $domNode)) { if ($xmlReader->elementExists('w:pPr/w:pStyle', $domNode)) { - $ret = $xmlReader->getAttribute('w:pPr/w:pStyle', 'w:val', $domNode); + $style = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:pStyle'); } else { - $ret = array(); + $style = array(); $mapping = array( - 'w:jc' => 'align', 'w:ind' => 'indent', 'w:spacing' => 'spacing', - 'w:basedOn' => 'basedOn', 'w:next' => 'next', + 'w:ind' => 'indent', 'w:spacing' => 'spacing', + 'w:jc' => 'align', 'w:basedOn' => 'basedOn', 'w:next' => 'next', 'w:widowControl' => 'widowControl', 'w:keepNext' => 'keepNext', 'w:keepLines' => 'keepLines', 'w:pageBreakBefore' => 'pageBreakBefore', ); + $nodes = $xmlReader->getElements('w:pPr/*', $domNode); foreach ($nodes as $node) { - $nodeName = $node->nodeName; - if (!array_key_exists($nodeName, $mapping)) { + if (!array_key_exists($node->nodeName, $mapping)) { continue; } - $retKey = $mapping[$nodeName]; - if ($nodeName == 'w:ind') { - $ret['indent'] = $xmlReader->getAttribute($node, 'w:left'); - $ret['hanging'] = $xmlReader->getAttribute($node, 'w:hanging'); - } elseif ($nodeName == 'w:spacing') { - $ret['spaceAfter'] = $xmlReader->getAttribute($node, 'w:after'); - $ret['spaceBefore'] = $xmlReader->getAttribute($node, 'w:before'); - $ret['line'] = $xmlReader->getAttribute($node, 'w:line'); - } elseif (in_array($nodeName, array('w:keepNext', 'w:keepLines', 'w:pageBreakBefore'))) { - $ret[$retKey] = true; - } elseif (in_array($nodeName, array('w:widowControl'))) { - $ret[$retKey] = false; - } elseif (in_array($nodeName, array('w:jc', 'w:basedOn', 'w:next'))) { - $ret[$retKey] = $xmlReader->getAttribute($node, 'w:val'); + $property = $mapping[$node->nodeName]; + switch ($node->nodeName) { + case 'w:ind': + $style['indent'] = $xmlReader->getAttribute('w:left', $node); + $style['hanging'] = $xmlReader->getAttribute('w:hanging', $node); + break; + + case 'w:spacing': + $style['spaceAfter'] = $xmlReader->getAttribute('w:after', $node); + $style['spaceBefore'] = $xmlReader->getAttribute('w:before', $node); + // Commented. Need to adjust the number when return value is null + // $style['spacing'] = $xmlReader->getAttribute('w:line', $node); + break; + + case 'w:keepNext': + case 'w:keepLines': + case 'w:pageBreakBefore': + $style[$property] = true; + break; + + case 'w:widowControl': + $style[$property] = false; + break; + + case 'w:jc': + case 'w:basedOn': + case 'w:next': + $style[$property] = $xmlReader->getAttribute('w:val', $node); + break; } } } } - return $ret; + return $style; } /** @@ -583,50 +654,68 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return string|array|null */ - private function readWrPr(XMLReader $xmlReader, \DOMNode $domNode) + private function readFontStyle(XMLReader $xmlReader, \DOMNode $domNode) { - $ret = null; + $style = null; + // Hyperlink has an extra w:r child + if ($domNode->nodeName == 'w:hyperlink') { + $domNode = $xmlReader->getElement('w:r', $domNode); + } if ($xmlReader->elementExists('w:rPr', $domNode)) { if ($xmlReader->elementExists('w:rPr/w:rStyle', $domNode)) { - $ret = $xmlReader->getAttribute('w:rPr/w:rStyle', 'w:val', $domNode); + $style = $xmlReader->getAttribute('w:val', $domNode, 'w:rPr/w:rStyle'); } else { - $ret = array(); + $style = array(); $mapping = array( 'w:b' => 'bold', 'w:i' => 'italic', 'w:color' => 'color', 'w:strike' => 'strikethrough', 'w:u' => 'underline', 'w:highlight' => 'fgColor', 'w:sz' => 'size', 'w:rFonts' => 'name', 'w:vertAlign' => 'superScript', ); + $nodes = $xmlReader->getElements('w:rPr/*', $domNode); foreach ($nodes as $node) { - $nodeName = $node->nodeName; - if (!array_key_exists($nodeName, $mapping)) { + if (!array_key_exists($node->nodeName, $mapping)) { continue; } - $retKey = $mapping[$nodeName]; - if ($nodeName == 'w:rFonts') { - $ret['name'] = $xmlReader->getAttribute($node, 'w:ascii'); - $ret['hint'] = $xmlReader->getAttribute($node, 'w:hint'); - } elseif (in_array($nodeName, array('w:b', 'w:i', 'w:strike'))) { - $ret[$retKey] = true; - } elseif (in_array($nodeName, array('w:u', 'w:highlight', 'w:color'))) { - $ret[$retKey] = $xmlReader->getAttribute($node, 'w:val'); - } elseif ($nodeName == 'w:sz') { - $ret[$retKey] = $xmlReader->getAttribute($node, 'w:val') / 2; - } elseif ($nodeName == 'w:vertAlign') { - $ret[$retKey] = $xmlReader->getAttribute($node, 'w:val'); - if ($ret[$retKey] == 'superscript') { - $ret['superScript'] = true; - } else { - $ret['superScript'] = false; - $ret['subScript'] = true; - } + $property = $mapping[$node->nodeName]; + switch ($node->nodeName) { + case 'w:rFonts': + $style['name'] = $xmlReader->getAttribute('w:ascii', $node); + $style['hint'] = $xmlReader->getAttribute('w:hint', $node); + break; + + case 'w:b': + case 'w:i': + case 'w:strike': + $style[$property] = true; + break; + + case 'w:u': + case 'w:highlight': + case 'w:color': + $style[$property] = $xmlReader->getAttribute('w:val', $node); + break; + + case 'w:sz': + $style[$property] = $xmlReader->getAttribute('w:val', $node) / 2; + break; + + case 'w:vertAlign': + $style[$property] = $xmlReader->getAttribute('w:val', $node); + if ($style[$property] == 'superscript') { + $style['superScript'] = true; + } else { + $style['superScript'] = false; + $style['subScript'] = true; + } + break; } } } } - return $ret; + return $style; } /** * Read w:tblPr @@ -634,44 +723,49 @@ class Word2007 extends AbstractReader implements ReaderInterface * @return string|array|null * @todo Capture w:tblStylePr w:type="firstRow" */ - private function readWtblPr(XMLReader $xmlReader, \DOMNode $domNode) + private function readTableStyle(XMLReader $xmlReader, \DOMNode $domNode) { - $ret = null; + $style = null; $margins = array('top', 'left', 'bottom', 'right'); $borders = $margins + array('insideH', 'insideV'); if ($xmlReader->elementExists('w:tblPr', $domNode)) { if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) { - $ret = $xmlReader->getAttribute('w:tblPr/w:tblStyle', 'w:val', $domNode); + $style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle'); } else { - $ret = array(); + $style = array(); $mapping = array( - 'w:tblCellMar' => 'cellMargin', 'w:tblBorders' => 'border', + 'w:tblCellMar' => 'cellMargin', + 'w:tblBorders' => 'border', ); + $nodes = $xmlReader->getElements('w:tblPr/*', $domNode); foreach ($nodes as $node) { - $nodeName = $node->nodeName; - if (!array_key_exists($nodeName, $mapping)) { + if (!array_key_exists($node->nodeName, $mapping)) { continue; } - $retKey = $mapping[$nodeName]; - if ($nodeName == 'w:tblCellMar') { - foreach ($margins as $side) { - $ucfirstSide = ucfirst($side); - $ret["cellMargin$ucfirstSide"] = $xmlReader->getAttribute("w:$side", 'w:w', $node); - } - } elseif ($nodeName == 'w:tblBorders') { - foreach ($borders as $side) { - $ucfirstSide = ucfirst($side); - $ret["border{$ucfirstSide}Size"] = $xmlReader->getAttribute("w:$side", 'w:sz', $node); - $ret["border{$ucfirstSide}Color"] = $xmlReader->getAttribute("w:$side", 'w:color', $node); - } + $property = $mapping[$node->nodeName]; + switch ($node->nodeName) { + case 'w:tblCellMar': + foreach ($margins as $side) { + $ucfSide = ucfirst($side); + $style["cellMargin$ucfSide"] = $xmlReader->getAttribute('w:w', $node, "w:$side"); + } + break; + + case 'w:tblBorders': + foreach ($borders as $side) { + $ucfSide = ucfirst($side); + $style["border{$ucfSide}Size"] = $xmlReader->getAttribute('w:sz', $node, "w:$side"); + $style["border{$ucfSide}Color"] = $xmlReader->getAttribute('w:color', $node, "w:$side"); + } + break; } } } } - return $ret; + return $style; } /** @@ -679,9 +773,9 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return array|null */ - private function readWtcPr(XMLReader $xmlReader, \DOMNode $domNode) + private function readCellStyle(XMLReader $xmlReader, \DOMNode $domNode) { - $ret = null; + $style = null; $mapping = array( 'w:shd' => 'bgColor', 'w:vAlign' => 'valign', 'w:textDirection' => 'textDirection', @@ -689,30 +783,81 @@ class Word2007 extends AbstractReader implements ReaderInterface ); $nodes = $xmlReader->getElements('*', $domNode); foreach ($nodes as $node) { - $nodeName = $node->nodeName; - if (!array_key_exists($nodeName, $mapping)) { + if (!array_key_exists($node->nodeName, $mapping)) { continue; } - $retKey = $mapping[$nodeName]; - if ($nodeName == 'w:shd') { - $ret['bgColor'] = $xmlReader->getAttribute($node, 'w:fill'); - } else { - $ret[$retKey] = $xmlReader->getAttribute($node, 'w:val'); + $property = $mapping[$node->nodeName]; + switch ($node->nodeName) { + case 'w:shd': + $style['bgColor'] = $xmlReader->getAttribute('w:fill', $node); + break; + + default: + $style[$property] = $xmlReader->getAttribute('w:val', $node); + break; } } - return $ret; + return $style; } /** - * Return item of array + * Get relationship array * - * @param array $array - * @param integer $key - * @return string + * @param string $filename + * @param string $xmlFile + * @param string $targetPrefix + * @return array */ - private static function arrayItem($array, $key = 0) + private function getRels($filename, $xmlFile, $targetPrefix = '') { - return (isset($array[$key]) ? $array[$key] : null); + $metaPrefix = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/'; + $officePrefix = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/'; + + $rels = array(); + + $xmlReader = new XMLReader(); + $xmlReader->getDomFromZip($filename, $xmlFile); + $nodes = $xmlReader->getElements('*'); + foreach ($nodes as $node) { + $rId = $xmlReader->getAttribute('Id', $node); + $type = $xmlReader->getAttribute('Type', $node); + $target = $xmlReader->getAttribute('Target', $node); + + // Remove URL prefixes from $type to make it easier to read + $type = str_replace($metaPrefix, '', $type); + $type = str_replace($officePrefix, '', $type); + $docPart = str_replace('.xml', '', $target); + + // Do not add prefix to link source + if (!in_array($type, array('hyperlink'))) { + $target = $targetPrefix . $target; + } + + // Push to return array + $rels[$rId] = array('type' => $type, 'target' => $target, 'docPart' => $docPart); + } + ksort($rels); + + return $rels; + } + + /** + * Returns the target of image, object, or link as stored in ::readMainRels + * + * @param string $docPart + * @param string $rId + * @return string|null + */ + private function getMediaTarget($docPart, $rId) + { + $target = null; + if (array_key_exists($docPart, $this->rels)) { + if (array_key_exists($rId, $this->rels[$docPart])) { + $target = $this->rels[$docPart][$rId]['target']; + } + } + + return $target; } } diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php index 15ed9da3..0f6ce3f0 100644 --- a/src/PhpWord/Shared/XMLReader.php +++ b/src/PhpWord/Shared/XMLReader.php @@ -38,7 +38,7 @@ class XMLReader * * @param string $zipFile * @param string $xmlFile - * @return \DOMDocument + * @return \DOMDocument|false */ public function getDomFromZip($zipFile, $xmlFile) { @@ -54,6 +54,7 @@ class XMLReader } $contents = $zip->getFromName($xmlFile); $zip->close(); + if ($contents === false) { return false; } else { @@ -69,7 +70,7 @@ class XMLReader * @param string $path * @return \DOMNodeList */ - public function getElements($path, \DOMNode $context = null) + public function getElements($path, \DOMNode $contextNode = null) { if ($this->dom === null) { return array(); @@ -78,42 +79,42 @@ class XMLReader $this->xpath = new \DOMXpath($this->dom); } - return $this->xpath->query($path, $context); + return $this->xpath->query($path, $contextNode); } /** - * Get elements + * Get element * * @param string $path - * @return \DOMNodeList + * @return \DOMNode|null */ - public function getElement($path, \DOMNode $context = null) + public function getElement($path, \DOMNode $contextNode) { - $elements = $this->getElements($path, $context); + $elements = $this->getElements($path, $contextNode); if ($elements->length > 0) { return $elements->item(0); } else { - return false; + return null; } } /** * Get element attribute * - * @param string|\DOMNode $path * @param string $attribute - * @return null|string + * @param string $path + * @return string|null */ - public function getAttribute($path, $attribute, \DOMNode $context = null) + public function getAttribute($attribute, \DOMElement $contextNode, $path = null) { - if ($path instanceof \DOMNode) { - $return = $path->getAttribute($attribute); + if (is_null($path)) { + $return = $contextNode->getAttribute($attribute); } else { - $elements = $this->getElements($path, $context); + $elements = $this->getElements($path, $contextNode); if ($elements->length > 0) { $return = $elements->item(0)->getAttribute($attribute); } else { - $return = ''; + $return = null; } } @@ -124,29 +125,28 @@ class XMLReader * Get element value * * @param string $path - * @return null|string + * @return string|null */ - public function getValue($path, \DOMNode $context = null) + public function getValue($path, \DOMNode $contextNode) { - $elements = $this->getElements($path, $context); + $elements = $this->getElements($path, $contextNode); if ($elements->length > 0) { - $return = $elements->item(0)->nodeValue; + return $elements->item(0)->nodeValue; } else { - $return = ''; + return null; } - - return ($return == '') ? null : $return; } /** * Count elements * * @param string $path - * @return \DOMNodeList + * @return integer */ - public function countElements($path, \DOMNode $context = null) + public function countElements($path, \DOMNode $contextNode) { - $elements = $this->getElements($path, $context); + $elements = $this->getElements($path, $contextNode); + return $elements->length; } @@ -156,8 +156,8 @@ class XMLReader * @param string $path * @return \DOMNodeList */ - public function elementExists($path, \DOMNode $context = null) + public function elementExists($path, \DOMNode $contextNode) { - return $this->getElements($path, $context)->length > 0; + return $this->getElements($path, $contextNode)->length > 0; } } diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php index 46d5c036..60b7198c 100644 --- a/src/PhpWord/Shared/ZipArchive.php +++ b/src/PhpWord/Shared/ZipArchive.php @@ -31,6 +31,13 @@ class ZipArchive const OVERWRITE = 'OVERWRITE'; const CREATE = 'CREATE'; + /** + * Number of files (emulate ZipArchive::$numFiles) + * + * @var string + */ + public $numFiles = 0; + /** * Temporary storage directory * @@ -48,13 +55,14 @@ class ZipArchive /** * Open a new zip archive * - * @param string $fileName Filename for the zip archive + * @param string $filename Filename for the zip archive * @return boolean */ - public function open($fileName) + public function open($filename) { $this->tempDir = sys_get_temp_dir(); - $this->zip = new \PclZip($fileName); + $this->zip = new \PclZip($filename); + $this->numFiles = count($this->zip->listContent()); return true; } @@ -138,19 +146,19 @@ class ZipArchive } /** - * Find if given fileName exist in archive (Emulate ZipArchive locateName()) + * Find if given file name exist in archive (Emulate ZipArchive locateName()) * - * @param string $fileName Filename for the file in zip archive + * @param string $filename Filename for the file in zip archive * @return boolean */ - public function locateName($fileName) + public function locateName($filename) { $list = $this->zip->listContent(); $listCount = count($list); $listIndex = -1; for ($i = 0; $i < $listCount; ++$i) { - if (strtolower($list[$i]["filename"]) == strtolower($fileName) || - strtolower($list[$i]["stored_filename"]) == strtolower($fileName)) { + if (strtolower($list[$i]["filename"]) == strtolower($filename) || + strtolower($list[$i]["stored_filename"]) == strtolower($filename)) { $listIndex = $i; break; } @@ -160,12 +168,12 @@ class ZipArchive } /** - * Extract file from archive by given fileName (Emulate ZipArchive getFromName()) + * Extract file from archive by given file name (Emulate ZipArchive getFromName()) * - * @param string $fileName Filename for the file in zip archive + * @param string $filename Filename for the file in zip archive * @return string $contents File string contents */ - public function getFromName($fileName) + public function getFromName($filename) { $list = $this->zip->listContent(); $listCount = count($list); @@ -173,8 +181,8 @@ class ZipArchive $contents = null; for ($i = 0; $i < $listCount; ++$i) { - if (strtolower($list[$i]["filename"]) == strtolower($fileName) || - strtolower($list[$i]["stored_filename"]) == strtolower($fileName)) { + if (strtolower($list[$i]["filename"]) == strtolower($filename) || + strtolower($list[$i]["stored_filename"]) == strtolower($filename)) { $listIndex = $i; break; } @@ -183,11 +191,11 @@ class ZipArchive if ($listIndex != -1) { $extracted = $this->zip->extractByIndex($listIndex, PCLZIP_OPT_EXTRACT_AS_STRING); } else { - $fileName = substr($fileName, 1); + $filename = substr($filename, 1); $listIndex = -1; for ($i = 0; $i < $listCount; ++$i) { - if (strtolower($list[$i]["filename"]) == strtolower($fileName) || - strtolower($list[$i]["stored_filename"]) == strtolower($fileName)) { + if (strtolower($list[$i]["filename"]) == strtolower($filename) || + strtolower($list[$i]["stored_filename"]) == strtolower($filename)) { $listIndex = $i; break; } @@ -200,4 +208,21 @@ class ZipArchive return $contents; } + + /** + * Returns the name of an entry using its index + * + * @param integer $index + * @return string|false + */ + public function getNameIndex($index) + { + $list = $this->zip->listContent(); + $listCount = count($list); + if ($index <= $listCount) { + return $list[$index]['filename']; + } else { + return false; + } + } } diff --git a/src/PhpWord/Style.php b/src/PhpWord/Style.php index ab1159ce..3c2eef09 100755 --- a/src/PhpWord/Style.php +++ b/src/PhpWord/Style.php @@ -14,7 +14,7 @@ use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\Style\Table; /** - * Style + * Style collection */ class Style { @@ -91,7 +91,7 @@ class Style /** * Reset styles */ - public static function reset() + public static function resetStyles() { self::$styles = array(); } @@ -109,7 +109,7 @@ class Style /** * Get all styles * - * @return \PhpOffice\PhpWord\Style\Font[] + * @return Font[] */ public static function getStyles() { diff --git a/src/PhpWord/TOC.php b/src/PhpWord/TOC.php index cb3ad02f..d84bf770 100644 --- a/src/PhpWord/TOC.php +++ b/src/PhpWord/TOC.php @@ -152,9 +152,9 @@ class TOC } /** - * Reset footnotes + * Reset titles */ - public static function reset() + public static function resetTitles() { self::$titles = array(); } diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 5a489542..c89f09ba 100755 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -13,6 +13,7 @@ use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Footnote; use PhpOffice\PhpWord\Media; +use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Writer\Word2007\ContentTypes; use PhpOffice\PhpWord\Writer\Word2007\Rels; use PhpOffice\PhpWord\Writer\Word2007\DocProps; @@ -97,11 +98,11 @@ class Word2007 extends AbstractWriter implements WriterInterface // Add header/footer contents $overrides = array(); - $rID = Media::countElements('section') + 6; // @see Rels::writeDocRels for 6 first elements + $rId = Media::countElements('section') + 6; // @see Rels::writeDocRels for 6 first elements $sections = $this->phpWord->getSections(); foreach ($sections as $section) { - $this->addHeaderFooterContent($section, $objZip, 'header', $rID); - $this->addHeaderFooterContent($section, $objZip, 'footer', $rID); + $this->addHeaderFooterContent($section, $objZip, 'header', $rId); + $this->addHeaderFooterContent($section, $objZip, 'footer', $rId); } // Add footnotes media files, relations, and contents @@ -113,7 +114,7 @@ class Word2007 extends AbstractWriter implements WriterInterface } $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); + $this->docRels[] = array('target' => 'footnotes.xml', 'type' => 'footnotes', 'rID' => ++$rId); } // Write dynamic files @@ -207,12 +208,11 @@ class Word2007 extends AbstractWriter implements WriterInterface /** * Add header/footer content * - * @param \PhpOffice\PhpWord\Element\Section $section * @param mixed $objZip * @param string $elmType - * @param integer $rID + * @param integer $rId */ - private function addHeaderFooterContent(&$section, $objZip, $elmType, &$rID) + private function addHeaderFooterContent(Section &$section, $objZip, $elmType, &$rId) { $getFunction = $elmType == 'header' ? 'getHeaders' : 'getFooters'; $writeFunction = $elmType == 'header' ? 'writeHeader' : 'writeFooter'; @@ -220,11 +220,11 @@ class Word2007 extends AbstractWriter implements WriterInterface $elmObjects = $section->$getFunction(); foreach ($elmObjects as $index => &$elmObject) { $elmCount++; - $elmObject->setRelationId(++$rID); + $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); + $this->docRels[] = array('target' => $elmFile, 'type' => $elmType, 'rID' => $rId); } } } diff --git a/src/PhpWord/Writer/Word2007/Base.php b/src/PhpWord/Writer/Word2007/Base.php index a5f9a5e0..12291694 100644 --- a/src/PhpWord/Writer/Word2007/Base.php +++ b/src/PhpWord/Writer/Word2007/Base.php @@ -93,7 +93,7 @@ class Base extends AbstractWriterPart */ protected function writeLink(XMLWriter $xmlWriter, Link $link, $withoutP = false) { - $rID = $link->getRelationId() + ($link->isInSection() ? 6 : 0); + $rId = $link->getRelationId() + ($link->isInSection() ? 6 : 0); $linkName = $link->getLinkName(); if (is_null($linkName)) { $linkName = $link->getLinkSrc(); @@ -106,7 +106,7 @@ class Base extends AbstractWriterPart $this->writeInlineParagraphStyle($xmlWriter, $styleParagraph); } $xmlWriter->startElement('w:hyperlink'); - $xmlWriter->writeAttribute('r:id', 'rId' . $rID); + $xmlWriter->writeAttribute('r:id', 'rId' . $rId); $xmlWriter->writeAttribute('w:history', '1'); $xmlWriter->startElement('w:r'); $this->writeInlineFontStyle($xmlWriter, $styleFont); diff --git a/tests/PhpWord/Tests/FootnoteTest.php b/tests/PhpWord/Tests/FootnoteTest.php index 47aea9a3..25fd8ae8 100644 --- a/tests/PhpWord/Tests/FootnoteTest.php +++ b/tests/PhpWord/Tests/FootnoteTest.php @@ -32,7 +32,7 @@ class FootnoteTest extends \PHPUnit_Framework_TestCase $this->assertEquals(1, count(Footnote::getFootnoteElements())); $this->assertEquals(1, count(Footnote::getFootnoteLinkElements())); - Footnote::reset(); + Footnote::resetElements(); $this->assertEquals(0, count(Footnote::getFootnoteElements())); } } diff --git a/tests/PhpWord/Tests/MediaTest.php b/tests/PhpWord/Tests/MediaTest.php index 93dfaf87..0b98bfd2 100644 --- a/tests/PhpWord/Tests/MediaTest.php +++ b/tests/PhpWord/Tests/MediaTest.php @@ -95,7 +95,7 @@ class MediaTest extends \PHPUnit_Framework_TestCase $this->assertEquals(2, Media::countElements('footer1')); - Media::reset(); + Media::resetElements(); $this->assertEquals(0, Media::countElements('footer1')); } diff --git a/tests/PhpWord/Tests/StyleTest.php b/tests/PhpWord/Tests/StyleTest.php index b323ece4..bf28fa88 100644 --- a/tests/PhpWord/Tests/StyleTest.php +++ b/tests/PhpWord/Tests/StyleTest.php @@ -44,7 +44,7 @@ class StyleTest extends \PHPUnit_Framework_TestCase } $this->assertNull(Style::getStyle('Unknown')); - Style::reset(); + Style::resetStyles(); $this->assertEquals(0, count(Style::getStyles())); } diff --git a/tests/PhpWord/Tests/TOCTest.php b/tests/PhpWord/Tests/TOCTest.php index 2b9d50e7..7092795a 100644 --- a/tests/PhpWord/Tests/TOCTest.php +++ b/tests/PhpWord/Tests/TOCTest.php @@ -80,7 +80,7 @@ class TOCTest extends \PHPUnit_Framework_TestCase $i++; } - TOC::reset(); + TOC::resetTitles(); $this->assertEquals(0, count($toc->getTitles())); }