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
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
@ -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
- Media: Add `Media::resetElements()` to reset all media data - @juzi GH-19
- General: Add `Style::resetStyles()`, `Footnote::resetElements()`, and `TOC::resetTitles()` - @ivanlanin GH-187
- Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table - @ivanlanin
- Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table, and list - @ivanlanin
- Endnote: Ability to add endnotes - @ivanlanin
- ListItem: Ability to create custom list and reset list number - @ivanlanin GH-10
### Bugfixes

View File

@ -8,41 +8,52 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord();
// Begin code
$section = $phpWord->addSection();
// Add listitem elements
$section->addListItem('List Item 1', 0);
$section->addListItem('List Item 2', 0);
$section->addListItem('List Item 3', 0);
$section->addTextBreak(2);
// Style definition
// 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->addParagraphStyle('P-Style', array('spaceAfter'=>95));
$listStyle = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER_NESTED);
$section->addListItem('List Item 1', 0, 'myOwnStyle', $listStyle, 'P-Style');
$section->addListItem('List Item 2', 0, 'myOwnStyle', $listStyle, 'P-Style');
$section->addListItem('List Item 3', 1, 'myOwnStyle', $listStyle, 'P-Style');
$section->addListItem('List Item 4', 1, 'myOwnStyle', $listStyle, 'P-Style');
$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');
$phpWord->addNumberingStyle(
'multilevel',
array('type' => 'multilevel', 'levels' => array(
array('format' => 'decimal', 'text' => '%1.', 'left' => 360, 'hanging' => 360, 'tabPos' => 360),
array('format' => 'upperLetter', 'text' => '%2.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720),
)
)
);
$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
$name = basename(__FILE__, '.php');

View File

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

View File

@ -44,15 +44,21 @@ class ListItem extends AbstractElement
*
* @param string $text
* @param int $depth
* @param mixed $styleFont
* @param mixed $styleList
* @param mixed $styleParagraph
* @param mixed $fontStyle
* @param array|string|null $listStyle
* @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->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;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Element\Endnote;
/**

View File

@ -214,6 +214,17 @@ class PhpWord
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
*

View File

@ -11,8 +11,6 @@ namespace PhpOffice\PhpWord\Reader;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Footnote;
use PhpOffice\PhpWord\Endnotes;
use PhpOffice\PhpWord\DocumentProperties;
use PhpOffice\PhpWord\Shared\XMLReader;
use PhpOffice\PhpWord\Element\Section;
@ -52,6 +50,19 @@ class Word2007 extends AbstractReader implements ReaderInterface
$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
foreach ($this->rels['main'] as $rId => $rel) {
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) {
switch ($rel['type']) {
case 'styles':
$this->readStyles($filename, $rel['target']);
break;
case 'footnotes':
case 'endnotes':
$this->readNotes($filename, $rel['target'], $rel['type']);
@ -178,7 +184,6 @@ class Word2007 extends AbstractReader implements ReaderInterface
$nodes = $xmlReader->getElements('*');
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$nodeName = $node->nodeName;
$propertyName = $xmlReader->getAttribute('name', $node);
$attributeNode = $xmlReader->getElement('*', $node);
$attributeType = $attributeNode->nodeName;
@ -206,6 +211,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
$section = $this->phpWord->addSection();
foreach ($nodes as $node) {
switch ($node->nodeName) {
case 'w:p': // Paragraph
if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') {
$section->addPageBreak(); // PageBreak
@ -221,9 +227,11 @@ class Word2007 extends AbstractReader implements ReaderInterface
$section = $this->phpWord->addSection();
}
break;
case 'w:tbl': // Table
$this->readTable($xmlReader, $node, $section, 'document');
break;
case 'w:sectPr': // Last section
$settings = $this->readSectionStyle($xmlReader, $node);
$section->setSettings($settings);
@ -255,6 +263,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
// $default = ($xmlReader->getAttribute('w:default', $node) == 1);
switch ($type) {
case 'paragraph':
$pStyle = $this->readParagraphStyle($xmlReader, $node);
$fStyle = $this->readFontStyle($xmlReader, $node);
@ -264,12 +273,14 @@ class Word2007 extends AbstractReader implements ReaderInterface
$this->phpWord->addFontStyle($name, $fStyle, $pStyle);
}
break;
case 'character':
$fStyle = $this->readFontStyle($xmlReader, $node);
if (!empty($fStyle)) {
$this->phpWord->addFontStyle($name, $fStyle);
}
break;
case 'table':
$tStyle = $this->readTableStyle($xmlReader, $node);
if (!empty($tStyle)) {
@ -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
*
@ -325,6 +428,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
*
* @param string $filename
* @param string $xmlFile
* @param string $notesType
*/
private function readNotes($filename, $xmlFile, $notesType = 'footnotes')
{
@ -362,7 +466,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
*
* @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
$pStyle = null;
@ -396,6 +500,17 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
$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
} else {
$runCount = $xmlReader->countElements('w:r', $domNode);
@ -421,13 +536,15 @@ class Word2007 extends AbstractReader implements ReaderInterface
/**
* Read w:r
*
* @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader
* @param \DOMElement $domNode
* @param mixed $parent
* @param string $docPart
* @param mixed $pStyle
*
* @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'))) {
return;
@ -484,7 +601,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* @param mixed $parent
* @param string $docPart
*/
private function readTable(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart)
private function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart)
{
// Table style
$tblStyle = null;
@ -539,7 +656,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
*
* @return array|null
*/
private function readSectionStyle(XMLReader $xmlReader, \DOMNode $domNode)
private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
$ret = null;
$mapping = array(
@ -554,6 +671,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
$property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:type':
$ret['breakType'] = $xmlReader->getAttribute('w:val', $node);
break;
@ -598,7 +716,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
*
* @return string|array|null
*/
private function readParagraphStyle(XMLReader $xmlReader, \DOMNode $domNode)
private function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
$style = null;
if ($xmlReader->elementExists('w:pPr', $domNode)) {
@ -620,6 +738,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
$property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:ind':
$style['indent'] = $xmlReader->getAttribute('w:left', $node);
$style['hanging'] = $xmlReader->getAttribute('w:hanging', $node);
@ -660,7 +779,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
*
* @return string|array|null
*/
private function readFontStyle(XMLReader $xmlReader, \DOMNode $domNode)
private function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
$style = null;
// Hyperlink has an extra w:r child
@ -686,6 +805,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
$property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:rFonts':
$style['name'] = $xmlReader->getAttribute('w:ascii', $node);
$style['hint'] = $xmlReader->getAttribute('w:hint', $node);
@ -729,7 +849,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* @return string|array|null
* @todo Capture w:tblStylePr w:type="firstRow"
*/
private function readTableStyle(XMLReader $xmlReader, \DOMNode $domNode)
private function readTableStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
$style = null;
$margins = array('top', 'left', 'bottom', 'right');
@ -752,6 +872,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
// $property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:tblCellMar':
foreach ($margins as $side) {
$ucfSide = ucfirst($side);
@ -779,7 +900,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
*
* @return array|null
*/
private function readCellStyle(XMLReader $xmlReader, \DOMNode $domNode)
private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode)
{
$style = null;
$mapping = array(

View File

@ -70,7 +70,7 @@ class XMLReader
* @param string $path
* @return \DOMNodeList
*/
public function getElements($path, \DOMNode $contextNode = null)
public function getElements($path, \DOMElement $contextNode = null)
{
if ($this->dom === null) {
return array();
@ -86,9 +86,9 @@ class XMLReader
* Get element
*
* @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);
if ($elements->length > 0) {
@ -105,7 +105,7 @@ class XMLReader
* @param string $path
* @return string|null
*/
public function getAttribute($attribute, \DOMNode $contextNode, $path = null)
public function getAttribute($attribute, \DOMElement $contextNode, $path = null)
{
if (is_null($path)) {
$return = $contextNode->getAttribute($attribute);
@ -127,7 +127,7 @@ class XMLReader
* @param string $path
* @return string|null
*/
public function getValue($path, \DOMNode $contextNode)
public function getValue($path, \DOMElement $contextNode)
{
$elements = $this->getElements($path, $contextNode);
if ($elements->length > 0) {
@ -143,7 +143,7 @@ class XMLReader
* @param string $path
* @return integer
*/
public function countElements($path, \DOMNode $contextNode)
public function countElements($path, \DOMElement $contextNode)
{
$elements = $this->getElements($path, $contextNode);
@ -156,7 +156,7 @@ class XMLReader
* @param string $path
* @return boolean
*/
public function elementExists($path, \DOMNode $contextNode)
public function elementExists($path, \DOMElement $contextNode)
{
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\Paragraph;
use PhpOffice\PhpWord\Style\Table;
use PhpOffice\PhpWord\Style\Numbering;
/**
* Style collection
@ -68,11 +69,7 @@ class Style
*/
public static function addTableStyle($styleName, $styleTable, $styleFirstRow = null)
{
if (!array_key_exists($styleName, self::$styles)) {
$style = new Table($styleTable, $styleFirstRow);
self::$styles[$styleName] = $style;
}
self::setStyleValues($styleName, null, new Table($styleTable, $styleFirstRow));
}
/**
@ -84,12 +81,36 @@ class Style
*/
public static function addTitleStyle($titleCount, $styleFont, $styleParagraph = null)
{
$styleName = 'Heading_' . $titleCount;
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
* @since 0.9.2
*/
public static function resetStyles()
{
@ -120,6 +141,7 @@ class Style
* Get style by name
*
* @param string $styleName
* @return Paragraph|Font|Table|Numbering|null
*/
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 array $styleValues
* @param mixed $styleObject
* @param Paragraph|Font|Table|Numbering $styleObject
*/
private static function setStyleValues($styleName, $styleValues, $styleObject)
{
if (!array_key_exists($styleName, self::$styles)) {
if (is_array($styleValues)) {
foreach ($styleValues as $key => $value) {
if (substr($key, 0, 1) == '_') {
$key = substr($key, 1);
}
$styleObject->setStyleValue($key, $value);
}
}
$styleObject->setIndex(self::countStyles() + 1); // One based index
self::$styles[$styleName] = $styleObject;
}
}

View File

@ -16,6 +16,37 @@ namespace PhpOffice\PhpWord\Style;
*/
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
*
@ -23,8 +54,7 @@ abstract class AbstractStyle
*
* @param string $key
* @param string $value
*
* @todo Implement type check mechanism, e.g. boolean, integer, enum, defaults
* @return self
*/
public function setStyleValue($key, $value)
{
@ -39,5 +69,88 @@ abstract class AbstractStyle
if (method_exists($this, $method)) {
$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;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Numbering;
/**
* 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
{
const TYPE_SQUARE_FILLED = 1;
const TYPE_BULLET_FILLED = 3; // default
const TYPE_BULLET_EMPTY = 5;
const TYPE_NUMBER = 7;
const TYPE_NUMBER_NESTED = 8;
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;
/**
* Create a new ListItem Style
* Numbering style name
*
* @var string
* @since 0.9.2
*/
public function __construct()
{
$this->listType = self::TYPE_BULLET_FILLED;
}
private $numStyle;
/**
* 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
*
* @return integer
*/
public function getListType()
{
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 = '')
{
$this->deleteBlock($blockname, $replacement);
$this->deleteBlock($blockname);
}
}

View File

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

View File

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

View File

@ -69,7 +69,7 @@ class Notes extends Base
// Content
foreach ($elements as $element) {
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 Footnote|Endnote $element
* @param boolean $withoutP
* @param string $notesTypes
*/
protected function writeNote(XMLWriter $xmlWriter, $element, $withoutP = false, $notesTypes = 'footnotes')
protected function writeNote(XMLWriter $xmlWriter, $element, $notesTypes = 'footnotes')
{
$isFootnote = ($notesTypes == 'footnotes');
$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\Paragraph;
use PhpOffice\PhpWord\Style\Table;
use PhpOffice\PhpWord\Style\Numbering;
/**
* Word2007 styles part writer
*
* @todo Do something with the numbering style introduced in 0.9.2
*/
class Styles extends Base
{
@ -38,37 +41,32 @@ class Styles extends Base
// XML header
$xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
$xmlWriter->startElement('w:styles');
$xmlWriter->writeAttribute(
'xmlns:r',
'http://schemas.openxmlformats.org/officeDocument/2006/relationships'
);
$xmlWriter->writeAttribute(
'xmlns:w',
'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
);
$xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
$xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main');
// Write default styles
$styles = Style::getStyles();
$this->writeDefaultStyles($xmlWriter, $phpWord, $styles);
// Write other styles
// Write styles
if (count($styles) > 0) {
foreach ($styles as $styleName => $style) {
if ($styleName == 'Normal') {
continue;
}
if ($style instanceof Font) {
$styleClass = str_replace('PhpOffice\\PhpWord\\Style\\', '', get_class($style));
// Font style
if ($style instanceof Font) {
$paragraphStyle = $style->getParagraphStyle();
$styleType = $style->getStyleType();
$type = ($styleType == 'title') ? 'paragraph' : 'character';
if (!is_null($paragraphStyle)) {
$type = 'paragraph';
}
$xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', $type);
if ($styleType == 'title') {
$arrStyle = explode('_', $styleName);
$styleId = 'Heading' . $arrStyle[1];
@ -80,11 +78,9 @@ class Styles extends Base
$xmlWriter->writeAttribute('w:val', $styleLink);
$xmlWriter->endElement();
}
$xmlWriter->startElement('w:name');
$xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement();
if (!is_null($paragraphStyle)) {
// Point parent style to Normal
$xmlWriter->startElement('w:basedOn');
@ -94,19 +90,17 @@ class Styles extends Base
}
$this->writeFontStyle($xmlWriter, $style);
$xmlWriter->endElement();
// Paragraph style
} elseif ($style instanceof Paragraph) {
$xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', 'paragraph');
$xmlWriter->writeAttribute('w:customStyle', '1');
$xmlWriter->writeAttribute('w:styleId', $styleName);
$xmlWriter->startElement('w:name');
$xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement();
// Parent style
$basedOn = $style->getBasedOn();
if (!is_null($basedOn)) {
@ -114,7 +108,6 @@ class Styles extends Base
$xmlWriter->writeAttribute('w:val', $basedOn);
$xmlWriter->endElement();
}
// Next paragraph style
$next = $style->getNext();
if (!is_null($next)) {
@ -126,22 +119,20 @@ class Styles extends Base
$this->writeParagraphStyle($xmlWriter, $style);
$xmlWriter->endElement();
// Table style
} elseif ($style instanceof Table) {
$xmlWriter->startElement('w:style');
$xmlWriter->writeAttribute('w:type', 'table');
$xmlWriter->writeAttribute('w:customStyle', '1');
$xmlWriter->writeAttribute('w:styleId', $styleName);
$xmlWriter->startElement('w:name');
$xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement();
$xmlWriter->startElement('w:uiPriority');
$xmlWriter->writeAttribute('w:val', '99');
$xmlWriter->endElement();
$this->writeTableStyle($xmlWriter, $style);
$xmlWriter->endElement(); // w:style
}
}
@ -149,7 +140,6 @@ class Styles extends Base
$xmlWriter->endElement(); // w:styles
// Return
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";
$phpWord = new PhpWord();
$phpWord->addParagraphStyle('pStyle', array('align' => 'center'));
$phpWord->addFontStyle('fStyle', array('size' => '20'));
$phpWord->addTitleStyle(1, array('color' => '333333', 'bold' => true));
$phpWord->addParagraphStyle('pStyle', array('align' => 'center')); // Style #1
$phpWord->addFontStyle('fStyle', array('size' => '20')); // Style #2
$phpWord->addTitleStyle(1, array('color' => '333333', 'bold' => true)); // Style #3
$fontStyle = new Font('text', array('align' => 'center'));
$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->addTOC($fontStyle);
$section->addTitle('Title 1', 1);
@ -113,7 +113,7 @@ class DocumentTest extends \PHPUnit_Framework_TestCase
// List item
$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
$element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:object/o:OLEObject');