Ability to create custom list #10 and to read list definition from DOCX

This commit is contained in:
Ivan Lanin 2014-04-11 16:16:22 +07:00
parent f837381238
commit 47669f501a
21 changed files with 1276 additions and 138 deletions

View File

@ -4,7 +4,7 @@ This is the changelog between releases of PHPWord. Releases are listed in revers
## 0.9.2 - Not yet released ## 0.9.2 - Not yet released
This release marked heavy refactorings on internal code structure with the creation of some abstract classes to reduce code duplication. `Element` subnamespace is introduced in this release to replace `Section`. Word2007 reader capability is greatly enhanced. Endnote is introduced. This release marked heavy refactorings on internal code structure with the creation of some abstract classes to reduce code duplication. `Element` subnamespace is introduced in this release to replace `Section`. Word2007 reader capability is greatly enhanced. Endnote is introduced. List numbering is now customizable.
### Features ### Features
@ -27,8 +27,9 @@ This release marked heavy refactorings on internal code structure with the creat
- Object: Ability to add object in header, footer, textrun, and footnote - @ivanlanin GH-187 - Object: Ability to add object in header, footer, textrun, and footnote - @ivanlanin GH-187
- Media: Add `Media::resetElements()` to reset all media data - @juzi GH-19 - Media: Add `Media::resetElements()` to reset all media data - @juzi GH-19
- General: Add `Style::resetStyles()`, `Footnote::resetElements()`, and `TOC::resetTitles()` - @ivanlanin GH-187 - 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 - Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table, and list - @ivanlanin
- Endnote: Ability to add endnotes - @ivanlanin - Endnote: Ability to add endnotes - @ivanlanin
- ListItem: Ability to create custom list and reset list number - @ivanlanin GH-10
### Bugfixes ### Bugfixes

View File

@ -8,41 +8,52 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord();
// Begin code // Begin code
$section = $phpWord->addSection(); $section = $phpWord->addSection();
// Add listitem elements // Style definition
$section->addListItem('List Item 1', 0);
$section->addListItem('List Item 2', 0);
$section->addListItem('List Item 3', 0);
$section->addTextBreak(2);
// Add listitem elements
$section->addListItem('List Item 1', 0);
$section->addListItem('List Item 1.1', 1);
$section->addListItem('List Item 1.2', 1);
$section->addListItem('List Item 1.3 (styled)', 1, array('bold'=>true));
$section->addListItem('List Item 1.3.1', 2);
$section->addListItem('List Item 1.3.2', 2);
$section->addTextBreak(2);
// Add listitem elements
$listStyle = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER);
$section->addListItem('List Item 1', 0, null, $listStyle);
$section->addListItem('List Item 2', 0, null, $listStyle);
$section->addListItem('List Item 3', 0, null, $listStyle);
$section->addTextBreak(2);
// Add listitem elements
$phpWord->addFontStyle('myOwnStyle', array('color'=>'FF0000')); $phpWord->addFontStyle('myOwnStyle', array('color'=>'FF0000'));
$phpWord->addParagraphStyle('P-Style', array('spaceAfter'=>95)); $phpWord->addParagraphStyle('P-Style', array('spaceAfter'=>95));
$listStyle = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER_NESTED); $phpWord->addNumberingStyle(
$section->addListItem('List Item 1', 0, 'myOwnStyle', $listStyle, 'P-Style'); 'multilevel',
$section->addListItem('List Item 2', 0, 'myOwnStyle', $listStyle, 'P-Style'); array('type' => 'multilevel', 'levels' => array(
$section->addListItem('List Item 3', 1, 'myOwnStyle', $listStyle, 'P-Style'); array('format' => 'decimal', 'text' => '%1.', 'left' => 360, 'hanging' => 360, 'tabPos' => 360),
$section->addListItem('List Item 4', 1, 'myOwnStyle', $listStyle, 'P-Style'); array('format' => 'upperLetter', 'text' => '%2.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720),
$section->addListItem('List Item 5', 2, 'myOwnStyle', $listStyle, 'P-Style'); )
$section->addListItem('List Item 6', 1, 'myOwnStyle', $listStyle, 'P-Style'); )
$section->addListItem('List Item 7', 0, 'myOwnStyle', $listStyle, 'P-Style'); );
$predefinedMultilevel = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER_NESTED);
// Lists
$section->addText('Multilevel list.');
$section->addListItem('List Item I', 0, null, 'multilevel');
$section->addListItem('List Item I.a', 1, null, 'multilevel');
$section->addListItem('List Item I.b', 1, null, 'multilevel');
$section->addListItem('List Item II', 0, null, 'multilevel');
$section->addListItem('List Item II.a', 1, null, 'multilevel');
$section->addListItem('List Item III', 0, null, 'multilevel');
$section->addTextBreak(2);
$section->addText('Basic simple bulleted list.');
$section->addListItem('List Item 1');
$section->addListItem('List Item 2');
$section->addListItem('List Item 3');
$section->addTextBreak(2);
$section->addText('Continue from multilevel list above.');
$section->addListItem('List Item IV', 0, null, 'multilevel');
$section->addListItem('List Item IV.a', 1, null, 'multilevel');
$section->addTextBreak(2);
$section->addText('Multilevel predefined list.');
$section->addListItem('List Item 1', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addListItem('List Item 2', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addListItem('List Item 3', 1, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addListItem('List Item 4', 1, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addListItem('List Item 5', 2, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addListItem('List Item 6', 1, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addListItem('List Item 7', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style');
$section->addTextBreak(2);
// End code
// Save file // Save file
$name = basename(__FILE__, '.php'); $name = basename(__FILE__, '.php');

View File

@ -469,9 +469,6 @@ abstract class AbstractElement
{ {
if (!is_null($styleValue) && is_array($styleValue)) { if (!is_null($styleValue) && is_array($styleValue)) {
foreach ($styleValue as $key => $value) { foreach ($styleValue as $key => $value) {
if (substr($key, 0, 1) == '_') {
$key = substr($key, 1);
}
$styleObject->setStyleValue($key, $value); $styleObject->setStyleValue($key, $value);
} }
$style = $styleObject; $style = $styleObject;

View File

@ -44,15 +44,21 @@ class ListItem extends AbstractElement
* *
* @param string $text * @param string $text
* @param int $depth * @param int $depth
* @param mixed $styleFont * @param mixed $fontStyle
* @param mixed $styleList * @param array|string|null $listStyle
* @param mixed $styleParagraph * @param mixed $paragraphStyle
*/ */
public function __construct($text, $depth = 0, $styleFont = null, $styleList = null, $styleParagraph = null) public function __construct($text, $depth = 0, $fontStyle = null, $listStyle = null, $paragraphStyle = null)
{ {
$this->textObject = new Text($text, $styleFont, $styleParagraph); $this->textObject = new Text($text, $fontStyle, $paragraphStyle);
$this->depth = $depth; $this->depth = $depth;
$this->style = $this->setStyle(new ListItemStyle(), $styleList, true);
// Version >= 0.9.2 will pass numbering style name. Older version will use old method
if (!is_null($listStyle) && is_string($listStyle)) {
$this->style = new ListItemStyle($listStyle);
} else {
$this->style = $this->setStyle(new ListItemStyle(), $listStyle, true);
}
} }
/** /**

View File

@ -9,7 +9,6 @@
namespace PhpOffice\PhpWord; namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Element\Endnote; use PhpOffice\PhpWord\Element\Endnote;
/** /**

View File

@ -214,6 +214,17 @@ class PhpWord
Style::addLinkStyle($styleName, $styles); Style::addLinkStyle($styleName, $styles);
} }
/**
* Adds a numbering style
*
* @param string $styleName
* @param mixed $styles
*/
public function addNumberingStyle($styleName, $styles)
{
Style::addNumberingStyle($styleName, $styles);
}
/** /**
* Get all sections * Get all sections
* *

View File

@ -11,8 +11,6 @@ namespace PhpOffice\PhpWord\Reader;
use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Footnote;
use PhpOffice\PhpWord\Endnotes;
use PhpOffice\PhpWord\DocumentProperties; use PhpOffice\PhpWord\DocumentProperties;
use PhpOffice\PhpWord\Shared\XMLReader; use PhpOffice\PhpWord\Shared\XMLReader;
use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Element\Section;
@ -52,6 +50,19 @@ class Word2007 extends AbstractReader implements ReaderInterface
$this->readRelationships($filename); $this->readRelationships($filename);
// Read styles and numbering first
foreach ($this->rels['document'] as $rId => $rel) {
switch ($rel['type']) {
case 'styles':
$this->readStyles($filename, $rel['target']);
break;
case 'numbering':
$this->readNumbering($filename, $rel['target']);
break;
}
}
// Read main relationship // Read main relationship
foreach ($this->rels['main'] as $rId => $rel) { foreach ($this->rels['main'] as $rId => $rel) {
switch ($rel['type']) { switch ($rel['type']) {
@ -87,14 +98,9 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
} }
// Read document relationships // Read footnotes and endnotes
foreach ($this->rels['document'] as $rId => $rel) { foreach ($this->rels['document'] as $rId => $rel) {
switch ($rel['type']) { switch ($rel['type']) {
case 'styles':
$this->readStyles($filename, $rel['target']);
break;
case 'footnotes': case 'footnotes':
case 'endnotes': case 'endnotes':
$this->readNotes($filename, $rel['target'], $rel['type']); $this->readNotes($filename, $rel['target'], $rel['type']);
@ -178,7 +184,6 @@ class Word2007 extends AbstractReader implements ReaderInterface
$nodes = $xmlReader->getElements('*'); $nodes = $xmlReader->getElements('*');
if ($nodes->length > 0) { if ($nodes->length > 0) {
foreach ($nodes as $node) { foreach ($nodes as $node) {
$nodeName = $node->nodeName;
$propertyName = $xmlReader->getAttribute('name', $node); $propertyName = $xmlReader->getAttribute('name', $node);
$attributeNode = $xmlReader->getElement('*', $node); $attributeNode = $xmlReader->getElement('*', $node);
$attributeType = $attributeNode->nodeName; $attributeType = $attributeNode->nodeName;
@ -206,6 +211,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
$section = $this->phpWord->addSection(); $section = $this->phpWord->addSection();
foreach ($nodes as $node) { foreach ($nodes as $node) {
switch ($node->nodeName) { switch ($node->nodeName) {
case 'w:p': // Paragraph case 'w:p': // Paragraph
if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') { if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') {
$section->addPageBreak(); // PageBreak $section->addPageBreak(); // PageBreak
@ -221,9 +227,11 @@ class Word2007 extends AbstractReader implements ReaderInterface
$section = $this->phpWord->addSection(); $section = $this->phpWord->addSection();
} }
break; break;
case 'w:tbl': // Table case 'w:tbl': // Table
$this->readTable($xmlReader, $node, $section, 'document'); $this->readTable($xmlReader, $node, $section, 'document');
break; break;
case 'w:sectPr': // Last section case 'w:sectPr': // Last section
$settings = $this->readSectionStyle($xmlReader, $node); $settings = $this->readSectionStyle($xmlReader, $node);
$section->setSettings($settings); $section->setSettings($settings);
@ -255,6 +263,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
// $default = ($xmlReader->getAttribute('w:default', $node) == 1); // $default = ($xmlReader->getAttribute('w:default', $node) == 1);
switch ($type) { switch ($type) {
case 'paragraph': case 'paragraph':
$pStyle = $this->readParagraphStyle($xmlReader, $node); $pStyle = $this->readParagraphStyle($xmlReader, $node);
$fStyle = $this->readFontStyle($xmlReader, $node); $fStyle = $this->readFontStyle($xmlReader, $node);
@ -264,12 +273,14 @@ class Word2007 extends AbstractReader implements ReaderInterface
$this->phpWord->addFontStyle($name, $fStyle, $pStyle); $this->phpWord->addFontStyle($name, $fStyle, $pStyle);
} }
break; break;
case 'character': case 'character':
$fStyle = $this->readFontStyle($xmlReader, $node); $fStyle = $this->readFontStyle($xmlReader, $node);
if (!empty($fStyle)) { if (!empty($fStyle)) {
$this->phpWord->addFontStyle($name, $fStyle); $this->phpWord->addFontStyle($name, $fStyle);
} }
break; break;
case 'table': case 'table':
$tStyle = $this->readTableStyle($xmlReader, $node); $tStyle = $this->readTableStyle($xmlReader, $node);
if (!empty($tStyle)) { if (!empty($tStyle)) {
@ -281,6 +292,98 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
} }
/**
* Read numbering.xml
*
* @param string $filename
* @param string $xmlFile
*/
private function readNumbering($filename, $xmlFile)
{
$abstracts = array();
$numberings = array();
$xmlReader = new XMLReader();
$xmlReader->getDomFromZip($filename, $xmlFile);
// Abstract numbering definition
$nodes = $xmlReader->getElements('w:abstractNum');
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$abstractId = $xmlReader->getAttribute('w:abstractNumId', $node);
$abstracts[$abstractId] = array('levels' => array());
$abstract = &$abstracts[$abstractId];
$subnodes = $xmlReader->getElements('*', $node);
foreach ($subnodes as $subnode) {
switch ($subnode->nodeName) {
case 'w:multiLevelType':
$abstract['type'] = $xmlReader->getAttribute('w:val', $subnode);
break;
case 'w:lvl':
$levelId = $xmlReader->getAttribute('w:ilvl', $subnode);
$abstract['levels'][$levelId] = $this->readNumberingLevel($xmlReader, $subnode, $levelId);
break;
}
}
}
}
// Numbering instance definition
$nodes = $xmlReader->getElements('w:num');
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$numId = $xmlReader->getAttribute('w:numId', $node);
$abstractId = $xmlReader->getAttribute('w:val', $node, 'w:abstractNumId');
$numberings[$numId] = $abstracts[$abstractId];
$numberings[$numId]['numId'] = $numId;
$subnodes = $xmlReader->getElements('w:lvlOverride/w:lvl', $node);
foreach ($subnodes as $subnode) {
$levelId = $xmlReader->getAttribute('w:ilvl', $subnode);
$overrides = $this->readNumberingLevel($xmlReader, $subnode, $levelId);
foreach ($overrides as $key => $value) {
$numberings[$numId]['levels'][$levelId][$key] = $value;
}
}
}
}
// Push to Style collection
foreach ($numberings as $numId => $numbering) {
$this->phpWord->addNumberingStyle("PHPWordList{$numId}", $numbering);
}
}
/**
* Read numbering level definition from w:abstractNum and w:num
*
* @param integer $levelId
* @return array
*/
private function readNumberingLevel(XMLReader $xmlReader, \DOMElement $subnode, $levelId)
{
$level = array();
$level['level'] = $levelId;
$level['start'] = $xmlReader->getAttribute('w:val', $subnode, 'w:start');
$level['format'] = $xmlReader->getAttribute('w:val', $subnode, 'w:numFmt');
$level['restart'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlRestart');
$level['suffix'] = $xmlReader->getAttribute('w:val', $subnode, 'w:suff');
$level['text'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlText');
$level['align'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlJc');
$level['tab'] = $xmlReader->getAttribute('w:pos', $subnode, 'w:pPr/w:tabs/w:tab');
$level['left'] = $xmlReader->getAttribute('w:left', $subnode, 'w:pPr/w:ind');
$level['hanging'] = $xmlReader->getAttribute('w:hanging', $subnode, 'w:pPr/w:ind');
$level['font'] = $xmlReader->getAttribute('w:ascii', $subnode, 'w:rPr/w:rFonts');
$level['hint'] = $xmlReader->getAttribute('w:hint', $subnode, 'w:rPr/w:rFonts');
foreach ($level as $key => $value) {
if (is_null($value)) {
unset($level[$key]);
}
}
return $level;
}
/** /**
* Read header footer * Read header footer
* *
@ -325,6 +428,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* *
* @param string $filename * @param string $filename
* @param string $xmlFile * @param string $xmlFile
* @param string $notesType
*/ */
private function readNotes($filename, $xmlFile, $notesType = 'footnotes') private function readNotes($filename, $xmlFile, $notesType = 'footnotes')
{ {
@ -362,7 +466,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* *
* @todo Get font style for preserve text * @todo Get font style for preserve text
*/ */
private function readParagraph(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart) private function readParagraph(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
{ {
// Paragraph style // Paragraph style
$pStyle = null; $pStyle = null;
@ -396,6 +500,17 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
$parent->addPreserveText($textContent, $fStyle, $pStyle); $parent->addPreserveText($textContent, $fStyle, $pStyle);
// List item
} elseif ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) {
$textContent = '';
$numId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:numId');
$levelId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:ilvl');
$nodes = $xmlReader->getElements('w:r', $domNode);
foreach ($nodes as $node) {
$textContent .= $xmlReader->getValue('w:t', $node);
}
$parent->addListItem($textContent, $levelId, null, "PHPWordList{$numId}", $pStyle);
// Text and TextRun // Text and TextRun
} else { } else {
$runCount = $xmlReader->countElements('w:r', $domNode); $runCount = $xmlReader->countElements('w:r', $domNode);
@ -421,13 +536,15 @@ class Word2007 extends AbstractReader implements ReaderInterface
/** /**
* Read w:r * Read w:r
* *
* @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
* @param \DOMElement $domNode
* @param mixed $parent * @param mixed $parent
* @param string $docPart * @param string $docPart
* @param mixed $pStyle * @param mixed $pStyle
* *
* @todo Footnote paragraph style * @todo Footnote paragraph style
*/ */
private function readRun(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart, $pStyle = null) private function readRun(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart, $pStyle = null)
{ {
if (!in_array($domNode->nodeName, array('w:r', 'w:hyperlink'))) { if (!in_array($domNode->nodeName, array('w:r', 'w:hyperlink'))) {
return; return;
@ -484,7 +601,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* @param mixed $parent * @param mixed $parent
* @param string $docPart * @param string $docPart
*/ */
private function readTable(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart) private function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
{ {
// Table style // Table style
$tblStyle = null; $tblStyle = null;
@ -539,7 +656,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* *
* @return array|null * @return array|null
*/ */
private function readSectionStyle(XMLReader $xmlReader, \DOMNode $domNode) private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$ret = null; $ret = null;
$mapping = array( $mapping = array(
@ -554,6 +671,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
$property = $mapping[$node->nodeName]; $property = $mapping[$node->nodeName];
switch ($node->nodeName) { switch ($node->nodeName) {
case 'w:type': case 'w:type':
$ret['breakType'] = $xmlReader->getAttribute('w:val', $node); $ret['breakType'] = $xmlReader->getAttribute('w:val', $node);
break; break;
@ -598,7 +716,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* *
* @return string|array|null * @return string|array|null
*/ */
private function readParagraphStyle(XMLReader $xmlReader, \DOMNode $domNode) private function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$style = null; $style = null;
if ($xmlReader->elementExists('w:pPr', $domNode)) { if ($xmlReader->elementExists('w:pPr', $domNode)) {
@ -620,6 +738,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
$property = $mapping[$node->nodeName]; $property = $mapping[$node->nodeName];
switch ($node->nodeName) { switch ($node->nodeName) {
case 'w:ind': case 'w:ind':
$style['indent'] = $xmlReader->getAttribute('w:left', $node); $style['indent'] = $xmlReader->getAttribute('w:left', $node);
$style['hanging'] = $xmlReader->getAttribute('w:hanging', $node); $style['hanging'] = $xmlReader->getAttribute('w:hanging', $node);
@ -660,7 +779,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* *
* @return string|array|null * @return string|array|null
*/ */
private function readFontStyle(XMLReader $xmlReader, \DOMNode $domNode) private function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$style = null; $style = null;
// Hyperlink has an extra w:r child // Hyperlink has an extra w:r child
@ -686,6 +805,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
$property = $mapping[$node->nodeName]; $property = $mapping[$node->nodeName];
switch ($node->nodeName) { switch ($node->nodeName) {
case 'w:rFonts': case 'w:rFonts':
$style['name'] = $xmlReader->getAttribute('w:ascii', $node); $style['name'] = $xmlReader->getAttribute('w:ascii', $node);
$style['hint'] = $xmlReader->getAttribute('w:hint', $node); $style['hint'] = $xmlReader->getAttribute('w:hint', $node);
@ -729,7 +849,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* @return string|array|null * @return string|array|null
* @todo Capture w:tblStylePr w:type="firstRow" * @todo Capture w:tblStylePr w:type="firstRow"
*/ */
private function readTableStyle(XMLReader $xmlReader, \DOMNode $domNode) private function readTableStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$style = null; $style = null;
$margins = array('top', 'left', 'bottom', 'right'); $margins = array('top', 'left', 'bottom', 'right');
@ -752,6 +872,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
} }
// $property = $mapping[$node->nodeName]; // $property = $mapping[$node->nodeName];
switch ($node->nodeName) { switch ($node->nodeName) {
case 'w:tblCellMar': case 'w:tblCellMar':
foreach ($margins as $side) { foreach ($margins as $side) {
$ucfSide = ucfirst($side); $ucfSide = ucfirst($side);
@ -779,7 +900,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* *
* @return array|null * @return array|null
*/ */
private function readCellStyle(XMLReader $xmlReader, \DOMNode $domNode) private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode)
{ {
$style = null; $style = null;
$mapping = array( $mapping = array(

View File

@ -70,7 +70,7 @@ class XMLReader
* @param string $path * @param string $path
* @return \DOMNodeList * @return \DOMNodeList
*/ */
public function getElements($path, \DOMNode $contextNode = null) public function getElements($path, \DOMElement $contextNode = null)
{ {
if ($this->dom === null) { if ($this->dom === null) {
return array(); return array();
@ -86,9 +86,9 @@ class XMLReader
* Get element * Get element
* *
* @param string $path * @param string $path
* @return \DOMNode|null * @return \DOMElement|null
*/ */
public function getElement($path, \DOMNode $contextNode) public function getElement($path, \DOMElement $contextNode)
{ {
$elements = $this->getElements($path, $contextNode); $elements = $this->getElements($path, $contextNode);
if ($elements->length > 0) { if ($elements->length > 0) {
@ -105,7 +105,7 @@ class XMLReader
* @param string $path * @param string $path
* @return string|null * @return string|null
*/ */
public function getAttribute($attribute, \DOMNode $contextNode, $path = null) public function getAttribute($attribute, \DOMElement $contextNode, $path = null)
{ {
if (is_null($path)) { if (is_null($path)) {
$return = $contextNode->getAttribute($attribute); $return = $contextNode->getAttribute($attribute);
@ -127,7 +127,7 @@ class XMLReader
* @param string $path * @param string $path
* @return string|null * @return string|null
*/ */
public function getValue($path, \DOMNode $contextNode) public function getValue($path, \DOMElement $contextNode)
{ {
$elements = $this->getElements($path, $contextNode); $elements = $this->getElements($path, $contextNode);
if ($elements->length > 0) { if ($elements->length > 0) {
@ -143,7 +143,7 @@ class XMLReader
* @param string $path * @param string $path
* @return integer * @return integer
*/ */
public function countElements($path, \DOMNode $contextNode) public function countElements($path, \DOMElement $contextNode)
{ {
$elements = $this->getElements($path, $contextNode); $elements = $this->getElements($path, $contextNode);
@ -156,7 +156,7 @@ class XMLReader
* @param string $path * @param string $path
* @return boolean * @return boolean
*/ */
public function elementExists($path, \DOMNode $contextNode) public function elementExists($path, \DOMElement $contextNode)
{ {
return $this->getElements($path, $contextNode)->length > 0; return $this->getElements($path, $contextNode)->length > 0;
} }

View File

@ -12,6 +12,7 @@ namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\Style\Table; use PhpOffice\PhpWord\Style\Table;
use PhpOffice\PhpWord\Style\Numbering;
/** /**
* Style collection * Style collection
@ -68,11 +69,7 @@ class Style
*/ */
public static function addTableStyle($styleName, $styleTable, $styleFirstRow = null) public static function addTableStyle($styleName, $styleTable, $styleFirstRow = null)
{ {
if (!array_key_exists($styleName, self::$styles)) { self::setStyleValues($styleName, null, new Table($styleTable, $styleFirstRow));
$style = new Table($styleTable, $styleFirstRow);
self::$styles[$styleName] = $style;
}
} }
/** /**
@ -84,12 +81,36 @@ class Style
*/ */
public static function addTitleStyle($titleCount, $styleFont, $styleParagraph = null) public static function addTitleStyle($titleCount, $styleFont, $styleParagraph = null)
{ {
$styleName = 'Heading_' . $titleCount;
self::setStyleValues("Heading_{$titleCount}", $styleFont, new Font('title', $styleParagraph)); self::setStyleValues("Heading_{$titleCount}", $styleFont, new Font('title', $styleParagraph));
} }
/**
* Add numbering style
*
* @param string $styleName
* @param array $styleValues
* @return Numbering
* @since 0.9.2
*/
public static function addNumberingStyle($styleName, $styleValues)
{
self::setStyleValues($styleName, $styleValues, new Numbering());
}
/**
* Count styles
*
* @return integer
* @since 0.9.2
*/
public static function countStyles()
{
return count(self::$styles);
}
/** /**
* Reset styles * Reset styles
* @since 0.9.2
*/ */
public static function resetStyles() public static function resetStyles()
{ {
@ -120,6 +141,7 @@ class Style
* Get style by name * Get style by name
* *
* @param string $styleName * @param string $styleName
* @return Paragraph|Font|Table|Numbering|null
*/ */
public static function getStyle($styleName) public static function getStyle($styleName)
{ {
@ -131,24 +153,21 @@ class Style
} }
/** /**
* Set style values * Set style values and put it to static style collection
* *
* @param string $styleName * @param string $styleName
* @param array $styleValues * @param array $styleValues
* @param mixed $styleObject * @param Paragraph|Font|Table|Numbering $styleObject
*/ */
private static function setStyleValues($styleName, $styleValues, $styleObject) private static function setStyleValues($styleName, $styleValues, $styleObject)
{ {
if (!array_key_exists($styleName, self::$styles)) { if (!array_key_exists($styleName, self::$styles)) {
if (is_array($styleValues)) { if (is_array($styleValues)) {
foreach ($styleValues as $key => $value) { foreach ($styleValues as $key => $value) {
if (substr($key, 0, 1) == '_') {
$key = substr($key, 1);
}
$styleObject->setStyleValue($key, $value); $styleObject->setStyleValue($key, $value);
} }
} }
$styleObject->setIndex(self::countStyles() + 1); // One based index
self::$styles[$styleName] = $styleObject; self::$styles[$styleName] = $styleObject;
} }
} }

View File

@ -16,6 +16,37 @@ namespace PhpOffice\PhpWord\Style;
*/ */
abstract class AbstractStyle abstract class AbstractStyle
{ {
/**
* Index number in Style collection for named style
*
* This number starts from one and defined in Style::setStyleValues()
*
* @var integer|null
*/
protected $index;
/**
* Get index number
*
* @return integer|null
*/
public function getIndex()
{
return $this->index;
}
/**
* Set index number
*
* @param integer|null $value
*/
public function setIndex($value = null)
{
$this->index = $this->setIntVal($value, $this->index);
return $this;
}
/** /**
* Set style value template method * Set style value template method
* *
@ -23,8 +54,7 @@ abstract class AbstractStyle
* *
* @param string $key * @param string $key
* @param string $value * @param string $value
* * @return self
* @todo Implement type check mechanism, e.g. boolean, integer, enum, defaults
*/ */
public function setStyleValue($key, $value) public function setStyleValue($key, $value)
{ {
@ -39,5 +69,88 @@ abstract class AbstractStyle
if (method_exists($this, $method)) { if (method_exists($this, $method)) {
$this->$method($value); $this->$method($value);
} }
return $this;
}
/**
* Set style by using associative array
*
* @param array $styles
* @return self
*/
public function setStyleByArray($styles = array())
{
foreach ($styles as $key => $value) {
$this->setStyleValue($key, $value);
}
return $this;
}
/**
* Set boolean value
*
* @param mixed $value
* @param boolean|null $default
* @return boolean|null
*/
protected function setBoolVal($value, $default = null)
{
if (!is_bool($value)) {
$value = $default;
}
return $value;
}
/**
* Set integer value
*
* @param mixed $value
* @param integer|null $default
* @return integer|null
*/
protected function setIntVal($value, $default = null)
{
$value = intval($value);
if (!is_int($value)) {
$value = $default;
}
return $value;
}
/**
* Set float value
*
* @param mixed $value
* @param float|null $default
* @return float|null
*/
protected function setFloatVal($value, $default = null)
{
$value = floatval($value);
if (!is_float($value)) {
$value = $default;
}
return $value;
}
/**
* Set enum value
*
* @param mixed $value
* @param array $enum
* @param mixed $default
*/
protected function setEnumVal($value, $enum, $default = null)
{
if (!in_array($value, $enum)) {
$value = $default;
}
return $value;
} }
} }

View File

@ -9,46 +9,240 @@
namespace PhpOffice\PhpWord\Style; namespace PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Numbering;
/** /**
* List item style * List item style
*
* Before version 0.9.2, numbering style is defined statically with $listType.
* After version 0.9.2, numbering style is defined by using Numbering and
* recorded by $numStyle. $listStyle is maintained for backward compatility
*/ */
class ListItem extends AbstractStyle class ListItem extends AbstractStyle
{ {
const TYPE_SQUARE_FILLED = 1;
const TYPE_BULLET_FILLED = 3; // default
const TYPE_BULLET_EMPTY = 5;
const TYPE_NUMBER = 7; const TYPE_NUMBER = 7;
const TYPE_NUMBER_NESTED = 8; const TYPE_NUMBER_NESTED = 8;
const TYPE_ALPHANUM = 9; const TYPE_ALPHANUM = 9;
const TYPE_BULLET_FILLED = 3;
const TYPE_BULLET_EMPTY = 5;
const TYPE_SQUARE_FILLED = 1;
/** /**
* List Type * Legacy list type
*
* @var integer
*/ */
private $listType; private $listType;
/** /**
* Create a new ListItem Style * Numbering style name
*
* @var string
* @since 0.9.2
*/ */
public function __construct() private $numStyle;
{
$this->listType = self::TYPE_BULLET_FILLED;
}
/** /**
* Set List Type * Numbering definition instance ID
* *
* @param int $pValue * @var integer
* @since 0.9.2
*/ */
public function setListType($pValue = self::TYPE_BULLET_FILLED) private $numId;
/**
* Create new instance
*
* @param string $numStyle
*/
public function __construct($numStyle = null)
{ {
$this->listType = $pValue; if (!is_null($numStyle)) {
$this->setNumStyle($numStyle);
} else {
$this->setListType();
}
} }
/** /**
* Get List Type * Get List Type
*
* @return integer
*/ */
public function getListType() public function getListType()
{ {
return $this->listType; return $this->listType;
} }
/**
* Set legacy list type for version < 0.9.2
*
* @param integer $value
*/
public function setListType($value = self::TYPE_BULLET_FILLED)
{
$enum = array(self::TYPE_SQUARE_FILLED, self::TYPE_BULLET_FILLED,
self::TYPE_BULLET_EMPTY, self::TYPE_NUMBER,
self::TYPE_NUMBER_NESTED, self::TYPE_ALPHANUM);
$this->listType = $this->setEnumVal($value, $enum, $this->listType);
$this->getListTypeStyle();
}
/**
* Get numbering style name
*
* @return integer
*/
public function getNumStyle()
{
return $this->numStyle;
}
/**
* Set numbering style name
*
* @param string $value
*/
public function setNumStyle($value)
{
$this->numStyle = $value;
$numStyleObject = Style::getStyle($this->numStyle);
if (!is_null($numStyleObject)) {
$this->numId = $numStyleObject->getIndex();
$numStyleObject->setNumId($this->numId);
}
}
/**
* Get numbering Id
*
* @return integer
*/
public function getNumId()
{
return $this->numId;
}
/**
* Get legacy numbering definition
*
* @param integer $listType
* @return array
* @since 0.9.2
*/
private function getListTypeStyle()
{
// Check if legacy style already registered in global Style collection
$numStyle = "PHPWordList{$this->listType}";
if (!is_null(Style::getStyle($numStyle))) {
$this->setNumStyle($numStyle);
return;
}
// Property mapping for numbering level information
$properties = array('start', 'format', 'text', 'align', 'tabPos', 'left', 'hanging', 'font', 'hint');
// Legacy level information
$listTypeStyles = array(
self::TYPE_SQUARE_FILLED => array(
'type' => 'hybridMultilevel',
'levels' => array(
0 => '1, bullet, , left, 720, 720, 360, Wingdings, default',
1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default',
2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default',
3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default',
4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default',
5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default',
6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default',
7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default',
8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default',
),
),
self::TYPE_BULLET_FILLED => array(
'type' => 'hybridMultilevel',
'levels' => array(
0 => '1, bullet, , left, 720, 720, 360, Symbol, default',
1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default',
2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default',
3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default',
4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default',
5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default',
6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default',
7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default',
8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default',
),
),
self::TYPE_BULLET_EMPTY => array(
'type' => 'hybridMultilevel',
'levels' => array(
0 => '1, bullet, o, left, 720, 720, 360, Courier New, default',
1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default',
2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default',
3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default',
4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default',
5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default',
6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default',
7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default',
8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default',
),
),
self::TYPE_NUMBER => array(
'type' => 'hybridMultilevel',
'levels' => array(
0 => '1, decimal, %1., left, 720, 720, 360, , default',
1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default',
2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default',
3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default',
4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default',
5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default',
6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default',
7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default',
8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default',
),
),
self::TYPE_NUMBER_NESTED => array(
'type' => 'multilevel',
'levels' => array(
0 => '1, decimal, %1., left, 360, 360, 360, , ',
1 => '1, decimal, %1.%2., left, 792, 792, 432, , ',
2 => '1, decimal, %1.%2.%3., left, 1224, 1224, 504, , ',
3 => '1, decimal, %1.%2.%3.%4., left, 1800, 1728, 648, , ',
4 => '1, decimal, %1.%2.%3.%4.%5., left, 2520, 2232, 792, , ',
5 => '1, decimal, %1.%2.%3.%4.%5.%6., left, 2880, 2736, 936, , ',
6 => '1, decimal, %1.%2.%3.%4.%5.%6.%7., left, 3600, 3240, 1080, , ',
7 => '1, decimal, %1.%2.%3.%4.%5.%6.%7.%8., left, 3960, 3744, 1224, , ',
8 => '1, decimal, %1.%2.%3.%4.%5.%6.%7.%8.%9., left, 4680, 4320, 1440, , ',
),
),
self::TYPE_ALPHANUM => array(
'type' => 'multilevel',
'levels' => array(
0 => '1, decimal, %1., left, 720, 720, 360, , ',
1 => '1, lowerLetter, %2., left, 1440, 1440, 360, , ',
2 => '1, lowerRoman, %3., right, 2160, 2160, 180, , ',
3 => '1, decimal, %4., left, 2880, 2880, 360, , ',
4 => '1, lowerLetter, %5., left, 3600, 3600, 360, , ',
5 => '1, lowerRoman, %6., right, 4320, 4320, 180, , ',
6 => '1, decimal, %7., left, 5040, 5040, 360, , ',
7 => '1, lowerLetter, %8., left, 5760, 5760, 360, , ',
8 => '1, lowerRoman, %9., right, 6480, 6480, 180, , ',
),
),
);
// Populate style and register to global Style register
$style = $listTypeStyles[$this->listType];
foreach ($style['levels'] as $key => $value) {
$levelProperties = explode(', ', $value);
$level['level'] = $key;
for ($i = 0; $i < count($properties); $i++) {
$property = $properties[$i];
$level[$property] = $levelProperties[$i];
}
$style['levels'][$key] = $level;
}
Style::addNumberingStyle($numStyle, $style);
$this->setNumStyle($numStyle);
}
} }

View File

@ -0,0 +1,123 @@
<?php
/**
* PHPWord
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2014 PHPWord
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
namespace PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\NumberingLevel;
/**
* Numbering style
*
* @link http://www.schemacentral.com/sc/ooxml/e-w_numbering.html
* @link http://www.schemacentral.com/sc/ooxml/e-w_abstractNum-1.html
* @link http://www.schemacentral.com/sc/ooxml/e-w_num-1.html
* @since 0.9.2
*/
class Numbering extends AbstractStyle
{
/**
* Numbering definition instance ID
*
* @var int
* @link http://www.schemacentral.com/sc/ooxml/e-w_num-1.html
*/
private $numId;
/**
* Multilevel type singleLevel|multilevel|hybridMultilevel
*
* @var string
* @link http://www.schemacentral.com/sc/ooxml/a-w_val-67.html
*/
private $type;
/**
* Numbering levels
*
* @var NumberingLevel[]
*/
private $levels = array();
/**
* Get Id
*
* @return integer
*/
public function getNumId()
{
return $this->numId;
}
/**
* Set Id
*
* @param integer $value
* @return self
*/
public function setNumId($value)
{
$this->numId = $this->setIntVal($value, $this->numId);
return $this;
}
/**
* Get multilevel type
*
* @return string
*/
public function getType()
{
return $this->type;
}
/**
* Set multilevel type
*
* @param string $value
* @return self
*/
public function setType($value)
{
$enum = array('singleLevel', 'multilevel', 'hybridMultilevel');
$this->type = $this->setEnumVal($value, $enum, $this->type);
return $this;
}
/**
* Get levels
*
* @return NumberingLevel[]
*/
public function getLevels()
{
return $this->levels;
}
/**
* Set multilevel type
*
* @param array $values
* @return self
*/
public function setLevels($values)
{
if (is_array($values)) {
foreach ($values as $key => $value) {
$numberingLevel = new NumberingLevel();
if (is_array($value)) {
$numberingLevel->setStyleByArray($value);
$numberingLevel->setLevel($key);
}
$this->levels[$key] = $numberingLevel;
}
}
return $this;
}
}

View File

@ -0,0 +1,378 @@
<?php
/**
* PHPWord
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2014 PHPWord
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
namespace PhpOffice\PhpWord\Style;
/**
* Numbering level definition
*
* @link http://www.schemacentral.com/sc/ooxml/e-w_lvl-1.html
* @since 0.9.2
*/
class NumberingLevel extends AbstractStyle
{
/**
* Level number, 0 to 8 (total 9 levels)
*
* @var integer
*/
private $level = 0;
/**
* Starting value w:start
*
* @var integer
* @link http://www.schemacentral.com/sc/ooxml/e-w_start-1.html
*/
private $start = 1;
/**
* Numbering format bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
*
* @var string
* @link http://www.schemacentral.com/sc/ooxml/t-w_ST_NumberFormat.html
*/
private $format;
/**
* Restart numbering level symbol w:lvlRestart
*
* @var integer
* @link http://www.schemacentral.com/sc/ooxml/e-w_lvlRestart-1.html
*/
private $restart;
/**
* Content between numbering symbol and paragraph text
*
* @var string tab|space|nothing
* @link http://www.schemacentral.com/sc/ooxml/e-w_suff-1.html
*/
private $suffix = 'tab';
/**
* Numbering level text e.g. %1 for nonbullet or bullet character
*
* @var string
* @link http://www.schemacentral.com/sc/ooxml/e-w_lvlText-1.html
*/
private $text;
/**
* Align left|center|right|both
*
* @var string
* @link http://www.schemacentral.com/sc/ooxml/e-w_lvlJc-1.html
*/
private $align;
/**
* Left
*
* @var integer
*/
private $left;
/**
* Hanging
*
* @var integer
*/
private $hanging;
/**
* Tab position
*
* @var integer
*/
private $tabPos;
/**
* Font family
*
* @var string
*/
private $font;
/**
* Hint default|eastAsia|cs
*
* @var string
* @link http://www.schemacentral.com/sc/ooxml/a-w_hint-1.html
*/
private $hint;
/**
* Get level
*
* @return integer
*/
public function getLevel()
{
return $level->level;
}
/**
* Set level
*
* @param integer $value
* @return self
*/
public function setLevel($value)
{
$this->level = $this->setIntVal($value, $this->level);
return $this;
}
/**
* Get start
*
* @return integer
*/
public function getStart()
{
return $this->start;
}
/**
* Set start
*
* @param integer $value
* @return self
*/
public function setStart($value)
{
$this->start = $this->setIntVal($value, $this->start);
return $this;
}
/**
* Get format
*
* @return string
*/
public function getFormat()
{
return $this->format;
}
/**
* Set format
*
* @param string $value
* @return self
*/
public function setFormat($value)
{
$enum = array('bullet', 'decimal', 'upperRoman', 'lowerRoman', 'upperLetter', 'lowerLetter');
$this->format = $this->setEnumVal($value, $enum, $this->format);
return $this;
}
/**
* Get start
*
* @return integer
*/
public function getRestart()
{
return $this->restart;
}
/**
* Set start
*
* @param integer $value
* @return self
*/
public function setRestart($value)
{
$this->restart = $this->setIntVal($value, $this->restart);
return $this;
}
/**
* Get suffix
*
* @return string
*/
public function getSuffix()
{
return $this->suffix;
}
/**
* Set suffix
*
* @param string $value
* @return self
*/
public function setSuffix($value)
{
$enum = array('tab', 'space', 'nothing');
$this->suffix = $this->setEnumVal($value, $enum, $this->suffix);
return $this;
}
/**
* Get text
*
* @return string
*/
public function getText()
{
return $this->text;
}
/**
* Set text
*
* @param string $value
* @return self
*/
public function setText($value)
{
$this->text = $value;
return $this;
}
/**
* Get align
*
* @return string
*/
public function getAlign()
{
return $this->align;
}
/**
* Set align
*
* @param string $value
* @return self
*/
public function setAlign($value)
{
$enum = array('left', 'center', 'right', 'both');
$this->align = $this->setEnumVal($value, $enum, $this->align);
return $this;
}
/**
* Get left
*
* @return integer
*/
public function getLeft()
{
return $this->left;
}
/**
* Set left
*
* @param integer $value
* @return self
*/
public function setLeft($value)
{
$this->left = $this->setIntVal($value, $this->left);
return $this;
}
/**
* Get hanging
*
* @return integer
*/
public function getHanging()
{
return $this->hanging;
}
/**
* Set hanging
*
* @param integer $value
* @return self
*/
public function setHanging($value)
{
$this->hanging = $this->setIntVal($value, $this->hanging);
return $this;
}
/**
* Get tab
*
* @return integer
*/
public function getTabPos()
{
return $this->tabPos;
}
/**
* Set tab
*
* @param integer $value
* @return self
*/
public function setTabPos($value)
{
$this->tabPos = $this->setIntVal($value, $this->tabPos);
return $this;
}
/**
* Get font
*
* @return string
*/
public function getFont()
{
return $this->font;
}
/**
* Set font
*
* @param string $value
* @return self
*/
public function setFont($value)
{
$this->font = $value;
return $this;
}
/**
* Get hint
*
* @return string
*/
public function getHint()
{
return $this->hint;
}
/**
* Set hint
*
* @param string $value
* @return self
*/
public function setHint($value)
{
$enum = array('default', 'eastAsia', 'cs');
$this->hint = $this->setEnumVal($value, $enum, $this->hint);
return $this;
}
}

View File

@ -432,6 +432,6 @@ class Template
*/ */
public function deleteTemplateBlock($blockname, $replacement = '') public function deleteTemplateBlock($blockname, $replacement = '')
{ {
$this->deleteBlock($blockname, $replacement); $this->deleteBlock($blockname);
} }
} }

View File

@ -9,17 +9,18 @@
namespace PhpOffice\PhpWord\Writer; namespace PhpOffice\PhpWord\Writer;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Element\Section;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Writer\Word2007\ContentTypes; use PhpOffice\PhpWord\Writer\Word2007\ContentTypes;
use PhpOffice\PhpWord\Writer\Word2007\Rels;
use PhpOffice\PhpWord\Writer\Word2007\DocProps; use PhpOffice\PhpWord\Writer\Word2007\DocProps;
use PhpOffice\PhpWord\Writer\Word2007\Document; use PhpOffice\PhpWord\Writer\Word2007\Document;
use PhpOffice\PhpWord\Writer\Word2007\Footer; use PhpOffice\PhpWord\Writer\Word2007\Footer;
use PhpOffice\PhpWord\Writer\Word2007\Notes;
use PhpOffice\PhpWord\Writer\Word2007\Header; use PhpOffice\PhpWord\Writer\Word2007\Header;
use PhpOffice\PhpWord\Writer\Word2007\Notes;
use PhpOffice\PhpWord\Writer\Word2007\Numbering;
use PhpOffice\PhpWord\Writer\Word2007\Rels;
use PhpOffice\PhpWord\Writer\Word2007\Styles; use PhpOffice\PhpWord\Writer\Word2007\Styles;
/** /**
@ -57,6 +58,7 @@ class Word2007 extends AbstractWriter implements WriterInterface
$this->writerParts['docprops'] = new DocProps(); $this->writerParts['docprops'] = new DocProps();
$this->writerParts['document'] = new Document(); $this->writerParts['document'] = new Document();
$this->writerParts['styles'] = new Styles(); $this->writerParts['styles'] = new Styles();
$this->writerParts['numbering'] = new Numbering();
$this->writerParts['header'] = new Header(); $this->writerParts['header'] = new Header();
$this->writerParts['footer'] = new Footer(); $this->writerParts['footer'] = new Footer();
$this->writerParts['footnotes'] = new Notes(); $this->writerParts['footnotes'] = new Notes();
@ -97,7 +99,6 @@ class Word2007 extends AbstractWriter implements WriterInterface
$this->addHeaderFooterMedia($objZip, 'footer'); $this->addHeaderFooterMedia($objZip, 'footer');
// Add header/footer contents // 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(); $sections = $this->phpWord->getSections();
foreach ($sections as $section) { foreach ($sections as $section) {
@ -116,9 +117,9 @@ class Word2007 extends AbstractWriter implements WriterInterface
$objZip->addFromString('word/_rels/document.xml.rels', $this->getWriterPart('rels')->writeDocRels($this->docRels)); $objZip->addFromString('word/_rels/document.xml.rels', $this->getWriterPart('rels')->writeDocRels($this->docRels));
$objZip->addFromString('word/document.xml', $this->getWriterPart('document')->writeDocument($this->phpWord)); $objZip->addFromString('word/document.xml', $this->getWriterPart('document')->writeDocument($this->phpWord));
$objZip->addFromString('word/styles.xml', $this->getWriterPart('styles')->writeStyles($this->phpWord)); $objZip->addFromString('word/styles.xml', $this->getWriterPart('styles')->writeStyles($this->phpWord));
$objZip->addFromString('word/numbering.xml', $this->getWriterPart('numbering')->writeNumbering());
// Write static files // Write static files
$objZip->addFile(__DIR__ . '/../_staticDocParts/numbering.xml', 'word/numbering.xml');
$objZip->addFile(__DIR__ . '/../_staticDocParts/settings.xml', 'word/settings.xml'); $objZip->addFile(__DIR__ . '/../_staticDocParts/settings.xml', 'word/settings.xml');
$objZip->addFile(__DIR__ . '/../_staticDocParts/theme1.xml', 'word/theme/theme1.xml'); $objZip->addFile(__DIR__ . '/../_staticDocParts/theme1.xml', 'word/theme/theme1.xml');
$objZip->addFile(__DIR__ . '/../_staticDocParts/webSettings.xml', 'word/webSettings.xml'); $objZip->addFile(__DIR__ . '/../_staticDocParts/webSettings.xml', 'word/webSettings.xml');
@ -224,8 +225,8 @@ class Word2007 extends AbstractWriter implements WriterInterface
* Add footnotes/endnotes * Add footnotes/endnotes
* *
* @param mixed $objZip * @param mixed $objZip
* @param string $elmType
* @param integer $rId * @param integer $rId
* @param string $notesType
*/ */
private function addNotes($objZip, &$rId, $notesType = 'footnote') private function addNotes($objZip, &$rId, $notesType = 'footnote')
{ {

View File

@ -279,7 +279,7 @@ class Base extends AbstractWriterPart
{ {
$textObject = $listItem->getTextObject(); $textObject = $listItem->getTextObject();
$depth = $listItem->getDepth(); $depth = $listItem->getDepth();
$listType = $listItem->getStyle()->getListType(); $numId = $listItem->getStyle()->getNumId();
$styleParagraph = $textObject->getParagraphStyle(); $styleParagraph = $textObject->getParagraphStyle();
$xmlWriter->startElement('w:p'); $xmlWriter->startElement('w:p');
@ -290,7 +290,7 @@ class Base extends AbstractWriterPart
$xmlWriter->writeAttribute('w:val', $depth); $xmlWriter->writeAttribute('w:val', $depth);
$xmlWriter->endElement(); // w:ilvl $xmlWriter->endElement(); // w:ilvl
$xmlWriter->startElement('w:numId'); $xmlWriter->startElement('w:numId');
$xmlWriter->writeAttribute('w:val', $listType); $xmlWriter->writeAttribute('w:val', $numId);
$xmlWriter->endElement(); // w:numId $xmlWriter->endElement(); // w:numId
$xmlWriter->endElement(); // w:numPr $xmlWriter->endElement(); // w:numPr
$xmlWriter->endElement(); // w:pPr $xmlWriter->endElement(); // w:pPr

View File

@ -69,7 +69,7 @@ class Notes extends Base
// Content // Content
foreach ($elements as $element) { foreach ($elements as $element) {
if ($element instanceof Footnote || $element instanceof Endnote) { if ($element instanceof Footnote || $element instanceof Endnote) {
$this->writeNote($xmlWriter, $element, null, $notesTypes); $this->writeNote($xmlWriter, $element, $notesTypes);
} }
} }
@ -83,10 +83,9 @@ class Notes extends Base
* *
* @param XMLWriter $xmlWriter * @param XMLWriter $xmlWriter
* @param Footnote|Endnote $element * @param Footnote|Endnote $element
* @param boolean $withoutP
* @param string $notesTypes * @param string $notesTypes
*/ */
protected function writeNote(XMLWriter $xmlWriter, $element, $withoutP = false, $notesTypes = 'footnotes') protected function writeNote(XMLWriter $xmlWriter, $element, $notesTypes = 'footnotes')
{ {
$isFootnote = ($notesTypes == 'footnotes'); $isFootnote = ($notesTypes == 'footnotes');
$elementNode = $isFootnote ? 'w:footnote' : 'w:endnote'; $elementNode = $isFootnote ? 'w:footnote' : 'w:endnote';

View File

@ -0,0 +1,177 @@
<?php
/**
* PHPWord
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2014 PHPWord
* @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
*/
namespace PhpOffice\PhpWord\Writer\Word2007;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Numbering as NumberingStyle;
use PhpOffice\PhpWord\Style\NumberingLevel;
/**
* Word2007 numbering part writer
*/
class Numbering extends Base
{
/**
* Write word/numbering.xml
*/
public function writeNumbering()
{
$styles = Style::getStyles();
$xmlWriter = $this->getXmlWriter();
$xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
$xmlWriter->startElement('w:numbering');
$xmlWriter->writeAttribute('xmlns:ve', 'http://schemas.openxmlformats.org/markup-compatibility/2006');
$xmlWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office');
$xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$xmlWriter->writeAttribute('xmlns:m', 'http://schemas.openxmlformats.org/officeDocument/2006/math');
$xmlWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml');
$xmlWriter->writeAttribute('xmlns:wp', 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing');
$xmlWriter->writeAttribute('xmlns:w10', 'urn:schemas-microsoft-com:office:word');
$xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main');
$xmlWriter->writeAttribute('xmlns:wne', 'http://schemas.microsoft.com/office/word/2006/wordml');
// Abstract numbering definitions
foreach ($styles as $style) {
if ($style instanceof NumberingStyle) {
$levels = $style->getLevels();
$xmlWriter->startElement('w:abstractNum');
$xmlWriter->writeAttribute('w:abstractNumId', $style->getNumId());
$xmlWriter->startElement('w:nsid');
$xmlWriter->writeAttribute('w:val', $this->getRandomHexNumber());
$xmlWriter->endElement(); // w:nsid
$xmlWriter->startElement('w:multiLevelType');
$xmlWriter->writeAttribute('w:val', $style->getType());
$xmlWriter->endElement(); // w:multiLevelType
if (is_array($levels)) {
foreach ($levels as $levelNum => $levelObject) {
if ($levelObject instanceof NumberingLevel) {
$start = $levelObject->getStart();
$format = $levelObject->getFormat();
$restart = $levelObject->getRestart();
$suffix = $levelObject->getSuffix();
$text = $levelObject->getText();
$align = $levelObject->getAlign();
$tabPos = $levelObject->getTabPos();
$left = $levelObject->getLeft();
$hanging = $levelObject->getHanging();
$font = $levelObject->getFont();
$hint = $levelObject->getHint();
$xmlWriter->startElement('w:lvl');
$xmlWriter->writeAttribute('w:ilvl', $levelNum);
if (!is_null($start)) {
$xmlWriter->startElement('w:start');
$xmlWriter->writeAttribute('w:val', $start);
$xmlWriter->endElement(); // w:start
}
if (!is_null($format)) {
$xmlWriter->startElement('w:numFmt');
$xmlWriter->writeAttribute('w:val', $format);
$xmlWriter->endElement(); // w:numFmt
}
if (!is_null($restart)) {
$xmlWriter->startElement('w:lvlRestart');
$xmlWriter->writeAttribute('w:val', $restart);
$xmlWriter->endElement(); // w:lvlRestart
}
if (!is_null($suffix)) {
$xmlWriter->startElement('w:suff');
$xmlWriter->writeAttribute('w:val', $suffix);
$xmlWriter->endElement(); // w:suff
}
if (!is_null($text)) {
$xmlWriter->startElement('w:lvlText');
$xmlWriter->writeAttribute('w:val', $text);
$xmlWriter->endElement(); // w:start
}
if (!is_null($align)) {
$xmlWriter->startElement('w:lvlJc');
$xmlWriter->writeAttribute('w:val', $align);
$xmlWriter->endElement(); // w:lvlJc
}
if (!is_null($tabPos) || !is_null($left) || !is_null($hanging)) {
$xmlWriter->startElement('w:pPr');
if (!is_null($tabPos)) {
$xmlWriter->startElement('w:tabs');
$xmlWriter->startElement('w:tab');
$xmlWriter->writeAttribute('w:val', 'num');
$xmlWriter->writeAttribute('w:pos', $tabPos);
$xmlWriter->endElement(); // w:tab
$xmlWriter->endElement(); // w:tabs
}
if (!is_null($left) || !is_null($hanging)) {
$xmlWriter->startElement('w:ind');
if (!is_null($left)) {
$xmlWriter->writeAttribute('w:left', $left);
}
if (!is_null($hanging)) {
$xmlWriter->writeAttribute('w:hanging', $hanging);
}
$xmlWriter->endElement(); // w:ind
}
$xmlWriter->endElement(); // w:pPr
}
if (!is_null($font) || !is_null($hint)) {
$xmlWriter->startElement('w:rPr');
$xmlWriter->startElement('w:rFonts');
if (!is_null($font)) {
$xmlWriter->writeAttribute('w:ascii', $font);
$xmlWriter->writeAttribute('w:hAnsi', $font);
$xmlWriter->writeAttribute('w:cs', $font);
}
if (!is_null($hint)) {
$xmlWriter->writeAttribute('w:hint', $hint);
}
$xmlWriter->endElement(); // w:rFonts
$xmlWriter->endElement(); // w:rPr
}
$xmlWriter->endElement(); // w:lvl
}
}
}
$xmlWriter->endElement(); // w:abstractNum
}
}
// Numbering definition instances
foreach ($styles as $style) {
if ($style instanceof NumberingStyle) {
$xmlWriter->startElement('w:num');
$xmlWriter->writeAttribute('w:numId', $style->getNumId());
$xmlWriter->startElement('w:abstractNumId');
$xmlWriter->writeAttribute('w:val', $style->getNumId());
$xmlWriter->endElement(); // w:abstractNumId
$xmlWriter->endElement(); // w:num
}
}
$xmlWriter->endElement();
return $xmlWriter->getData();
}
/**
* Get random hexadecimal number value
*
* @param int $length
* @return string
*/
private function getRandomHexNumber($length = 8)
{
return strtoupper(substr(md5(rand()), 0, $length));
}
}

View File

@ -15,9 +15,12 @@ use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\Style\Table; use PhpOffice\PhpWord\Style\Table;
use PhpOffice\PhpWord\Style\Numbering;
/** /**
* Word2007 styles part writer * Word2007 styles part writer
*
* @todo Do something with the numbering style introduced in 0.9.2
*/ */
class Styles extends Base class Styles extends Base
{ {
@ -38,37 +41,32 @@ class Styles extends Base
// XML header // XML header
$xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
$xmlWriter->startElement('w:styles'); $xmlWriter->startElement('w:styles');
$xmlWriter->writeAttribute( $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
'xmlns:r', $xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main');
'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
);
$xmlWriter->writeAttribute(
'xmlns:w',
'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
);
// Write default styles // Write default styles
$styles = Style::getStyles(); $styles = Style::getStyles();
$this->writeDefaultStyles($xmlWriter, $phpWord, $styles); $this->writeDefaultStyles($xmlWriter, $phpWord, $styles);
// Write other styles
// Write styles
if (count($styles) > 0) { if (count($styles) > 0) {
foreach ($styles as $styleName => $style) { foreach ($styles as $styleName => $style) {
if ($styleName == 'Normal') { if ($styleName == 'Normal') {
continue; continue;
} }
if ($style instanceof Font) { $styleClass = str_replace('PhpOffice\\PhpWord\\Style\\', '', get_class($style));
// Font style
if ($style instanceof Font) {
$paragraphStyle = $style->getParagraphStyle(); $paragraphStyle = $style->getParagraphStyle();
$styleType = $style->getStyleType(); $styleType = $style->getStyleType();
$type = ($styleType == 'title') ? 'paragraph' : 'character'; $type = ($styleType == 'title') ? 'paragraph' : 'character';
if (!is_null($paragraphStyle)) { if (!is_null($paragraphStyle)) {
$type = 'paragraph'; $type = 'paragraph';
} }
$xmlWriter->startElement('w:style'); $xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', $type); $xmlWriter->writeAttribute('w:type', $type);
if ($styleType == 'title') { if ($styleType == 'title') {
$arrStyle = explode('_', $styleName); $arrStyle = explode('_', $styleName);
$styleId = 'Heading' . $arrStyle[1]; $styleId = 'Heading' . $arrStyle[1];
@ -80,11 +78,9 @@ class Styles extends Base
$xmlWriter->writeAttribute('w:val', $styleLink); $xmlWriter->writeAttribute('w:val', $styleLink);
$xmlWriter->endElement(); $xmlWriter->endElement();
} }
$xmlWriter->startElement('w:name'); $xmlWriter->startElement('w:name');
$xmlWriter->writeAttribute('w:val', $styleName); $xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement(); $xmlWriter->endElement();
if (!is_null($paragraphStyle)) { if (!is_null($paragraphStyle)) {
// Point parent style to Normal // Point parent style to Normal
$xmlWriter->startElement('w:basedOn'); $xmlWriter->startElement('w:basedOn');
@ -94,19 +90,17 @@ class Styles extends Base
} }
$this->writeFontStyle($xmlWriter, $style); $this->writeFontStyle($xmlWriter, $style);
$xmlWriter->endElement(); $xmlWriter->endElement();
// Paragraph style
} elseif ($style instanceof Paragraph) { } elseif ($style instanceof Paragraph) {
$xmlWriter->startElement('w:style'); $xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', 'paragraph'); $xmlWriter->writeAttribute('w:type', 'paragraph');
$xmlWriter->writeAttribute('w:customStyle', '1'); $xmlWriter->writeAttribute('w:customStyle', '1');
$xmlWriter->writeAttribute('w:styleId', $styleName); $xmlWriter->writeAttribute('w:styleId', $styleName);
$xmlWriter->startElement('w:name'); $xmlWriter->startElement('w:name');
$xmlWriter->writeAttribute('w:val', $styleName); $xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement(); $xmlWriter->endElement();
// Parent style // Parent style
$basedOn = $style->getBasedOn(); $basedOn = $style->getBasedOn();
if (!is_null($basedOn)) { if (!is_null($basedOn)) {
@ -114,7 +108,6 @@ class Styles extends Base
$xmlWriter->writeAttribute('w:val', $basedOn); $xmlWriter->writeAttribute('w:val', $basedOn);
$xmlWriter->endElement(); $xmlWriter->endElement();
} }
// Next paragraph style // Next paragraph style
$next = $style->getNext(); $next = $style->getNext();
if (!is_null($next)) { if (!is_null($next)) {
@ -126,22 +119,20 @@ class Styles extends Base
$this->writeParagraphStyle($xmlWriter, $style); $this->writeParagraphStyle($xmlWriter, $style);
$xmlWriter->endElement(); $xmlWriter->endElement();
// Table style
} elseif ($style instanceof Table) { } elseif ($style instanceof Table) {
$xmlWriter->startElement('w:style'); $xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', 'table'); $xmlWriter->writeAttribute('w:type', 'table');
$xmlWriter->writeAttribute('w:customStyle', '1'); $xmlWriter->writeAttribute('w:customStyle', '1');
$xmlWriter->writeAttribute('w:styleId', $styleName); $xmlWriter->writeAttribute('w:styleId', $styleName);
$xmlWriter->startElement('w:name'); $xmlWriter->startElement('w:name');
$xmlWriter->writeAttribute('w:val', $styleName); $xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement(); $xmlWriter->endElement();
$xmlWriter->startElement('w:uiPriority'); $xmlWriter->startElement('w:uiPriority');
$xmlWriter->writeAttribute('w:val', '99'); $xmlWriter->writeAttribute('w:val', '99');
$xmlWriter->endElement(); $xmlWriter->endElement();
$this->writeTableStyle($xmlWriter, $style); $this->writeTableStyle($xmlWriter, $style);
$xmlWriter->endElement(); // w:style $xmlWriter->endElement(); // w:style
} }
} }
@ -149,7 +140,6 @@ class Styles extends Base
$xmlWriter->endElement(); // w:styles $xmlWriter->endElement(); // w:styles
// Return
return $xmlWriter->getData(); return $xmlWriter->getData();
} }

File diff suppressed because one or more lines are too long

View File

@ -99,12 +99,12 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
$objectSrc = __DIR__ . "/../../_files/documents/sheet.xls"; $objectSrc = __DIR__ . "/../../_files/documents/sheet.xls";
$phpWord = new PhpWord(); $phpWord = new PhpWord();
$phpWord->addParagraphStyle('pStyle', array('align' => 'center')); $phpWord->addParagraphStyle('pStyle', array('align' => 'center')); // Style #1
$phpWord->addFontStyle('fStyle', array('size' => '20')); $phpWord->addFontStyle('fStyle', array('size' => '20')); // Style #2
$phpWord->addTitleStyle(1, array('color' => '333333', 'bold' => true)); $phpWord->addTitleStyle(1, array('color' => '333333', 'bold' => true)); // Style #3
$fontStyle = new Font('text', array('align' => 'center')); $fontStyle = new Font('text', array('align' => 'center'));
$section = $phpWord->addSection(); $section = $phpWord->addSection();
$section->addListItem('List Item', 0, null, null, 'pStyle'); $section->addListItem('List Item', 0, null, null, 'pStyle'); // Style #4
$section->addObject($objectSrc, array('align' => 'center')); $section->addObject($objectSrc, array('align' => 'center'));
$section->addTOC($fontStyle); $section->addTOC($fontStyle);
$section->addTitle('Title 1', 1); $section->addTitle('Title 1', 1);
@ -113,7 +113,7 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
// List item // List item
$element = $doc->getElement('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId'); $element = $doc->getElement('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId');
$this->assertEquals(3, $element->getAttribute('w:val')); $this->assertEquals(4, $element->getAttribute('w:val'));
// Object // Object
$element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:object/o:OLEObject'); $element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:object/o:OLEObject');