Merge pull request #1299 from zobo/word2007_read_tabs

Add support for reading <w:tab/> element in runs.
This commit is contained in:
troosan 2018-03-18 17:54:47 +01:00 committed by GitHub
commit 9ffce350f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 198 additions and 81 deletions

View File

@ -18,6 +18,7 @@
namespace PhpOffice\PhpWord\Reader\Word2007; namespace PhpOffice\PhpWord\Reader\Word2007;
use PhpOffice\Common\XMLReader; use PhpOffice\Common\XMLReader;
use PhpOffice\PhpWord\Element\AbstractContainer;
use PhpOffice\PhpWord\Element\TextRun; use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\Element\TrackChange;
use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\PhpWord;
@ -161,20 +162,14 @@ abstract class AbstractPart
$parent->addTitle($textContent, $headingDepth); $parent->addTitle($textContent, $headingDepth);
} else { } else {
// Text and TextRun // Text and TextRun
$runCount = $xmlReader->countElements('w:r', $domNode); $textRunContainers = $xmlReader->countElements('w:r|w:ins|w:del|w:hyperlink|w:smartTag', $domNode);
$insCount = $xmlReader->countElements('w:ins', $domNode); if (0 === $textRunContainers) {
$delCount = $xmlReader->countElements('w:del', $domNode);
$linkCount = $xmlReader->countElements('w:hyperlink', $domNode);
$runLinkCount = $runCount + $insCount + $delCount + $linkCount;
if (0 == $runLinkCount) {
$parent->addTextBreak(null, $paragraphStyle); $parent->addTextBreak(null, $paragraphStyle);
} else { } else {
$nodes = $xmlReader->getElements('*', $domNode); $nodes = $xmlReader->getElements('*', $domNode);
if ($runLinkCount > 1) { $paragraph = $parent->addTextRun($paragraphStyle);
$parent = $parent->addTextRun($paragraphStyle);
}
foreach ($nodes as $node) { foreach ($nodes as $node) {
$this->readRun($xmlReader, $node, $parent, $docPart, $paragraphStyle); $this->readRun($xmlReader, $node, $paragraph, $docPart, $paragraphStyle);
} }
} }
} }
@ -216,40 +211,46 @@ abstract class AbstractPart
*/ */
protected function readRun(XMLReader $xmlReader, \DOMElement $domNode, $parent, $docPart, $paragraphStyle = null) protected function readRun(XMLReader $xmlReader, \DOMElement $domNode, $parent, $docPart, $paragraphStyle = null)
{ {
if (in_array($domNode->nodeName, array('w:ins', 'w:del'))) { if (in_array($domNode->nodeName, array('w:ins', 'w:del', 'w:smartTag', 'w:hyperlink'))) {
$nodes = $xmlReader->getElements('*', $domNode); $nodes = $xmlReader->getElements('*', $domNode);
foreach ($nodes as $node) { foreach ($nodes as $node) {
return $this->readRun($xmlReader, $node, $parent, $docPart, $paragraphStyle); $this->readRun($xmlReader, $node, $parent, $docPart, $paragraphStyle);
}
}
if (!in_array($domNode->nodeName, array('w:r', 'w:hyperlink'))) {
return;
} }
} elseif ($domNode->nodeName == 'w:r') {
$fontStyle = $this->readFontStyle($xmlReader, $domNode); $fontStyle = $this->readFontStyle($xmlReader, $domNode);
$nodes = $xmlReader->getElements('*', $domNode);
// Link foreach ($nodes as $node) {
if ('w:hyperlink' == $domNode->nodeName) { $this->readRunChild($xmlReader, $node, $parent, $docPart, $paragraphStyle, $fontStyle);
$rId = $xmlReader->getAttribute('r:id', $domNode);
$textContent = $xmlReader->getValue('w:r/w:t', $domNode);
$target = $this->getMediaTarget($docPart, $rId);
if (!is_null($target)) {
$parent->addLink($target, $textContent, $fontStyle, $paragraphStyle);
} }
} else { }
if ($xmlReader->elementExists('w:footnoteReference', $domNode)) { }
/**
* Parses nodes under w:r
*
* @param XMLReader $xmlReader
* @param \DOMElement $node
* @param AbstractContainer $parent
* @param string $docPart
* @param mixed $paragraphStyle
* @param mixed $fontStyle
*/
protected function readRunChild(XMLReader $xmlReader, \DOMElement $node, AbstractContainer $parent, $docPart, $paragraphStyle = null, $fontStyle = null)
{
$runParent = $node->parentNode->parentNode;
if ($node->nodeName == 'w:footnoteReference') {
// Footnote // Footnote
$wId = $xmlReader->getAttribute('w:id', $domNode, 'w:footnoteReference'); $wId = $xmlReader->getAttribute('w:id', $node);
$footnote = $parent->addFootnote(); $footnote = $parent->addFootnote();
$footnote->setRelationId($wId); $footnote->setRelationId($wId);
} elseif ($xmlReader->elementExists('w:endnoteReference', $domNode)) { } elseif ($node->nodeName == 'w:endnoteReference') {
// Endnote // Endnote
$wId = $xmlReader->getAttribute('w:id', $domNode, 'w:endnoteReference'); $wId = $xmlReader->getAttribute('w:id', $node);
$endnote = $parent->addEndnote(); $endnote = $parent->addEndnote();
$endnote->setRelationId($wId); $endnote->setRelationId($wId);
} elseif ($xmlReader->elementExists('w:pict', $domNode)) { } elseif ($node->nodeName == 'w:pict') {
// Image // Image
$rId = $xmlReader->getAttribute('r:id', $domNode, 'w:pict/v:shape/v:imagedata'); $rId = $xmlReader->getAttribute('r:id', $node, 'v:shape/v:imagedata');
$target = $this->getMediaTarget($docPart, $rId); $target = $this->getMediaTarget($docPart, $rId);
if (!is_null($target)) { if (!is_null($target)) {
if ('External' == $this->getTargetMode($docPart, $rId)) { if ('External' == $this->getTargetMode($docPart, $rId)) {
@ -259,32 +260,36 @@ abstract class AbstractPart
} }
$parent->addImage($imageSource); $parent->addImage($imageSource);
} }
} elseif ($xmlReader->elementExists('w:object', $domNode)) { } elseif ($node->nodeName == 'w:object') {
// Object // Object
$rId = $xmlReader->getAttribute('r:id', $domNode, 'w:object/o:OLEObject'); $rId = $xmlReader->getAttribute('r:id', $node, 'o:OLEObject');
// $rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata'); // $rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata');
$target = $this->getMediaTarget($docPart, $rId); $target = $this->getMediaTarget($docPart, $rId);
if (!is_null($target)) { if (!is_null($target)) {
$textContent = "&lt;Object: {$target}>"; $textContent = "&lt;Object: {$target}>";
$parent->addText($textContent, $fontStyle, $paragraphStyle); $parent->addText($textContent, $fontStyle, $paragraphStyle);
} }
} } elseif ($node->nodeName == 'w:br') {
if ($xmlReader->elementExists('w:br', $domNode)) {
$parent->addTextBreak(); $parent->addTextBreak();
} } elseif ($node->nodeName == 'w:tab') {
if ($xmlReader->elementExists('w:t', $domNode)) { $parent->addText("\t");
} elseif ($node->nodeName == 'w:t' || $node->nodeName == 'w:delText') {
// TextRun // TextRun
if ($domNode->parentNode->nodeName == 'w:del') { $textContent = $xmlReader->getValue('.', $node);
$textContent = $xmlReader->getValue('w:delText', $domNode);
} else { if ($runParent->nodeName == 'w:hyperlink') {
$textContent = $xmlReader->getValue('w:t', $domNode); $rId = $xmlReader->getAttribute('r:id', $runParent);
$target = $this->getMediaTarget($docPart, $rId);
if (!is_null($target)) {
$parent->addLink($target, $textContent, $fontStyle, $paragraphStyle);
} }
} else {
/** @var AbstractElement $element */ /** @var AbstractElement $element */
$element = $parent->addText($textContent, $fontStyle, $paragraphStyle); $element = $parent->addText($textContent, $fontStyle, $paragraphStyle);
if (in_array($domNode->parentNode->nodeName, array('w:ins', 'w:del'))) { if (in_array($runParent->nodeName, array('w:ins', 'w:del'))) {
$type = ($domNode->parentNode->nodeName == 'w:del') ? TrackChange::DELETED : TrackChange::INSERTED; $type = ($runParent->nodeName == 'w:del') ? TrackChange::DELETED : TrackChange::INSERTED;
$author = $domNode->parentNode->getAttribute('w:author'); $author = $runParent->getAttribute('w:author');
$date = \DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $domNode->parentNode->getAttribute('w:date')); $date = \DateTime::createFromFormat('Y-m-d\TH:i:s\Z', $runParent->getAttribute('w:date'));
$element->setChangeInfo($type, $author, $date); $element->setChangeInfo($type, $author, $date);
} }
} }

View File

@ -38,11 +38,17 @@ class Title extends AbstractElement
} }
$tag = 'h' . $this->element->getDepth(); $tag = 'h' . $this->element->getDepth();
if (Settings::isOutputEscapingEnabled()) {
$text = $this->escaper->escapeHtml($this->element->getText());
} else {
$text = $this->element->getText(); $text = $this->element->getText();
if (is_string($text)) {
if (Settings::isOutputEscapingEnabled()) {
$text = $this->escaper->escapeHtml($text);
} }
} elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) {
$writer = new Container($this->parentWriter, $this->element);
$text = $writer->write();
}
$content = "<{$tag}>{$text}</{$tag}>" . PHP_EOL; $content = "<{$tag}>{$text}</{$tag}>" . PHP_EOL;
return $content; return $content;

View File

@ -37,7 +37,13 @@ class Title extends AbstractElement
$xmlWriter->startElement('text:h'); $xmlWriter->startElement('text:h');
$xmlWriter->writeAttribute('text:outline-level', $element->getDepth()); $xmlWriter->writeAttribute('text:outline-level', $element->getDepth());
$this->writeText($element->getText()); $text = $element->getText();
if (is_string($text)) {
$this->writeText($text);
} elseif ($text instanceof \PhpOffice\PhpWord\Element\AbstractContainer) {
$containerWriter = new Container($xmlWriter, $text);
$containerWriter->write();
}
$xmlWriter->endElement(); // text:h $xmlWriter->endElement(); // text:h
} }
} }

View File

@ -34,7 +34,7 @@ class Text extends AbstractElement
/** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */
$element = $this->element; $element = $this->element;
$elementClass = str_replace('\\Writer\\RTF', '', get_class($this)); $elementClass = str_replace('\\Writer\\RTF', '', get_class($this));
if (!$element instanceof $elementClass) { if (!$element instanceof $elementClass || !is_string($element->getText())) {
return ''; return '';
} }

View File

@ -18,6 +18,7 @@
namespace PhpOffice\PhpWord\Reader\Word2007; namespace PhpOffice\PhpWord\Reader\Word2007;
use PhpOffice\PhpWord\AbstractTestReader; use PhpOffice\PhpWord\AbstractTestReader;
use PhpOffice\PhpWord\Element\TrackChange;
/** /**
* Test class for PhpOffice\PhpWord\Reader\Word2007\Element subnamespace * Test class for PhpOffice\PhpWord\Reader\Word2007\Element subnamespace
@ -39,9 +40,35 @@ class ElementTest extends AbstractTestReader
$phpWord = $this->getDocumentFromString(array('document' => $documentXml)); $phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $phpWord->getSection(0)->getElements(); $elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextBreak', $elements[0]); $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $elements[1]); /** @var \PhpOffice\PhpWord\Element\TextRun $textRun */
$this->assertEquals('test string', $elements[1]->getText()); $textRun = $elements[0];
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextBreak', $textRun->getElement(0));
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(1));
$this->assertEquals('test string', $textRun->getElement(1)->getText());
}
/**
* Test reading content inside w:smartTag
*/
public function testSmartTag()
{
$documentXml = '<w:p>
<w:smartTag>
<w:r>
<w:t xml:space="preserve">test string</w:t>
</w:r>
</w:smartTag>
</w:p>';
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]);
/** @var \PhpOffice\PhpWord\Element\TextRun $textRun */
$textRun = $elements[0];
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(0));
$this->assertEquals('test string', $textRun->getElement(0)->getText());
} }
/** /**
@ -85,6 +112,76 @@ class ElementTest extends AbstractTestReader
$this->assertTrue($listElements[2]->getFontStyle()->getBold()); $this->assertTrue($listElements[2]->getFontStyle()->getBold());
} }
/**
* Test reading track changes
*/
public function testReadTrackChange()
{
$documentXml = '<w:p>
<w:r>
<w:t>One </w:t>
</w:r>
<w:del w:author="Barney" w:date="2018-03-14T10:57:05Z">
<w:r>
<w:delText>two</w:delText>
</w:r>
</w:del>
<w:ins w:author="Fred" w:date="2018-03-14T10:57:05Z">
<w:r>
<w:t>three</w:t>
</w:r>
</w:ins>
</w:p>';
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]);
/** @var \PhpOffice\PhpWord\Element\TextRun $elements */
$textRun = $elements[0];
$this->assertEquals('One ', $textRun->getElement(0)->getText());
$this->assertEquals('two', $textRun->getElement(1)->getText());
$this->assertNotNull($textRun->getElement(1)->getTrackChange());
/** @var \PhpOffice\PhpWord\Element\TrackChange $trackChange */
$trackChange = $textRun->getElement(1)->getTrackChange();
$this->assertEquals(TrackChange::DELETED, $trackChange->getChangeType());
$this->assertEquals('three', $textRun->getElement(2)->getText());
$this->assertNotNull($textRun->getElement(2)->getTrackChange());
/** @var \PhpOffice\PhpWord\Element\TrackChange $trackChange */
$trackChange = $textRun->getElement(2)->getTrackChange();
$this->assertEquals(TrackChange::INSERTED, $trackChange->getChangeType());
}
/**
* Test reading of tab
*/
public function testReadTab()
{
$documentXml = '<w:p>
<w:r>
<w:t>One</w:t>
<w:tab/>
<w:t>Two</w:t>
</w:r>
</w:p>';
$phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]);
/** @var \PhpOffice\PhpWord\Element\TextRun $textRun */
$textRun = $elements[0];
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(0));
$this->assertEquals('One', $textRun->getElement(0)->getText());
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(1));
$this->assertEquals("\t", $textRun->getElement(1)->getText());
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(2));
$this->assertEquals('Two', $textRun->getElement(2)->getText());
}
/** /**
* Test reading Title style * Test reading Title style
*/ */

View File

@ -117,10 +117,13 @@ class StyleTest extends AbstractTestReader
$phpWord = $this->getDocumentFromString(array('document' => $documentXml)); $phpWord = $this->getDocumentFromString(array('document' => $documentXml));
$elements = $phpWord->getSection(0)->getElements(); $elements = $phpWord->getSection(0)->getElements();
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $elements[0]); /** @var \PhpOffice\PhpWord\Element\TextRun $elements */
$this->assertInstanceOf('PhpOffice\PhpWord\Style\Font', $elements[0]->getFontStyle()); $textRun = $elements[0];
$this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $textRun);
$this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $textRun->getElement(0));
$this->assertInstanceOf('PhpOffice\PhpWord\Style\Font', $textRun->getElement(0)->getFontStyle());
/** @var \PhpOffice\PhpWord\Style\Font $fontStyle */ /** @var \PhpOffice\PhpWord\Style\Font $fontStyle */
$fontStyle = $elements[0]->getFontStyle(); $fontStyle = $textRun->getElement(0)->getFontStyle();
$this->assertEquals(15, $fontStyle->getPosition()); $this->assertEquals(15, $fontStyle->getPosition());
} }
} }

View File

@ -75,7 +75,7 @@ class Word2007Test extends \PHPUnit\Framework\TestCase
public function testSave() public function testSave()
{ {
$localImage = __DIR__ . '/../_files/images/earth.jpg'; $localImage = __DIR__ . '/../_files/images/earth.jpg';
$remoteImage = 'http://php.net//images/logos/php-med-trans-light.gif'; $remoteImage = 'http://php.net/images/logos/new-php-logo.png';
$phpWord = new PhpWord(); $phpWord = new PhpWord();
$phpWord->addFontStyle('Font', array('size' => 11)); $phpWord->addFontStyle('Font', array('size' => 11));
$phpWord->addParagraphStyle('Paragraph', array('alignment' => Jc::CENTER)); $phpWord->addParagraphStyle('Paragraph', array('alignment' => Jc::CENTER));