Word2007 parsing title formatting (#1297)

* Improve Title parsing
- Title should be able to contain TextRun
- Style 'Title' should be treated the same with as Heading
- Add tests for Heading/Title reader

* update the documentation and the changelog
* PHP 7.2 build should not fail anymore
* reduce dependencies versions
* fix parsing of footnotes and endnotes
* add method to remove an element from a section
This commit is contained in:
troosan 2018-03-06 06:34:55 +01:00 committed by GitHub
parent 740e66acf5
commit 30b224b3d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 539 additions and 99 deletions

View File

@ -1,3 +1,8 @@
build:
nodes:
analysis:
tests:
override: [php-scrutinizer-run]
filter:
excluded_paths: [ 'vendor/*', 'tests/*', 'samples/*', 'src/PhpWord/Shared/PCLZip/*' ]

View File

@ -15,12 +15,9 @@ matrix:
include:
- php: 5.6
env: COVERAGE=1
allow_failures:
- php: 7.2
cache:
directories:
- vendor
- $HOME/.composer/cache
- .php-cs.cache
@ -38,7 +35,7 @@ before_script:
- if [ -z "$COVERAGE" ]; then phpenv config-rm xdebug.ini ; fi
## Composer
- composer self-update
- composer install --prefer-source
- travis_wait composer install --prefer-source
## PHPDocumentor
- mkdir -p build/docs
- mkdir -p build/coverage

View File

@ -23,6 +23,7 @@ v0.15.0 (?? ??? 2018)
- Fix parsing of `<w:br/>` tag. @troosan #1274
- Bookmark are not writton as internal link in html writer @troosan #1263
- It should be possible to add a Footnote in a ListItemRun @troosan #1287 #1287
- Fix parsing of Heading and Title formating @troosan @gthomas2 #465
### Changed
- Remove zend-stdlib dependency @Trainmaster #1284

View File

@ -89,6 +89,7 @@ Titles
If you want to structure your document or build table of contents, you need titles or headings.
To add a title to the document, use the ``addTitleStyle`` and ``addTitle`` method.
If `depth` is 0, a Title will be inserted, otherwise a Heading1, Heading2, ...
.. code-block:: php
@ -98,7 +99,7 @@ To add a title to the document, use the ``addTitleStyle`` and ``addTitle`` metho
- ``depth``.
- ``$fontStyle``. See :ref:`font-style`.
- ``$paragraphStyle``. See :ref:`paragraph-style`.
- ``$text``. Text to be displayed in the document.
- ``$text``. Text to be displayed in the document. This can be `string` or a `\PhpOffice\PhpWord\Element\TextRun`
It's necessary to add a title style to your document because otherwise the title won't be detected as a real title.

View File

@ -12,13 +12,14 @@ $section = $phpWord->addSection();
// Define styles
$fontStyle12 = array('spaceAfter' => 60, 'size' => 12);
$fontStyle10 = array('size' => 10);
$phpWord->addTitleStyle(null, array('size' => 22, 'bold' => true));
$phpWord->addTitleStyle(1, array('size' => 20, 'color' => '333333', 'bold' => true));
$phpWord->addTitleStyle(2, array('size' => 16, 'color' => '666666'));
$phpWord->addTitleStyle(3, array('size' => 14, 'italic' => true));
$phpWord->addTitleStyle(4, array('size' => 12));
// Add text elements
$section->addText('Table of contents 1');
$section->addTitle('Table of contents 1', 0);
$section->addTextBreak(2);
// Add TOC #1

View File

@ -27,14 +27,14 @@ abstract class AbstractCollection
/**
* Items
*
* @var array
* @var \PhpOffice\PhpWord\Element\AbstractContainer[]
*/
private $items = array();
/**
* Get items
*
* @return array
* @return \PhpOffice\PhpWord\Element\AbstractContainer[]
*/
public function getItems()
{
@ -45,7 +45,7 @@ abstract class AbstractCollection
* Get item by index
*
* @param int $index
* @return mixed
* @return \PhpOffice\PhpWord\Element\AbstractContainer
*/
public function getItem($index)
{
@ -60,7 +60,7 @@ abstract class AbstractCollection
* Set item.
*
* @param int $index
* @param mixed $item
* @param \PhpOffice\PhpWord\Element\AbstractContainer $item
*/
public function setItem($index, $item)
{
@ -72,7 +72,7 @@ abstract class AbstractCollection
/**
* Add new item
*
* @param mixed $item
* @param \PhpOffice\PhpWord\Element\AbstractContainer $item
* @return int
*/
public function addItem($item)

View File

@ -54,7 +54,7 @@ abstract class AbstractContainer extends AbstractElement
/**
* Elements collection
*
* @var array
* @var \PhpOffice\PhpWord\Element\AbstractElement[]
*/
protected $elements = array();
@ -164,6 +164,41 @@ abstract class AbstractContainer extends AbstractElement
return $this->elements;
}
/**
* Returns the element at the requested position
*
* @param int $index
* @return \PhpOffice\PhpWord\Element\AbstractElement|null
*/
public function getElement($index)
{
if (array_key_exists($index, $this->elements)) {
return $this->elements[$index];
}
return null;
}
/**
* Removes the element at requested index
*
* @param int|\PhpOffice\PhpWord\Element\AbstractElement $toRemove
*/
public function removeElement($toRemove)
{
if (is_int($toRemove) && array_key_exists($toRemove, $this->elements)) {
unset($this->elements[$toRemove]);
} elseif ($toRemove instanceof \PhpOffice\PhpWord\Element\AbstractElement) {
foreach ($this->elements as $key => $element) {
if ($element->getElementId() === $toRemove->getElementId()) {
unset($this->elements[$key]);
return;
}
}
}
}
/**
* Count elements
*

View File

@ -43,7 +43,7 @@ class Bookmark extends AbstractElement
*
* @param string $name
*/
public function __construct($name)
public function __construct($name = '')
{
$this->name = CommonText::toUTF8($name);
}

View File

@ -62,7 +62,7 @@ class ListItem extends AbstractElement
// Version >= 0.10.0 will pass numbering style name. Older version will use old method
if (!is_null($listStyle) && is_string($listStyle)) {
$this->style = new ListItemStyle($listStyle);
$this->style = new ListItemStyle($listStyle); // @codeCoverageIgnore
} else {
$this->style = $this->setNewStyle(new ListItemStyle(), $listStyle, true);
}

View File

@ -28,7 +28,7 @@ class Title extends AbstractElement
/**
* Title Text content
*
* @var string
* @var string|TextRun
*/
private $text;
@ -56,15 +56,25 @@ class Title extends AbstractElement
/**
* Create a new Title Element
*
* @param string $text
* @param string|TextRun $text
* @param int $depth
*/
public function __construct($text, $depth = 1)
{
$this->text = CommonText::toUTF8($text);
if (isset($text)) {
if (is_string($text)) {
$this->text = CommonText::toUTF8($text);
} elseif ($text instanceof TextRun) {
$this->text = $text;
} else {
throw new \InvalidArgumentException('Invalid text, should be a string or a TextRun');
}
}
$this->depth = $depth;
if (array_key_exists("Heading_{$this->depth}", Style::getStyles())) {
$this->style = "Heading{$this->depth}";
$styleName = $depth === 0 ? 'Title' : "Heading_{$this->depth}";
if (array_key_exists($styleName, Style::getStyles())) {
$this->style = str_replace('_', '', $styleName);
}
return $this;

View File

@ -212,6 +212,21 @@ class PhpWord
return $this->sections;
}
/**
* Returns the section at the requested position
*
* @param int $index
* @return \PhpOffice\PhpWord\Element\Section|null
*/
public function getSection($index)
{
if (array_key_exists($index, $this->sections)) {
return $this->sections[$index];
}
return null;
}
/**
* Create new section
*

View File

@ -18,6 +18,7 @@
namespace PhpOffice\PhpWord\Reader\Word2007;
use PhpOffice\Common\XMLReader;
use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\Element\TrackChange;
use PhpOffice\PhpWord\PhpWord;
@ -103,12 +104,10 @@ abstract class AbstractPart
{
// Paragraph style
$paragraphStyle = null;
$headingMatches = array();
$headingDepth = null;
if ($xmlReader->elementExists('w:pPr', $domNode)) {
$paragraphStyle = $this->readParagraphStyle($xmlReader, $domNode);
if (is_array($paragraphStyle) && isset($paragraphStyle['styleName'])) {
preg_match('/Heading(\d)/', $paragraphStyle['styleName'], $headingMatches);
}
$headingDepth = $this->getHeadingDepth($paragraphStyle);
}
// PreserveText
@ -147,14 +146,19 @@ abstract class AbstractPart
foreach ($nodes as $node) {
$this->readRun($xmlReader, $node, $listItemRun, $docPart, $paragraphStyle);
}
} elseif (!empty($headingMatches)) {
// Heading
$textContent = '';
} elseif ($headingDepth !== null) {
// Heading or Title
$textContent = null;
$nodes = $xmlReader->getElements('w:r', $domNode);
foreach ($nodes as $node) {
$textContent .= $xmlReader->getValue('w:t', $node);
if ($nodes->length === 1) {
$textContent = $xmlReader->getValue('w:t', $nodes->item(0));
} else {
$textContent = new TextRun($paragraphStyle);
foreach ($nodes as $node) {
$this->readRun($xmlReader, $node, $textContent, $docPart, $paragraphStyle);
}
}
$parent->addTitle($textContent, $headingMatches[1]);
$parent->addTitle($textContent, $headingDepth);
} else {
// Text and TextRun
$runCount = $xmlReader->countElements('w:r', $domNode);
@ -176,6 +180,29 @@ abstract class AbstractPart
}
}
/**
* Returns the depth of the Heading, returns 0 for a Title
*
* @param array $paragraphStyle
* @return number|null
*/
private function getHeadingDepth(array $paragraphStyle = null)
{
if (is_array($paragraphStyle) && isset($paragraphStyle['styleName'])) {
if ('Title' === $paragraphStyle['styleName']) {
return 0;
}
$headingMatches = array();
preg_match('/Heading(\d)/', $paragraphStyle['styleName'], $headingMatches);
if (!empty($headingMatches)) {
return $headingMatches[1];
}
}
return null;
}
/**
* Read w:r.
*
@ -212,10 +239,14 @@ abstract class AbstractPart
} else {
if ($xmlReader->elementExists('w:footnoteReference', $domNode)) {
// Footnote
$parent->addFootnote();
$wId = $xmlReader->getAttribute('w:id', $domNode, 'w:footnoteReference');
$footnote = $parent->addFootnote();
$footnote->setRelationId($wId);
} elseif ($xmlReader->elementExists('w:endnoteReference', $domNode)) {
// Endnote
$parent->addEndnote();
$wId = $xmlReader->getAttribute('w:id', $domNode, 'w:endnoteReference');
$endnote = $parent->addEndnote();
$endnote->setRelationId($wId);
} elseif ($xmlReader->elementExists('w:pict', $domNode)) {
// Image
$rId = $xmlReader->getAttribute('r:id', $domNode, 'w:pict/v:shape/v:imagedata');
@ -496,11 +527,9 @@ abstract class AbstractPart
return $possibleAttribute;
}
}
} else {
return $attributes;
}
return null;
return $attributes;
}
/**
@ -578,7 +607,7 @@ abstract class AbstractPart
*/
private function isOn($value = null)
{
return $value == null || $value == '1' || $value == 'true' || $value == 'on';
return $value === null || $value === '1' || $value === 'true' || $value === 'on';
}
/**

View File

@ -48,9 +48,6 @@ class Footnotes extends AbstractPart
*/
public function read(PhpWord $phpWord)
{
$getMethod = "get{$this->collection}";
$collection = $phpWord->$getMethod()->getItems();
$xmlReader = new XMLReader();
$xmlReader->getDomFromZip($this->docFile, $this->xmlFile);
$nodes = $xmlReader->getElements('*');
@ -60,17 +57,41 @@ class Footnotes extends AbstractPart
$type = $xmlReader->getAttribute('w:type', $node);
// Avoid w:type "separator" and "continuationSeparator"
// Only look for <footnote> or <endnote> without w:type attribute
if (is_null($type) && isset($collection[$id])) {
$element = $collection[$id];
$pNodes = $xmlReader->getElements('w:p/*', $node);
foreach ($pNodes as $pNode) {
$this->readRun($xmlReader, $pNode, $element, $this->collection);
// Only look for <footnote> or <endnote> without w:type attribute, or with w:type = normal
if ((is_null($type) || $type === 'normal')) {
$element = $this->getElement($phpWord, $id);
if ($element !== null) {
$pNodes = $xmlReader->getElements('w:p/*', $node);
foreach ($pNodes as $pNode) {
$this->readRun($xmlReader, $pNode, $element, $this->collection);
}
$addMethod = "add{$this->element}";
$phpWord->$addMethod($element);
}
$addMethod = "add{$this->element}";
$phpWord->$addMethod($element);
}
}
}
}
/**
* Searches for the element with the given relationId
*
* @param PhpWord $phpWord
* @param int $relationId
* @return \PhpOffice\PhpWord\Element\AbstractContainer|null
*/
private function getElement(PhpWord $phpWord, $relationId)
{
$getMethod = "get{$this->collection}";
$collection = $phpWord->$getMethod()->getItems();
//not found by key, looping to search by relationId
foreach ($collection as $collectionElement) {
if ($collectionElement->getRelationId() == $relationId) {
return $collectionElement;
}
}
return null;
}
}

View File

@ -95,7 +95,13 @@ class Style
*/
public static function addTitleStyle($depth, $fontStyle, $paragraphStyle = null)
{
return self::setStyleValues("Heading_{$depth}", new Font('title', $paragraphStyle), $fontStyle);
if ($depth == null) {
$styleName = 'Title';
} else {
$styleName = "Heading_{$depth}";
}
return self::setStyleValues($styleName, new Font('title', $paragraphStyle), $fontStyle);
}
/**

View File

@ -47,27 +47,36 @@ class Title extends AbstractElement
$xmlWriter->endElement();
}
$rId = $element->getRelationId();
$bookmarkRId = $element->getPhpWord()->addBookmark();
if ($element->getDepth() !== 0) {
$rId = $element->getRelationId();
$bookmarkRId = $element->getPhpWord()->addBookmark();
// Bookmark start for TOC
$xmlWriter->startElement('w:bookmarkStart');
$xmlWriter->writeAttribute('w:id', $bookmarkRId);
$xmlWriter->writeAttribute('w:name', "_Toc{$rId}");
$xmlWriter->endElement();
// Bookmark start for TOC
$xmlWriter->startElement('w:bookmarkStart');
$xmlWriter->writeAttribute('w:id', $bookmarkRId);
$xmlWriter->writeAttribute('w:name', "_Toc{$rId}");
$xmlWriter->endElement(); //w:bookmarkStart
}
// Actual text
$xmlWriter->startElement('w:r');
$xmlWriter->startElement('w:t');
$this->writeText($this->getText($element->getText()));
$xmlWriter->endElement(); // w:t
$xmlWriter->endElement(); // w:r
$text = $element->getText();
if (is_string($text)) {
$xmlWriter->startElement('w:r');
$xmlWriter->startElement('w:t');
$this->writeText($text);
$xmlWriter->endElement(); // w:t
$xmlWriter->endElement(); // w:r
} elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) {
$containerWriter = new Container($xmlWriter, $text);
$containerWriter->write();
}
// Bookmark end
$xmlWriter->startElement('w:bookmarkEnd');
$xmlWriter->writeAttribute('w:id', $bookmarkRId);
$xmlWriter->endElement();
$xmlWriter->endElement();
if ($element->getDepth() !== 0) {
// Bookmark end
$xmlWriter->startElement('w:bookmarkEnd');
$xmlWriter->writeAttribute('w:id', $bookmarkRId);
$xmlWriter->endElement(); //w:bookmarkEnd
}
$xmlWriter->endElement(); //w:p
}
}

View File

@ -180,9 +180,15 @@ class Styles extends AbstractPart
// Heading style
if ($styleType == 'title') {
$arrStyle = explode('_', $styleName);
$styleId = 'Heading' . $arrStyle[1];
$styleName = 'heading ' . $arrStyle[1];
$styleLink = 'Heading' . $arrStyle[1] . 'Char';
if (count($arrStyle) > 1) {
$styleId = 'Heading' . $arrStyle[1];
$styleName = 'heading ' . $arrStyle[1];
$styleLink = 'Heading' . $arrStyle[1] . 'Char';
} else {
$styleId = $styleName;
$styleName = strtolower($styleName);
$styleLink = $styleName . 'Char';
}
$xmlWriter->writeAttribute('w:styleId', $styleId);
$xmlWriter->startElement('w:link');

View File

@ -162,4 +162,35 @@ class SectionTest extends \PHPUnit\Framework\TestCase
$object = new Section(1);
$object->addHeader('ODD');
}
/**
* @covers \PhpOffice\PhpWord\Element\AbstractContainer::removeElement
*/
public function testRemoveElementByIndex()
{
$section = new Section(1);
$section->addText('firstText');
$section->addText('secondText');
$this->assertEquals(2, $section->countElements());
$section->removeElement(1);
$this->assertEquals(1, $section->countElements());
}
/**
* @covers \PhpOffice\PhpWord\Element\AbstractContainer::removeElement
*/
public function testRemoveElementByElement()
{
$section = new Section(1);
$fistText = $section->addText('firstText');
$secondText = $section->addText('secondText');
$this->assertEquals(2, $section->countElements());
$section->removeElement($fistText);
$this->assertEquals(1, $section->countElements());
$this->assertEquals($secondText->getElementId(), $section->getElement(1)->getElementId());
}
}

View File

@ -45,4 +45,25 @@ class TitleTest extends \PHPUnit\Framework\TestCase
$this->assertNull($oTitle->getStyle());
}
/**
* Create new instance with TextRun
*/
public function testConstructWithTextRun()
{
$oTextRun = new TextRun();
$oTextRun->addText('text');
$oTitle = new Title($oTextRun);
$this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\TextRun', $oTitle->getText());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testConstructWithInvalidArgument()
{
$oPageBreak = new PageBreak();
new Title($oPageBreak);
}
}

View File

@ -36,9 +36,9 @@ class ElementTest extends AbstractTestReader
</w:r>
</w:p>';
$phpWord = $this->getDocumentFromString($documentXml);
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $this->get($phpWord->getSections(), 0)->getElements();
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextBreak', $elements[0]);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $elements[1]);
$this->assertEquals('test string', $elements[1]->getText());
@ -70,17 +70,73 @@ class ElementTest extends AbstractTestReader
</w:r>
</w:p>';
$phpWord = $this->getDocumentFromString($documentXml);
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $this->get($phpWord->getSections(), 0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\ListItemRun', $elements[0]);
$this->assertEquals(0, $elements[0]->getDepth());
$sections = $phpWord->getSection(0);
$this->assertNull($sections->getElement(999));
$this->assertInstanceOf('PhpOffice\PhpWord\Element\ListItemRun', $sections->getElement(0));
$this->assertEquals(0, $sections->getElement(0)->getDepth());
$listElements = $this->get($elements, 0)->getElements();
$listElements = $sections->getElement(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $listElements[0]);
$this->assertEquals('Two', $listElements[0]->getText());
$this->assertEquals(' with ', $listElements[1]->getText());
$this->assertEquals('bold', $listElements[2]->getText());
$this->assertTrue($listElements[2]->getFontStyle()->getBold());
}
/**
* Test reading Title style
*/
public function testReadTitleStyle()
{
$documentXml = '<w:p>
<w:pPr>
<w:pStyle w:val="Title"/>
</w:pPr>
<w:r>
<w:t>This is a non formatted title</w:t>
</w:r>
</w:p>
<w:p>
<w:pPr>
<w:pStyle w:val="Title"/>
</w:pPr>
<w:r>
<w:t>This is a </w:t>
</w:r>
<w:r>
<w:rPr>
<w:b/>
</w:rPr>
<w:t>bold</w:t>
</w:r>
<w:r>
<w:t> title</w:t>
</w:r>
</w:p>';
$stylesXml = '<w:style w:type="paragraph" w:styleId="Title">
<w:name w:val="Title"/>
<w:link w:val="TitleChar"/>
<w:rPr>
<w:i/>
</w:rPr>
</w:style>';
$phpWord = $this->getDocumentFromString(array('document' => $documentXml, 'styles' => $stylesXml));
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Title', $elements[0]);
/** @var \PhpOffice\PhpWord\Element\Title $title */
$title = $elements[0];
$this->assertEquals('Title', $title->getStyle());
$this->assertEquals('This is a non formatted title', $title->getText());
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Title', $elements[1]);
/** @var \PhpOffice\PhpWord\Element\Title $formattedTitle */
$formattedTitle = $elements[1];
$this->assertEquals('Title', $formattedTitle->getStyle());
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $formattedTitle->getText());
}
}

View File

@ -0,0 +1,163 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @see https://github.com/PHPOffice/PHPWord
* @copyright 2010-2017 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord\Reader\Word2007;
use PhpOffice\PhpWord\AbstractTestReader;
/**
* Test class for PhpOffice\PhpWord\Reader\Word2007 subnamespace
*/
class PartTest extends AbstractTestReader
{
/**
* Test reading Footnotes
*/
public function testReadFootnote()
{
$documentXml = '<w:p>
<w:r>
<w:t>This is a test</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rStyle w:val="FootnoteReference"/>
</w:rPr>
<w:footnoteReference w:id="1"/>
</w:r>
</w:p>
<w:p>
<w:r>
<w:t>And another one</w:t>
</w:r>
<w:r>
<w:rPr>
<w:rStyle w:val="EndnoteReference"/>
</w:rPr>
<w:endnoteReference w:id="2"/>
</w:r>
</w:p>';
$footnotesXml = '<w:footnote w:type="separator" w:id="-1">
<w:p>
<w:r>
<w:separator/>
</w:r>
</w:p>
</w:footnote>
<w:footnote w:id="1">
<w:p>
<w:pPr>
<w:pStyle w:val="FootnoteText"/>
</w:pPr>
<w:r>
<w:rPr>
<w:rStyle w:val="FootnoteReference"/>
</w:rPr>
<w:footnoteRef/>
</w:r>
<w:r>
<w:rPr>
<w:lang w:val="nl-NL"/>
</w:rPr>
<w:t>footnote text</w:t>
</w:r>
</w:p>
</w:footnote>';
$endnotesXml = '<w:endnote w:type="separator" w:id="-1">
<w:p>
<w:r>
<w:separator/>
</w:r>
</w:p>
</w:endnote>
<w:endnote w:type="continuationNotice" w:id="1">
<w:p>
<w:r>
<w:separator/>
</w:r>
</w:p>
</w:endnote>
<w:endnote w:id="2">
<w:p>
<w:pPr>
<w:pStyle w:val="EndnoteText"/>
</w:pPr>
<w:r>
<w:rPr>
<w:rStyle w:val="EndnoteReference"/>
</w:rPr>
<w:endnoteRef/>
</w:r>
<w:r>
<w:rPr>
<w:lang w:val="nl-NL"/>
</w:rPr>
<w:t>This is an endnote</w:t>
</w:r>
</w:p>
</w:endnote>';
$phpWord = $this->getDocumentFromString(array('document' => $documentXml, 'footnotes' => $footnotesXml, 'endnotes' => $endnotesXml));
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]);
/** @var \PhpOffice\PhpWord\Element\TextRun $textRun */
$textRun = $elements[0];
//test the text in the first paragraph
/** @var \PhpOffice\PhpWord\Element\Text $text */
$text = $elements[0]->getElement(0);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $text);
$this->assertEquals('This is a test', $text->getText());
//test the presence of the footnote in the document.xml
/** @var \PhpOffice\PhpWord\Element\Footnote $footnote */
$documentFootnote = $textRun->getElement(1);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Footnote', $documentFootnote);
$this->assertEquals(1, $documentFootnote->getRelationId());
//test the presence of the footnote in the footnote.xml
/** @var \PhpOffice\PhpWord\Element\Footnote $footnote */
$footnote = $phpWord->getFootnotes()->getItem(1);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Footnote', $footnote);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $footnote->getElement(0));
$this->assertEquals('footnote text', $footnote->getElement(0)->getText());
$this->assertEquals(1, $footnote->getRelationId());
//test the text in the second paragraph
/** @var \PhpOffice\PhpWord\Element\Text $text */
$text = $elements[1]->getElement(0);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $text);
$this->assertEquals('And another one', $text->getText());
//test the presence of the endnote in the document.xml
/** @var \PhpOffice\PhpWord\Element\Endnote $endnote */
$documentEndnote = $elements[1]->getElement(1);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Endnote', $documentEndnote);
$this->assertEquals(2, $documentEndnote->getRelationId());
//test the presence of the endnote in the endnote.xml
/** @var \PhpOffice\PhpWord\Element\Endnote $endnote */
$endnote = $phpWord->getEndnotes()->getItem(1);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Endnote', $endnote);
$this->assertEquals(2, $endnote->getRelationId());
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $endnote->getElement(0));
$this->assertEquals('This is an endnote', $endnote->getElement(0)->getText());
}
}

View File

@ -37,9 +37,9 @@ class StyleTest extends AbstractTestReader
</w:tblPr>
</w:tbl>';
$phpWord = $this->getDocumentFromString($documentXml);
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $this->get($phpWord->getSections(), 0)->getElements();
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]);
$this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle());
$this->assertEquals(Table::LAYOUT_FIXED, $elements[0]->getStyle()->getLayout());
@ -56,9 +56,9 @@ class StyleTest extends AbstractTestReader
</w:tblPr>
</w:tbl>';
$phpWord = $this->getDocumentFromString($documentXml);
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $this->get($phpWord->getSections(), 0)->getElements();
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Table', $elements[0]);
$this->assertInstanceOf('PhpOffice\PhpWord\Style\Table', $elements[0]->getStyle());
$this->assertEquals(TblWidth::AUTO, $elements[0]->getStyle()->getUnit());

View File

@ -447,6 +447,9 @@ class ElementTest extends \PHPUnit\Framework\TestCase
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:commentReference'));
}
/**
* Test Track changes
*/
public function testTrackChange()
{
$phpWord = new PhpWord();
@ -462,4 +465,30 @@ class ElementTest extends \PHPUnit\Framework\TestCase
$this->assertEquals('author name', $doc->getElementAttribute('/w:document/w:body/w:p/w:ins', 'w:author'));
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:del/w:r/w:delText'));
}
/**
* Test Title and Headings
*/
public function testTitleAndHeading()
{
$phpWord = new PhpWord();
$phpWord->addTitleStyle(0, array('size' => 14, 'italic' => true));
$phpWord->addTitleStyle(1, array('size' => 20, 'color' => '333333', 'bold' => true));
$section = $phpWord->addSection();
$section->addTitle('This is a title', 0);
$section->addTitle('Heading 1', 1);
$doc = TestHelperDOCX::getDocument($phpWord);
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:r/w:t'));
$this->assertEquals('This is a title', $doc->getElement('/w:document/w:body/w:p[1]/w:r/w:t')->textContent);
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p[1]/w:pPr/w:pStyle'));
$this->assertEquals('Title', $doc->getElementAttribute('/w:document/w:body/w:p[1]/w:pPr/w:pStyle', 'w:val'));
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:t'));
$this->assertEquals('Heading 1', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->textContent);
$this->assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:pPr/w:pStyle'));
$this->assertEquals('Heading1', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:pPr/w:pStyle', 'w:val'));
}
}

View File

@ -24,7 +24,6 @@ use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\SimpleType\NumberFormat;
use PhpOffice\PhpWord\Style\Cell;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\TestHelperDOCX;
/**

View File

@ -17,43 +17,48 @@
namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Reader\Word2007\Document;
/**
* Base class for Word2007 reader tests
*/
abstract class AbstractTestReader extends \PHPUnit\Framework\TestCase
{
private $parts = array(
'styles' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Styles', 'xml' => '<w:styles xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:docDefaults><w:rPrDefault><w:rPr><w:sz w:val="24"/></w:rPr></w:rPrDefault></w:docDefaults>{toReplace}</w:styles>'),
'document' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Document', 'xml' => '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>{toReplace}</w:body></w:document>'),
'footnotes' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Footnotes', 'xml' => '<w:footnotes xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">{toReplace}</w:footnotes>'),
'endnotes' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Endnotes', 'xml' => '<w:endnotes xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">{toReplace}</w:endnotes>'),
'settings' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Settings', 'xml' => '<w:comments xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">{toReplace}</w:comments>'),
);
/**
* Builds a PhpWord instance based on the xml passed
*
* @param string $documentXml
* @param null|string $stylesXml
* @return \PhpOffice\PhpWord\PhpWord
*/
protected function getDocumentFromString($documentXml)
protected function getDocumentFromString(array $partXmls = array())
{
$phpWord = new PhpWord();
$file = __DIR__ . '/../_files/temp.docx';
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE);
$zip->addFromString('document.xml', '<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"><w:body>' . $documentXml . '</w:body></w:document>');
foreach ($this->parts as $partName => $part) {
if (array_key_exists($partName, $partXmls)) {
$zip->addFromString("{$partName}.xml", str_replace('{toReplace}', $partXmls[$partName], $this->parts[$partName]['xml']));
}
}
$zip->close();
$documentReader = new Document($file, 'document.xml');
$documentReader->read($phpWord);
$phpWord = new PhpWord();
foreach ($this->parts as $partName => $part) {
if (array_key_exists($partName, $partXmls)) {
$className = $this->parts[$partName]['class'];
$reader = new $className($file, "{$partName}.xml");
$reader->read($phpWord);
}
}
unlink($file);
return $phpWord;
}
/**
* Returns the element at position $index in the array
*
* @param array $array
* @param number $index
* @return mixed
*/
protected function get(array $array, $index = 0)
{
return $array[$index];
}
}