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:
parent
740e66acf5
commit
30b224b3d0
|
|
@ -1,3 +1,8 @@
|
|||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
tests:
|
||||
override: [php-scrutinizer-run]
|
||||
filter:
|
||||
excluded_paths: [ 'vendor/*', 'tests/*', 'samples/*', 'src/PhpWord/Shared/PCLZip/*' ]
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class Bookmark extends AbstractElement
|
|||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function __construct($name)
|
||||
public function __construct($name = '')
|
||||
{
|
||||
$this->name = CommonText::toUTF8($name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue