diff --git a/CHANGELOG.md b/CHANGELOG.md index 2325b587..7f4e6cf0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four - Element: New `Field` element - @basjan GH-251 - RTF Reader: Basic RTF reader - @ivanlanin GH-72 GH-252 - Element: New `Line` element - @basjan GH-253 +- Title: Ability to apply numbering in heading - @ivanlanin GH-193 ### Bugfixes @@ -39,6 +40,7 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four - Conversion: Fix conversion from cm to pixel, pixel to cm, and pixel to point - @basjan GH-233 GH-234 - PageBreak: Page break adds new line in the beginning of the new page - @ivanlanin GH-150 - Image: `marginLeft` and `marginTop` cannot accept float value - @ivanlanin GH-248 +- Title: Orphan `w:fldChar` caused OpenOffice to crash when opening DOCX - @ivanlanin GH-236 ### Deprecated @@ -61,6 +63,9 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3. Four - Docs: Show code quality and test code coverage badge on README - Style: Change behaviour of `set...` function of boolean properties; when none is defined, assumed true - @ivanlanin - Shared: Unify PHP ZipArchive and PCLZip features into PhpWord ZipArchive - @ivanlanin +- Docs: Create VERSION file - @ivanlanin +- QA: Improve dan update requirement check in `samples` folder - @ivanlanin + ## 0.10.1 - 21 May 2014 diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index c504e653..1aacd44f 100644 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -22,6 +22,30 @@ use PhpOffice\PhpWord\Shared\String; /** * Paragraph style + * + * OOXML: + * - General: alignment, outline level + * - Indentation: left, right, firstline, hanging + * - Spacing: before, after, line spacing + * - Pagination: widow control, keep next, keep line, page break before + * - Formatting exception: suppress line numbers, don't hyphenate + * - Textbox options + * - Tabs + * - Shading + * - Borders + * + * OpenOffice: + * - Indents & spacing + * - Alignment + * - Text flow + * - Outline & numbering + * - Tabs + * - Dropcaps + * - Tabs + * - Borders + * - Background + * + * @link http://www.schemacentral.com/sc/ooxml/t-w_CT_PPr.html */ class Paragraph extends AbstractStyle { @@ -37,20 +61,6 @@ class Paragraph extends AbstractStyle */ protected $aliases = array('line-height' => 'lineHeight'); - /** - * Text line height - * - * @var int - */ - private $lineHeight; - - /** - * Set of Custom Tab Stops - * - * @var \PhpOffice\PhpWord\Style\Tab[] - */ - private $tabs = array(); - /** * Parent style * @@ -65,6 +75,34 @@ class Paragraph extends AbstractStyle */ private $next; + /** + * Alignment + * + * @var \PhpOffice\PhpWord\Style\Alignment + */ + private $alignment; + + /** + * Indentation + * + * @var \PhpOffice\PhpWord\Style\Indentation + */ + private $indentation; + + /** + * Spacing + * + * @var \PhpOffice\PhpWord\Style\Spacing + */ + private $spacing; + + /** + * Text line height + * + * @var int + */ + private $lineHeight; + /** * Allow first/last line to display on a separate page * @@ -93,27 +131,6 @@ class Paragraph extends AbstractStyle */ private $pageBreakBefore = false; - /** - * Indentation - * - * @var \PhpOffice\PhpWord\Style\Indentation - */ - private $indentation; - - /** - * Spacing - * - * @var \PhpOffice\PhpWord\Style\Spacing - */ - private $spacing; - - /** - * Alignment - * - * @var \PhpOffice\PhpWord\Style\Alignment - */ - private $alignment; - /** * Numbering style name * @@ -128,6 +145,13 @@ class Paragraph extends AbstractStyle */ private $numLevel = 0; + /** + * Set of Custom Tab Stops + * + * @var \PhpOffice\PhpWord\Style\Tab[] + */ + private $tabs = array(); + /** * Create new instance */ @@ -155,6 +179,38 @@ class Paragraph extends AbstractStyle return parent::setStyleValue($key, $value); } + /** + * Get style values + * + * An experiment to retrieve all style values in one function. This will + * reduce function call and increase cohesion between functions. Should be + * implemented in all styles. + * + * @return array + */ + public function getStyleValues() + { + return array( + 'name' => $this->getStyleName(), + 'basedOn' => $this->getBasedOn(), + 'next' => $this->getNext(), + 'alignment' => $this->getAlign(), + 'indentation' => $this->getIndentation(), + 'spacing' => $this->getSpace(), + 'pagination' => array( + 'widowControl' => $this->hasWidowControl(), + 'keepNext' => $this->isKeepNext(), + 'keepLines' => $this->isKeepLines(), + 'pageBreak' => $this->hasPageBreakBefore(), + ), + 'numbering' => array( + 'style' => $this->getNumStyle(), + 'level' => $this->getNumLevel(), + ), + 'tabs' => $this->getTabs(), + ); + } + /** * Get alignment * @@ -178,6 +234,150 @@ class Paragraph extends AbstractStyle return $this; } + /** + * Get parent style ID + * + * @return string + */ + public function getBasedOn() + { + return $this->basedOn; + } + + /** + * Set parent style ID + * + * @param string $value + * @return self + */ + public function setBasedOn($value = 'Normal') + { + $this->basedOn = $value; + + return $this; + } + + /** + * Get style for next paragraph + * + * @return string + */ + public function getNext() + { + return $this->next; + } + + /** + * Set style for next paragraph + * + * @param string $value + * @return self + */ + public function setNext($value = null) + { + $this->next = $value; + + return $this; + } + + /** + * Get shading + * + * @return \PhpOffice\PhpWord\Style\Indentation + */ + public function getIndentation() + { + return $this->indentation; + } + + /** + * Set shading + * + * @param mixed $value + * @return self + */ + public function setIndentation($value = null) + { + $this->setObjectVal($value, 'Indentation', $this->indentation); + + return $this; + } + + /** + * Get indentation + * + * @return int + */ + public function getIndent() + { + if ($this->indentation !== null) { + return $this->indentation->getLeft(); + } else { + return null; + } + } + + /** + * Set indentation + * + * @param int $value + * @return self + */ + public function setIndent($value = null) + { + return $this->setIndentation(array('left' => $value)); + } + + /** + * Get hanging + * + * @return int + */ + public function getHanging() + { + if ($this->indentation !== null) { + return $this->indentation->getHanging(); + } else { + return null; + } + } + + /** + * Set hanging + * + * @param int $value + * @return self + */ + public function setHanging($value = null) + { + return $this->setIndentation(array('hanging' => $value)); + } + + /** + * Get spacing + * + * @return \PhpOffice\PhpWord\Style\Spacing + * @todo Rename to getSpacing in 1.0 + */ + public function getSpace() + { + return $this->spacing; + } + + /** + * Set spacing + * + * @param mixed $value + * @return self + * @todo Rename to setSpacing in 1.0 + */ + public function setSpace($value = null) + { + $this->setObjectVal($value, 'Spacing', $this->spacing); + + return $this; + } + /** * Get space before paragraph * @@ -285,127 +485,6 @@ class Paragraph extends AbstractStyle return $this; } - /** - * Get indentation - * - * @return int - */ - public function getIndent() - { - if ($this->indentation !== null) { - return $this->indentation->getLeft(); - } else { - return null; - } - } - - /** - * Set indentation - * - * @param int $value - * @return self - */ - public function setIndent($value = null) - { - return $this->setIndentation(array('left' => $value)); - } - - /** - * Get hanging - * - * @return int - */ - public function getHanging() - { - if ($this->indentation !== null) { - return $this->indentation->getHanging(); - } else { - return null; - } - } - - /** - * Set hanging - * - * @param int $value - * @return self - */ - public function setHanging($value = null) - { - return $this->setIndentation(array('hanging' => $value)); - } - - /** - * Get tabs - * - * @return \PhpOffice\PhpWord\Style\Tab[] - */ - public function getTabs() - { - return $this->tabs; - } - - /** - * Set tabs - * - * @param array $value - * @return self - */ - public function setTabs($value = null) - { - if (is_array($value)) { - $this->tabs = $value; - } - - return $this; - } - - /** - * Get parent style ID - * - * @return string - */ - public function getBasedOn() - { - return $this->basedOn; - } - - /** - * Set parent style ID - * - * @param string $value - * @return self - */ - public function setBasedOn($value = 'Normal') - { - $this->basedOn = $value; - - return $this; - } - - /** - * Get style for next paragraph - * - * @return string - */ - public function getNext() - { - return $this->next; - } - - /** - * Set style for next paragraph - * - * @param string $value - * @return self - */ - public function setNext($value = null) - { - $this->next = $value; - - return $this; - } - /** * Get allow first/last line to display on a separate page setting * @@ -498,54 +577,6 @@ class Paragraph extends AbstractStyle return $this; } - /** - * Get shading - * - * @return \PhpOffice\PhpWord\Style\Indentation - */ - public function getIndentation() - { - return $this->indentation; - } - - /** - * Set shading - * - * @param mixed $value - * @return self - */ - public function setIndentation($value = null) - { - $this->setObjectVal($value, 'Indentation', $this->indentation); - - return $this; - } - - /** - * Get shading - * - * @return \PhpOffice\PhpWord\Style\Spacing - * @todo Rename to getSpacing in 1.0 - */ - public function getSpace() - { - return $this->spacing; - } - - /** - * Set shading - * - * @param mixed $value - * @return self - * @todo Rename to setSpacing in 1.0 - */ - public function setSpace($value = null) - { - $this->setObjectVal($value, 'Spacing', $this->spacing); - - return $this; - } - /** * Get numbering style name * @@ -592,6 +623,31 @@ class Paragraph extends AbstractStyle return $this; } + /** + * Get tabs + * + * @return \PhpOffice\PhpWord\Style\Tab[] + */ + public function getTabs() + { + return $this->tabs; + } + + /** + * Set tabs + * + * @param array $value + * @return self + */ + public function setTabs($value = null) + { + if (is_array($value)) { + $this->tabs = $value; + } + + return $this; + } + /** * Get allow first/last line to display on a separate page setting * diff --git a/src/PhpWord/Writer/Word2007/Element/TOC.php b/src/PhpWord/Writer/Word2007/Element/TOC.php index 36ef4866..1db2efdd 100644 --- a/src/PhpWord/Writer/Word2007/Element/TOC.php +++ b/src/PhpWord/Writer/Word2007/Element/TOC.php @@ -74,7 +74,7 @@ class TOC extends AbstractElement $tocStyle = $element->getStyleTOC(); $fontStyle = $element->getStyleFont(); $isObject = ($fontStyle instanceof Font) ? true : false; - $anchor = '_Toc' . ($title->getRelationId() + 252634154); + $rId = $title->getRelationId(); $indent = ($title->getDepth() - 1) * $tocStyle->getIndent(); $xmlWriter->startElement('w:p'); @@ -87,7 +87,7 @@ class TOC extends AbstractElement // Hyperlink $xmlWriter->startElement('w:hyperlink'); - $xmlWriter->writeAttribute('w:anchor', $anchor); + $xmlWriter->writeAttribute('w:anchor', "_Toc{$rId}"); $xmlWriter->writeAttribute('w:history', '1'); // Title text @@ -114,7 +114,7 @@ class TOC extends AbstractElement $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); - $xmlWriter->writeRaw('PAGEREF ' . $anchor . ' \h'); + $xmlWriter->writeRaw("PAGEREF _Toc{$rId} \h"); $xmlWriter->endElement(); $xmlWriter->endElement(); diff --git a/src/PhpWord/Writer/Word2007/Element/Title.php b/src/PhpWord/Writer/Word2007/Element/Title.php index 298bd9b1..144e67ab 100644 --- a/src/PhpWord/Writer/Word2007/Element/Title.php +++ b/src/PhpWord/Writer/Word2007/Element/Title.php @@ -35,8 +35,6 @@ class Title extends AbstractElement return; } - $rId = $element->getRelationId(); - $anchor = '_Toc' . ($rId + 252634154); $style = $element->getStyle(); $xmlWriter->startElement('w:p'); @@ -49,23 +47,22 @@ class Title extends AbstractElement $xmlWriter->endElement(); } - $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:fldChar'); - $xmlWriter->writeAttribute('w:fldCharType', 'end'); - $xmlWriter->endElement(); - $xmlWriter->endElement(); + $rId = $element->getRelationId(); + // Bookmark start for TOC $xmlWriter->startElement('w:bookmarkStart'); $xmlWriter->writeAttribute('w:id', $rId); - $xmlWriter->writeAttribute('w:name', $anchor); + $xmlWriter->writeAttribute('w:name', "_Toc{$rId}"); $xmlWriter->endElement(); + // Actual text $xmlWriter->startElement('w:r'); $xmlWriter->startElement('w:t'); $xmlWriter->writeRaw($this->getText($element->getText())); $xmlWriter->endElement(); $xmlWriter->endElement(); + // Bookmark end $xmlWriter->startElement('w:bookmarkEnd'); $xmlWriter->writeAttribute('w:id', $rId); $xmlWriter->endElement(); diff --git a/src/PhpWord/Writer/Word2007/Style/Paragraph.php b/src/PhpWord/Writer/Word2007/Style/Paragraph.php index 8741c177..4487aa71 100644 --- a/src/PhpWord/Writer/Word2007/Style/Paragraph.php +++ b/src/PhpWord/Writer/Word2007/Style/Paragraph.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Alignment as AlignmentStyle; @@ -74,41 +75,66 @@ class Paragraph extends AbstractStyle return; } $xmlWriter = $this->getXmlWriter(); + $styles = $style->getStyleValues(); if (!$this->withoutPPR) { $xmlWriter->startElement('w:pPr'); } // Style name - $styleName = $style->getStyleName(); - $xmlWriter->writeElementIf(!is_null($styleName), 'w:pStyle', 'w:val', $styleName); + $xmlWriter->writeElementIf($styles['name'] !== null, 'w:pStyle', 'w:val', $styles['name']); // Alignment - $styleWriter = new Alignment($xmlWriter, new AlignmentStyle(array('value' => $style->getAlign()))); + $styleWriter = new Alignment($xmlWriter, new AlignmentStyle(array('value' => $styles['alignment']))); $styleWriter->write(); // Pagination - $xmlWriter->writeElementIf(!$style->hasWidowControl(), 'w:widowControl', 'w:val', '0'); - $xmlWriter->writeElementIf($style->isKeepNext(), 'w:keepNext', 'w:val', '1'); - $xmlWriter->writeElementIf($style->isKeepLines(), 'w:keepLines', 'w:val', '1'); - $xmlWriter->writeElementIf($style->hasPageBreakBefore(), 'w:pageBreakBefore', 'w:val', '1'); + $xmlWriter->writeElementIf($styles['pagination']['widowControl'] === false, 'w:widowControl', 'w:val', '0'); + $xmlWriter->writeElementIf($styles['pagination']['keepNext'] === true, 'w:keepNext', 'w:val', '1'); + $xmlWriter->writeElementIf($styles['pagination']['keepLines'] === true, 'w:keepLines', 'w:val', '1'); + $xmlWriter->writeElementIf($styles['pagination']['pageBreak'] === true, 'w:pageBreakBefore', 'w:val', '1'); - // Indentation - $indentation = $style->getIndentation(); - if (!is_null($indentation)) { - $styleWriter = new Indentation($xmlWriter, $indentation); - $styleWriter->write(); - } - - // Spacing - $spacing = $style->getSpace(); - if (!is_null($spacing)) { - $styleWriter = new Spacing($xmlWriter, $spacing); - $styleWriter->write(); - } + // Indentation & spacing + $this->writeChildStyle($xmlWriter, 'Indentation', $styles['indentation']); + $this->writeChildStyle($xmlWriter, 'Spacing', $styles['spacing']); // Tabs - $tabs = $style->getTabs(); + $this->writeTabs($xmlWriter, $styles['tabs']); + + // Numbering + $this->writeNumbering($xmlWriter, $styles['numbering']); + + if (!$this->withoutPPR) { + $xmlWriter->endElement(); // w:pPr + } + } + + /** + * Write child style + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param string $name + * @param string $value + */ + private function writeChildStyle(XMLWriter $xmlWriter, $name, $value) + { + if ($value !== null) { + $class = "PhpOffice\\PhpWord\\Writer\\Word2007\\Style\\" . $name; + + /** @var \PhpOffice\PhpWord\Writer\Word2007\Style\AbstractStyle $writer */ + $writer = new $class($xmlWriter, $value); + $writer->write(); + } + } + + /** + * Write tabs + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param array $tabs + */ + private function writeTabs(XMLWriter $xmlWriter, $tabs) + { if (!empty($tabs)) { $xmlWriter->startElement("w:tabs"); foreach ($tabs as $tab) { @@ -117,28 +143,35 @@ class Paragraph extends AbstractStyle } $xmlWriter->endElement(); } + } - // Numbering - $numStyleName = $style->getNumStyle(); - $numStyleObject = Style::getStyle($numStyleName); - if ($numStyleName !== null && $numStyleObject !== null) { + /** + * Write numbering + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param array $numbering + */ + private function writeNumbering(XMLWriter $xmlWriter, $numbering) + { + $numStyle = $numbering['style']; + $numLevel = $numbering['level']; + + /** @var \PhpOffice\PhpWord\Style\Numbering $numbering */ + $numbering = Style::getStyle($numStyle); + if ($numStyle !== null && $numbering !== null) { $xmlWriter->startElement('w:numPr'); $xmlWriter->startElement('w:numId'); - $xmlWriter->writeAttribute('w:val', $numStyleObject->getIndex()); + $xmlWriter->writeAttribute('w:val', $numbering->getIndex()); $xmlWriter->endElement(); // w:numId $xmlWriter->startElement('w:ilvl'); - $xmlWriter->writeAttribute('w:val', $style->getNumLevel()); + $xmlWriter->writeAttribute('w:val', $numLevel); $xmlWriter->endElement(); // w:ilvl $xmlWriter->endElement(); // w:numPr $xmlWriter->startElement('w:outlineLvl'); - $xmlWriter->writeAttribute('w:val', $style->getNumLevel()); + $xmlWriter->writeAttribute('w:val', $numLevel); $xmlWriter->endElement(); // w:outlineLvl } - - if (!$this->withoutPPR) { - $xmlWriter->endElement(); // w:pPr - } } /** diff --git a/tests/PhpWord/Tests/Writer/Word2007/Part/DocumentTest.php b/tests/PhpWord/Tests/Writer/Word2007/Part/DocumentTest.php index 9223470e..e3933557 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/Part/DocumentTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/Part/DocumentTest.php @@ -342,8 +342,6 @@ class DocumentTest extends \PHPUnit_Framework_TestCase $element = "/w:document/w:body/w:p/w:pPr/w:pStyle"; $this->assertEquals('Heading1', $doc->getElementAttribute($element, 'w:val')); - $element = "/w:document/w:body/w:p/w:r/w:fldChar"; - $this->assertEquals('end', $doc->getElementAttribute($element, 'w:fldCharType')); } /**