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 fe8ec7ac..2143c628 100644 Binary files a/samples/resources/Sample_11_ReadWord2007.docx and b/samples/resources/Sample_11_ReadWord2007.docx differ 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())); }