Ability to add Endnotes

This commit is contained in:
Ivan Lanin 2014-04-09 21:35:55 +07:00
parent 38418677f8
commit f837381238
25 changed files with 470 additions and 205 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`.
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.
### Features
@ -28,6 +28,7 @@ This release marked heavy refactorings on internal code structure with the creat
- 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
- Endnote: Ability to add endnotes - @ivanlanin
### Bugfixes
@ -59,6 +60,7 @@ This release marked heavy refactorings on internal code structure with the creat
- General: Give `Abstract` prefix and `Interface` suffix for all abstract classes and interfaces as per [PHP-FIG recommendation](https://github.com/php-fig/fig-standards/blob/master/bylaws/002-psr-naming-conventions.md) - @ivanlanin GH-187
- Style: New `Style\AbstractStyle` abstract class - @ivanlanin GH-187
- Writer: New 'ODText\Base` class - @ivanlanin GH-187
- General: Rename `Footnote` to `Footnotes` to reflect the nature of collection - @ivanlanin
## 0.9.1 - 27 Mar 2014

View File

@ -25,6 +25,7 @@ With PHPWord, you can create DOCX, ODT, or RTF documents dynamically using your
* Insert and format table with customized properties for each rows (e.g. repeat as header row) and cells (e.g. background color, rowspan, colspan)
* Insert list items as bulleted, numbered, or multilevel
* Insert hyperlinks
* Insert footnotes and endnotes
* Create document from templates
* Use XSL 1.0 style sheets to transform main document part of OOXML template
* ... and many more features on progress

View File

@ -37,7 +37,9 @@ the containers while the rows lists the elements.
+-----+---------------+---------+--------+--------+------+----------+----------+
| 14 | Footnote | v | \- | \- | v\*\*| v\*\* | \- |
+-----+---------------+---------+--------+--------+------+----------+----------+
| 15 | CheckBox | v | v | v | v | ? | ? |
| 15 | Endnote | v | \- | \- | v\*\*| v\*\* | \- |
+-----+---------------+---------+--------+--------+------+----------+----------+
| 16 | CheckBox | v | v | v | v | ? | ? |
+-----+---------------+---------+--------+--------+------+----------+----------+
Legend:
@ -376,12 +378,13 @@ Options for ``$tocStyle``:
twips.
- ``indent`` The indent factor of the titles in twips.
Footnotes
---------
Footnotes & endnotes
--------------------
You can create footnotes in texts or textruns, but it's recommended to
use textrun to have better layout. You can use ``addText``, ``addLink``,
and ``addTextBreak`` on a footnote.
You can create footnotes with ``addFootnote`` and endnotes with ``addEndnote``
in texts or textruns, but it's recommended to use textrun to have better layout.
You can use ``addText``, ``addLink``, ``addTextBreak``, ``addImage``,
``addObject`` on footnotes and endnotes.
On textrun:
@ -396,6 +399,8 @@ On textrun:
$footnote->addTextBreak();
$footnote->addText('And text break.');
$textrun->addText('Trailing text.');
$endnote = $textrun->addEndnote();
$endnote->addText('Endnote put at the end');
On text:

View File

@ -48,6 +48,7 @@ Features
rowspan, colspan)
- Insert list items as bulleted, numbered, or multilevel
- Insert hyperlinks
- Insert footnotes and endnotes
- Create document from templates
- Use XSL 1.0 style sheets to transform main document part of OOXML
template
@ -90,8 +91,6 @@ Writers
+ +-----------------------+--------+-------+-------+
| | Image | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | MemoryImage | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | Object | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | Watermark | ✓ | | |
@ -103,6 +102,8 @@ Writers
| | Footer | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | Footnote | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | Endnote | ✓ | | |
+-------------------------+-----------------------+--------+-------+-------+
| **Graphs** | 2D basic graphs | | | |
+ +-----------------------+--------+-------+-------+
@ -126,11 +127,11 @@ Readers
+-------------------------------------------------+--------+-------+-------+
| Features | DOCX | ODT | RTF |
+=========================+=======================+========+=======+=======+
| **Document Properties** | Standard | | | |
| **Document Properties** | Standard | | | |
+ +-----------------------+--------+-------+-------+
| | Extended | | | |
| | Extended | | | |
+ +-----------------------+--------+-------+-------+
| | UserDefined | | | |
| | UserDefined | | | |
+-------------------------+-----------------------+--------+-------+-------+
| **Element Type** | Text | ✓ | | |
+ +-----------------------+--------+-------+-------+
@ -138,33 +139,33 @@ Readers
+ +-----------------------+--------+-------+-------+
| | Title | | | |
+ +-----------------------+--------+-------+-------+
| | Link | | | |
| | Link | | | |
+ +-----------------------+--------+-------+-------+
| | Preserve Text | | | |
| | Preserve Text | | | |
+ +-----------------------+--------+-------+-------+
| | Text Break | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | Page Break | | | |
| | Page Break | | | |
+ +-----------------------+--------+-------+-------+
| | List | | | |
+ +-----------------------+--------+-------+-------+
| | Table | | | |
| | Table | | | |
+ +-----------------------+--------+-------+-------+
| | Image | | | |
+ +-----------------------+--------+-------+-------+
| | MemoryImage | | | |
+ +-----------------------+--------+-------+-------+
| | Object | | | |
+ +-----------------------+--------+-------+-------+
| | Watermark | | | |
+ +-----------------------+--------+-------+-------+
| | Table of Contents | | | |
+ +-----------------------+--------+-------+-------+
| | Header | | | |
| | Header | | | |
+ +-----------------------+--------+-------+-------+
| | Footer | | | |
| | Footer | | | |
+ +-----------------------+--------+-------+-------+
| | Footnote | | | |
| | Footnote | ✓ | | |
+ +-----------------------+--------+-------+-------+
| | Endnote | ✓ | | |
+-------------------------+-----------------------+--------+-------+-------+
| **Graphs** | 2D basic graphs | | | |
+ +-----------------------+--------+-------+-------+

View File

@ -13,7 +13,8 @@ use PhpOffice\PhpWord\Exception\InvalidObjectException;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\TOC;
use PhpOffice\PhpWord\Footnote as FootnoteCollection;
use PhpOffice\PhpWord\Footnotes;
use PhpOffice\PhpWord\Endnotes;
use PhpOffice\PhpWord\Shared\String;
use PhpOffice\PhpWord\Element\Element;
use PhpOffice\PhpWord\Element\Text;
@ -27,6 +28,7 @@ use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\Element\Image;
use PhpOffice\PhpWord\Element\Object;
use PhpOffice\PhpWord\Element\Footnote as FootnoteElement;
use PhpOffice\PhpWord\Element\Endnote;
use PhpOffice\PhpWord\Element\CheckBox;
/**
@ -37,7 +39,7 @@ use PhpOffice\PhpWord\Element\CheckBox;
abstract class AbstractElement
{
/**
* Container type section|header|footer|cell|textrun|footnote
* Container type section|header|footer|cell|textrun|footnote|endnote
*
* @var string
*/
@ -99,7 +101,7 @@ abstract class AbstractElement
$this->checkValidity('text');
// Reset paragraph style for footnote and textrun. They have their own
if (in_array($this->container, array('footnote', 'textrun'))) {
if (in_array($this->container, array('textrun', 'footnote', 'endnote'))) {
$paragraphStyle = null;
}
@ -323,7 +325,7 @@ abstract class AbstractElement
$this->checkValidity('footnote');
$footnote = new FootnoteElement($paragraphStyle);
$rId = FootnoteCollection::addFootnoteElement($footnote);
$rId = Footnotes::addElement($footnote);
$footnote->setDocPart('footnote', $this->getDocPartId());
$footnote->setRelationId($rId);
@ -332,6 +334,26 @@ abstract class AbstractElement
return $footnote;
}
/**
* Add endnote element
*
* @param mixed $paragraphStyle
* @return Endnote
*/
public function addEndnote($paragraphStyle = null)
{
$this->checkValidity('endnote');
$endnote = new Endnote($paragraphStyle);
$rId = Endnotes::addElement($endnote);
$endnote->setDocPart('endnote', $this->getDocPartId());
$endnote->setRelationId($rId);
$this->elements[] = $endnote;
return $endnote;
}
/**
* Add a CheckBox Element
*
@ -469,7 +491,7 @@ abstract class AbstractElement
private function checkValidity($method)
{
// Valid containers for each element
$allContainers = array('section', 'header', 'footer', 'cell', 'textrun', 'footnote');
$allContainers = array('section', 'header', 'footer', 'cell', 'textrun', 'footnote', 'endnote');
$validContainers = array(
'text' => $allContainers,
'link' => $allContainers,
@ -481,6 +503,7 @@ abstract class AbstractElement
'checkbox' => array('section', 'header', 'footer', 'cell'),
'table' => array('section', 'header', 'footer'),
'footnote' => array('section', 'textrun', 'cell'),
'endnote' => array('section', 'textrun', 'cell'),
'preservetext' => array('header', 'footer', 'cell'),
'title' => array('section'),
);
@ -489,6 +512,7 @@ abstract class AbstractElement
$validContainerInContainers = array(
'preservetext' => array(array('cell'), array('header', 'footer')),
'footnote' => array(array('cell', 'textrun'), array('section')),
'endnote' => array(array('cell', 'textrun'), array('section')),
);
// Check if a method is valid for current container

View File

@ -0,0 +1,31 @@
<?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\Element;
use PhpOffice\PhpWord\Style\Paragraph;
/**
* Endnote element
*
* @since 0.9.2
*/
class Endnote extends Footnote
{
/**
* Create new instance
*
* @param string|array|Paragraph $paragraphStyle
*/
public function __construct($paragraphStyle = null)
{
$this->container = 'endnote';
$this->paragraphStyle = $this->setStyle(new Paragraph(), $paragraphStyle);
}
}

View File

@ -21,7 +21,7 @@ class Footnote extends AbstractElement
*
* @var string|Paragraph
*/
private $paragraphStyle;
protected $paragraphStyle;
/**
* Create new instance

98
src/PhpWord/Endnotes.php Normal file
View File

@ -0,0 +1,98 @@
<?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;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Element\Endnote;
/**
* Endnote collection
*
* @since 0.9.2
*/
class Endnotes
{
/**
* Elements
*
* @var array
*/
private static $elements = array();
/**
* Add new element
*
* @param Endnote $element
* @return integer Reference ID
*/
public static function addElement($element)
{
$rId = self::countElements() + 1;
self::$elements[$rId] = $element;
return $rId;
}
/**
* Set element
*
* @param integer $index
* @param Endnote $element
*/
public static function setElement($index, $element)
{
if (array_key_exists($index, self::$elements)) {
self::$elements[$index] = $element;
}
}
/**
* Get element by index
*
* @param integer $index
* @return Endnote
*/
public static function getElement($index)
{
if (array_key_exists($index, self::$elements)) {
return self::$elements[$index];
} else {
return null;
}
}
/**
* Get elements
*
* @return array
*/
public static function getElements()
{
return self::$elements;
}
/**
* Get element count
*
* @return integer
*/
public static function countElements()
{
return count(self::$elements);
}
/**
* Reset elements
*/
public static function resetElements()
{
self::$elements = array();
}
}

View File

@ -10,12 +10,12 @@
namespace PhpOffice\PhpWord;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Element\Footnote as FootnoteElement;
use PhpOffice\PhpWord\Element\Footnote;
/**
* Footnote collection
* Footnote collection
*/
class Footnote
class Footnotes
{
/**
* Elements
@ -27,14 +27,14 @@ class Footnote
/**
* Add new element
*
* @param FootnoteElement $footnote
* @param Footnote $element
* @return integer Reference ID
* @since 0.9.2
*/
public static function addElement(FootnoteElement $footnote)
public static function addElement($element)
{
$rId = self::countElements() + 1;
self::$elements[$rId] = $footnote;
self::$elements[$rId] = $element;
return $rId;
}
@ -43,13 +43,13 @@ class Footnote
* Set element
*
* @param integer $index
* @param FootnoteElement $footnote
* @param Footnote $element
* @since 0.9.2
*/
public static function setElement($index, FootnoteElement $footnote)
public static function setElement($index, $element)
{
if (array_key_exists($index, self::$elements)) {
self::$elements[$index] = $footnote;
self::$elements[$index] = $element;
}
}
@ -57,7 +57,7 @@ class Footnote
* Get element by index
*
* @param integer $index
* @return FootnoteElement
* @return Footnote
* @since 0.9.2
*/
public static function getElement($index)
@ -104,14 +104,14 @@ class Footnote
/**
* Add new footnote
*
* @param FootnoteElement $footnote
* @param Footnote $element
* @return integer Reference ID
* @deprecated 0.9.2
* @codeCoverageIgnore
*/
public static function addFootnoteElement(FootnoteElement $footnote)
public static function addFootnoteElement($element)
{
return self::addElement($footnote);
return self::addElement($element);
}
/**

View File

@ -27,7 +27,7 @@ class Media
/**
* Add new media element
*
* @param string $container section|headerx|footerx|footnote
* @param string $container section|headerx|footerx|footnote|endnote
* @param string $mediaType image|object|link
* @param string $source
* @param Image $image
@ -51,28 +51,33 @@ class Media
$target = null;
$mediaTypeCount++;
// Images
if ($mediaType == 'image') {
if (is_null($image)) {
throw new Exception('Image object not assigned.');
}
$isMemImage = $image->getIsMemImage();
$extension = $image->getImageExtension();
$mediaData['imageExtension'] = $extension;
$mediaData['imageType'] = $image->getImageType();
if ($isMemImage) {
$mediaData['isMemImage'] = true;
$mediaData['createFunction'] = $image->getImageCreateFunction();
$mediaData['imageFunction'] = $image->getImageFunction();
}
$target = "media/{$container}_image{$mediaTypeCount}.{$extension}";
// Objects
} elseif ($mediaType == 'object') {
$file = "oleObject{$mediaTypeCount}.bin";
$target = "embeddings/{$container}_oleObject{$mediaTypeCount}.bin";
// Links
} elseif ($mediaType == 'link') {
$target = $source;
switch ($mediaType) {
// Images
case 'image':
if (is_null($image)) {
throw new Exception('Image object not assigned.');
}
$isMemImage = $image->getIsMemImage();
$extension = $image->getImageExtension();
$mediaData['imageExtension'] = $extension;
$mediaData['imageType'] = $image->getImageType();
if ($isMemImage) {
$mediaData['isMemImage'] = true;
$mediaData['createFunction'] = $image->getImageCreateFunction();
$mediaData['imageFunction'] = $image->getImageFunction();
}
$target = "media/{$container}_image{$mediaTypeCount}.{$extension}";
break;
// Objects
case 'object':
$target = "embeddings/{$container}_oleObject{$mediaTypeCount}.bin";
break;
// Links
case 'link':
$target = $source;
break;
}
$mediaData['source'] = $source;
@ -89,7 +94,7 @@ class Media
/**
* Get media elements count
*
* @param string $container section|headerx|footerx|footnote
* @param string $container section|headerx|footerx|footnote|endnote
* @param string $mediaType image|object|link
* @return integer
* @since 0.9.2
@ -116,7 +121,7 @@ class Media
/**
* Get media elements
*
* @param string $container section|headerx|footerx|footnote
* @param string $container section|headerx|footerx|footnote|endnote
* @param string $mediaType image|object|link
* @return array
* @since 0.9.2

View File

@ -12,8 +12,8 @@ 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\Exception\Exception;
use PhpOffice\PhpWord\Shared\XMLReader;
use PhpOffice\PhpWord\Element\Section;
@ -44,7 +44,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
* Loads PhpWord from file
*
* @param string $filename
* @return PhpWord|null
* @return PhpWord
*/
public function load($filename)
{
@ -96,7 +96,8 @@ class Word2007 extends AbstractReader implements ReaderInterface
break;
case 'footnotes':
$this->readFootnotes($filename, $rel['target']);
case 'endnotes':
$this->readNotes($filename, $rel['target'], $rel['type']);
break;
}
}
@ -147,14 +148,13 @@ class Word2007 extends AbstractReader implements ReaderInterface
$nodes = $xmlReader->getElements('*');
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$nodeName = $node->nodeName;
if (!array_key_exists($nodeName, $mapping)) {
if (!array_key_exists($node->nodeName, $mapping)) {
continue;
}
$method = $mapping[$nodeName];
$method = $mapping[$node->nodeName];
$value = $node->nodeValue == '' ? null : $node->nodeValue;
if (array_key_exists($nodeName, $callbacks)) {
$value = $callbacks[$nodeName]($value);
if (array_key_exists($node->nodeName, $callbacks)) {
$value = $callbacks[$node->nodeName]($value);
}
if (method_exists($docProps, $method)) {
$docProps->$method($value);
@ -253,7 +253,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
if (is_null($name)) {
$name = $xmlReader->getAttribute('w:val', $node, 'w:name');
}
$default = ($xmlReader->getAttribute('w:default', $node) == 1);
// $default = ($xmlReader->getAttribute('w:default', $node) == 1);
switch ($type) {
case 'paragraph':
$pStyle = $this->readParagraphStyle($xmlReader, $node);
@ -321,14 +321,16 @@ class Word2007 extends AbstractReader implements ReaderInterface
}
/**
* Read footnotes.xml
* Read (footnotes|endnotes).xml
*
* @param string $filename
* @param string $xmlFile
*/
private function readFootnotes($filename, $xmlFile)
private function readNotes($filename, $xmlFile, $notesType = 'footnotes')
{
$footnotes = Footnote::getElements();
$notesType = ($notesType == 'endnotes') ? 'endnotes' : 'footnotes';
$collectionClass = 'PhpOffice\\PhpWord\\' . ucfirst($notesType);
$collection = $collectionClass::getElements();
$xmlReader = new XMLReader();
$xmlReader->getDomFromZip($filename, $xmlFile);
@ -339,14 +341,14 @@ class Word2007 extends AbstractReader implements ReaderInterface
$type = $xmlReader->getAttribute('w:type', $node);
// Avoid w:type "separator" and "continuationSeparator"
// Only look for <footnote> without w:type attribute
if (is_null($type) && array_key_exists($id, $footnotes)) {
$footnote = $footnotes[$id];
// Only look for <footnote> or <endnote> without w:type attribute
if (is_null($type) && array_key_exists($id, $collection)) {
$element = $collection[$id];
$pNodes = $xmlReader->getElements('w:p/*', $node);
foreach ($pNodes as $pNode) {
$this->readRun($xmlReader, $pNode, $footnote, 'footnotes');
$this->readRun($xmlReader, $pNode, $element, $notesType);
}
Footnote::setElement($id, $footnote);
$collectionClass::setElement($id, $element);
}
}
}
@ -445,6 +447,10 @@ class Word2007 extends AbstractReader implements ReaderInterface
if ($xmlReader->elementExists('w:footnoteReference', $domNode)) {
$parent->addFootnote();
// Endnote
} elseif ($xmlReader->elementExists('w:endnoteReference', $domNode)) {
$parent->addEndnote();
// Image
} elseif ($xmlReader->elementExists('w:pict', $domNode)) {
$rId = $xmlReader->getAttribute('r:id', $domNode, 'w:pict/v:shape/v:imagedata');
@ -457,7 +463,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
// Object
} elseif ($xmlReader->elementExists('w:object', $domNode)) {
$rId = $xmlReader->getAttribute('r:id', $domNode, 'w:object/o:OLEObject');
$rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata');
// $rIdIcon = $xmlReader->getAttribute('r:id', $domNode, 'w:object/v:shape/v:imagedata');
$target = $this->getMediaTarget($docPart, $rId);
if (!is_null($target)) {
$textContent = "<Object: {$target}>";
@ -489,7 +495,6 @@ class Word2007 extends AbstractReader implements ReaderInterface
$table = $parent->addTable($tblStyle);
$tblNodes = $xmlReader->getElements('*', $domNode);
foreach ($tblNodes as $tblNode) {
$tblNodeName = $tblNode->nodeName;
if ($tblNode->nodeName == 'w:tblGrid') { // Column
// @todo Do something with table columns
@ -745,7 +750,7 @@ class Word2007 extends AbstractReader implements ReaderInterface
if (!array_key_exists($node->nodeName, $mapping)) {
continue;
}
$property = $mapping[$node->nodeName];
// $property = $mapping[$node->nodeName];
switch ($node->nodeName) {
case 'w:tblCellMar':
foreach ($margins as $side) {

View File

@ -105,7 +105,7 @@ class XMLReader
* @param string $path
* @return string|null
*/
public function getAttribute($attribute, \DOMElement $contextNode, $path = null)
public function getAttribute($attribute, \DOMNode $contextNode, $path = null)
{
if (is_null($path)) {
$return = $contextNode->getAttribute($attribute);
@ -154,7 +154,7 @@ class XMLReader
* Element exists
*
* @param string $path
* @return \DOMNodeList
* @return boolean
*/
public function elementExists($path, \DOMNode $contextNode)
{

View File

@ -109,7 +109,7 @@ class Style
/**
* Get all styles
*
* @return Font[]
* @return array
*/
public static function getStyles()
{

View File

@ -192,7 +192,6 @@ class Image extends AbstractStyle
break;
default:
throw new \InvalidArgumentException('Wrapping style does not exists');
break;
}
return $this;
}

View File

@ -48,6 +48,7 @@ class ODText extends AbstractWriter implements WriterInterface
*
* @param string $filename
* @throws Exception
* @todo Not in \ZipArchive::CM_STORE mode
*/
public function save($filename = null)
{
@ -56,20 +57,11 @@ class ODText extends AbstractWriter implements WriterInterface
$objZip = $this->getZipArchive($filename);
// Add mimetype to ZIP file
//@todo Not in \ZipArchive::CM_STORE mode
$objZip->addFromString('mimetype', $this->getWriterPart('mimetype')->writeMimetype($this->phpWord));
// Add content.xml to ZIP file
$objZip->addFromString('mimetype', $this->getWriterPart('mimetype')->writeMimetype());
$objZip->addFromString('content.xml', $this->getWriterPart('content')->writeContent($this->phpWord));
// Add meta.xml to ZIP file
$objZip->addFromString('meta.xml', $this->getWriterPart('meta')->writeMeta($this->phpWord));
// Add styles.xml to ZIP file
$objZip->addFromString('styles.xml', $this->getWriterPart('styles')->writeStyles($this->phpWord));
// Add META-INF/manifest.xml
$objZip->addFromString('META-INF/manifest.xml', $this->getWriterPart('manifest')->writeManifest($this->phpWord));
$objZip->addFromString('META-INF/manifest.xml', $this->getWriterPart('manifest')->writeManifest());
// Close file
if ($objZip->close() === false) {

View File

@ -9,8 +9,6 @@
namespace PhpOffice\PhpWord\Writer\ODText;
use PhpOffice\PhpWord\PhpWord;
/**
* ODText manifest part writer
*/
@ -19,10 +17,9 @@ class Manifest extends AbstractWriterPart
/**
* Write Manifest file to XML format
*
* @param PhpWord $phpWord
* @return string XML Output
*/
public function writeManifest(PhpWord $phpWord = null)
public function writeManifest()
{
// Create XML writer
$xmlWriter = $this->getXmlWriter();

View File

@ -9,8 +9,6 @@
namespace PhpOffice\PhpWord\Writer\ODText;
use PhpOffice\PhpWord\PhpWord;
/**
* ODText mimetype part writer
*/
@ -19,10 +17,9 @@ class Mimetype extends AbstractWriterPart
/**
* Write Mimetype to Text format
*
* @param PhpWord $phpWord
* @return string Text Output
*/
public function writeMimetype(PhpWord $phpWord = null)
public function writeMimetype()
{
return 'application/vnd.oasis.opendocument.text';
}

View File

@ -11,7 +11,6 @@ namespace PhpOffice\PhpWord\Writer;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Footnote;
use PhpOffice\PhpWord\Media;
use PhpOffice\PhpWord\Element\Section;
use PhpOffice\PhpWord\Writer\Word2007\ContentTypes;
@ -19,7 +18,7 @@ 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\Footnotes;
use PhpOffice\PhpWord\Writer\Word2007\Notes;
use PhpOffice\PhpWord\Writer\Word2007\Header;
use PhpOffice\PhpWord\Writer\Word2007\Styles;
@ -60,7 +59,8 @@ class Word2007 extends AbstractWriter implements WriterInterface
$this->writerParts['styles'] = new Styles();
$this->writerParts['header'] = new Header();
$this->writerParts['footer'] = new Footer();
$this->writerParts['footnotes'] = new Footnotes();
$this->writerParts['footnotes'] = new Notes();
$this->writerParts['endnotes'] = new Notes();
foreach ($this->writerParts as $writer) {
$writer->setParentWriter($this);
}
@ -105,17 +105,8 @@ class Word2007 extends AbstractWriter implements WriterInterface
$this->addHeaderFooterContent($section, $objZip, 'footer', $rId);
}
// Add footnotes media files, relations, and contents
if (Footnote::countFootnoteElements() > 0) {
$footnoteMedia = Media::getElements('footnote');
$this->addFilesToPackage($objZip, $footnoteMedia);
if (!empty($footnoteMedia)) {
$objZip->addFromString('word/_rels/footnotes.xml.rels', $this->getWriterPart('rels')->writeMediaRels($footnoteMedia));
}
$objZip->addFromString('word/footnotes.xml', $this->getWriterPart('footnotes')->writeFootnotes(Footnote::getFootnoteElements()));
$this->cTypes['override']["/word/footnotes.xml"] = 'footnotes';
$this->docRels[] = array('target' => 'footnotes.xml', 'type' => 'footnotes', 'rID' => ++$rId);
}
$this->addNotes($objZip, $rId, 'footnote');
$this->addNotes($objZip, $rId, 'endnote');
// Write dynamic files
$objZip->addFromString('[Content_Types].xml', $this->getWriterPart('contenttypes')->writeContentTypes($this->cTypes));
@ -199,7 +190,8 @@ class Word2007 extends AbstractWriter implements WriterInterface
if (!empty($media)) {
$this->addFilesToPackage($objZip, $media);
}
$objZip->addFromString("word/_rels/{$file}.xml.rels", $this->getWriterPart('rels')->writeMediaRels($media));
$relsFile = "word/_rels/{$file}.xml.rels";
$objZip->addFromString($relsFile, $this->getWriterPart('rels')->writeMediaRels($media));
}
}
}
@ -227,4 +219,34 @@ class Word2007 extends AbstractWriter implements WriterInterface
$this->docRels[] = array('target' => $elmFile, 'type' => $elmType, 'rID' => $rId);
}
}
/**
* Add footnotes/endnotes
*
* @param mixed $objZip
* @param string $elmType
* @param integer $rId
*/
private function addNotes($objZip, &$rId, $notesType = 'footnote')
{
$notesType = ($notesType == 'endnote') ? 'endnote' : 'footnote';
$notesTypes = "{$notesType}s";
$collection = 'PhpOffice\\PhpWord\\' . ucfirst($notesTypes);
$xmlFile = "{$notesTypes}.xml";
$relsFile = "word/_rels/{$xmlFile}.rels";
$xmlPath = "word/{$xmlFile}";
// Add footnotes media files, relations, and contents
if ($collection::countElements() > 0) {
$media = Media::getElements($notesType);
$elements = $collection::getElements();
$this->addFilesToPackage($objZip, $media);
if (!empty($media)) {
$objZip->addFromString($relsFile, $this->getWriterPart('rels')->writeMediaRels($media));
}
$objZip->addFromString($xmlPath, $this->getWriterPart($notesTypes)->writeNotes($elements, $notesTypes));
$this->cTypes['override']["/{$xmlPath}"] = $notesTypes;
$this->docRels[] = array('target' => $xmlFile, 'type' => $notesTypes, 'rID' => ++$rId);
}
}
}

View File

@ -23,6 +23,7 @@ use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\Element\Image;
use PhpOffice\PhpWord\Element\Object;
use PhpOffice\PhpWord\Element\Footnote;
use PhpOffice\PhpWord\Element\Endnote;
use PhpOffice\PhpWord\Element\CheckBox;
use PhpOffice\PhpWord\Shared\String;
use PhpOffice\PhpWord\Shared\XMLWriter;
@ -623,6 +624,33 @@ class Base extends AbstractWriterPart
}
}
/**
* Write endnote element which links to the actual content in endnotes.xml
*
* @param XMLWriter $xmlWriter
* @param Endnote $endnote
* @param boolean $withoutP
*/
protected function writeEndnote(XMLWriter $xmlWriter, Endnote $endnote, $withoutP = false)
{
if (!$withoutP) {
$xmlWriter->startElement('w:p');
}
$xmlWriter->startElement('w:r');
$xmlWriter->startElement('w:rPr');
$xmlWriter->startElement('w:rStyle');
$xmlWriter->writeAttribute('w:val', 'EndnoteReference');
$xmlWriter->endElement(); // w:rStyle
$xmlWriter->endElement(); // w:rPr
$xmlWriter->startElement('w:endnoteReference');
$xmlWriter->writeAttribute('w:id', $endnote->getRelationId());
$xmlWriter->endElement(); // w:endnoteReference
$xmlWriter->endElement(); // w:r
if (!$withoutP) {
$xmlWriter->endElement(); // w:p
}
}
/**
* Write CheckBox
*
@ -1132,9 +1160,10 @@ class Base extends AbstractWriterPart
'Section' => array_merge($elmMainCell, array('Table', 'Footnote', 'Title', 'PageBreak', 'TOC')),
'Header' => array_merge($elmMainCell, array('Table', 'PreserveText')),
'Footer' => array_merge($elmMainCell, array('Table', 'PreserveText')),
'Cell' => array_merge($elmMainCell, array('PreserveText', 'Footnote')),
'TextRun' => array_merge($elmCommon, array('Footnote')),
'Cell' => array_merge($elmMainCell, array('PreserveText', 'Footnote', 'Endnote')),
'TextRun' => array_merge($elmCommon, array('Footnote', 'Endnote')),
'Footnote' => $elmCommon,
'Endnote' => $elmCommon,
);
$containerName = get_class($container);
$containerName = substr($containerName, strrpos($containerName, '\\') + 1);
@ -1158,10 +1187,14 @@ class Base extends AbstractWriterPart
$method = "writeWatermark";
}
}
if (in_array($containerName, array('TextRun', 'Footnote'))) {
$this->$method($xmlWriter, $element, true);
} else {
$this->$method($xmlWriter, $element);
switch ($containerName) {
case 'TextRun':
case 'Footnote':
case 'Endnote':
$this->$method($xmlWriter, $element, true);
break;
default:
$this->$method($xmlWriter, $element);
}
}
}

View File

@ -10,27 +10,30 @@
namespace PhpOffice\PhpWord\Writer\Word2007;
use PhpOffice\PhpWord\Element\Footnote;
use PhpOffice\PhpWord\Element\Endnote;
use PhpOffice\PhpWord\Shared\XMLWriter;
/**
* Word2007 footnotes part writer
*/
class Footnotes extends Base
class Notes extends Base
{
/**
* Write word/footnotes.xml
* Write word/(footnotes|endnotes).xml
*
* @param array $allFootnotesCollection
* @param array $elements
* @param string $notesTypes
*/
public function writeFootnotes($allFootnotesCollection)
public function writeNotes($elements, $notesTypes = 'footnotes')
{
// Create XML writer
$isFootnote = $notesTypes == 'footnotes';
$rootNode = $isFootnote ? 'w:footnotes' : 'w:endnotes';
$elementNode = $isFootnote ? 'w:footnote' : 'w:endnote';
$xmlWriter = $this->getXmlWriter();
// XML header
$xmlWriter->startDocument('1.0', 'UTF-8', 'yes');
$xmlWriter->startElement('w:footnotes');
$xmlWriter->startElement($rootNode);
$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');
@ -42,7 +45,7 @@ class Footnotes extends Base
$xmlWriter->writeAttribute('xmlns:wne', 'http://schemas.microsoft.com/office/word/2006/wordml');
// Separator and continuation separator
$xmlWriter->startElement('w:footnote');
$xmlWriter->startElement($elementNode);
$xmlWriter->writeAttribute('w:id', -1);
$xmlWriter->writeAttribute('w:type', 'separator');
$xmlWriter->startElement('w:p');
@ -51,8 +54,8 @@ class Footnotes extends Base
$xmlWriter->endElement(); // w:separator
$xmlWriter->endElement(); // w:r
$xmlWriter->endElement(); // w:p
$xmlWriter->endElement(); // w:footnote
$xmlWriter->startElement('w:footnote');
$xmlWriter->endElement(); // $elementNode
$xmlWriter->startElement($elementNode);
$xmlWriter->writeAttribute('w:id', 0);
$xmlWriter->writeAttribute('w:type', 'continuationSeparator');
$xmlWriter->startElement('w:p');
@ -61,42 +64,53 @@ class Footnotes extends Base
$xmlWriter->endElement(); // w:continuationSeparator
$xmlWriter->endElement(); // w:r
$xmlWriter->endElement(); // w:p
$xmlWriter->endElement(); // w:footnote
$xmlWriter->endElement(); // $elementNode
// Content
foreach ($allFootnotesCollection as $footnote) {
if ($footnote instanceof Footnote) {
$this->writeFootnote($xmlWriter, $footnote);
foreach ($elements as $element) {
if ($element instanceof Footnote || $element instanceof Endnote) {
$this->writeNote($xmlWriter, $element, null, $notesTypes);
}
}
$xmlWriter->endElement();
return $xmlWriter->getData();
}
/**
* Write footnote content, overrides method in parent class
* Write note item
*
* @param XMLWriter $xmlWriter
* @param Footnote $footnote
* @param Footnote|Endnote $element
* @param boolean $withoutP
* @param string $notesTypes
*/
protected function writeFootnote(XMLWriter $xmlWriter, Footnote $footnote, $withoutP = false)
protected function writeNote(XMLWriter $xmlWriter, $element, $withoutP = false, $notesTypes = 'footnotes')
{
$xmlWriter->startElement('w:footnote');
$xmlWriter->writeAttribute('w:id', $footnote->getRelationId());
$isFootnote = ($notesTypes == 'footnotes');
$elementNode = $isFootnote ? 'w:footnote' : 'w:endnote';
$refNode = $isFootnote ? 'w:footnoteRef' : 'w:endnoteRef';
$styleName = $isFootnote ? 'FootnoteReference' : 'EndnoteReference';
$xmlWriter->startElement($elementNode);
$xmlWriter->writeAttribute('w:id', $element->getRelationId());
$xmlWriter->startElement('w:p');
// Paragraph style
$styleParagraph = $footnote->getParagraphStyle();
$styleParagraph = $element->getParagraphStyle();
$this->writeInlineParagraphStyle($xmlWriter, $styleParagraph);
// Reference symbol
$xmlWriter->startElement('w:r');
$xmlWriter->startElement('w:rPr');
$xmlWriter->startElement('w:rStyle');
$xmlWriter->writeAttribute('w:val', 'FootnoteReference');
$xmlWriter->writeAttribute('w:val', $styleName);
$xmlWriter->endElement(); // w:rStyle
$xmlWriter->endElement(); // w:rPr
$xmlWriter->writeElement('w:footnoteRef');
$xmlWriter->writeElement($refNode);
$xmlWriter->endElement(); // w:r
// Empty space after refence symbol
$xmlWriter->startElement('w:r');
$xmlWriter->startElement('w:t');
@ -105,9 +119,9 @@ class Footnotes extends Base
$xmlWriter->endElement(); // w:t
$xmlWriter->endElement(); // w:r
$this->writeContainerElements($xmlWriter, $footnote);
$this->writeContainerElements($xmlWriter, $element);
$xmlWriter->endElement(); // w:p
$xmlWriter->endElement(); // w:footnote
$xmlWriter->endElement(); // $elementNode
}
}

View File

@ -0,0 +1,39 @@
<?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\Tests;
use PhpOffice\PhpWord\Endnotes;
/**
* Test class for PhpOffice\PhpWord\Endnotes
*
* @runTestsInSeparateProcesses
*/
class EndnotesTest extends \PHPUnit_Framework_TestCase
{
/**
* Test endnote collection
*/
public function testEndnotes()
{
$endnote1 = new \PhpOffice\PhpWord\Element\Endnote();
$endnote2 = new \PhpOffice\PhpWord\Element\Endnote();
$rId = Endnotes::addElement($endnote1);
Endnotes::setElement(1, $endnote2);
$this->assertEquals(1, $rId);
$this->assertEquals(1, count(Endnotes::getElements()));
$this->assertEquals($endnote2, Endnotes::getElement(1));
$this->assertNull(Endnotes::getElement(2));
Endnotes::resetElements();
$this->assertEquals(0, Endnotes::countElements());
}
}

View File

@ -1,39 +0,0 @@
<?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\Tests;
use PhpOffice\PhpWord\Footnote;
/**
* Test class for PhpOffice\PhpWord\Footnote
*
* @runTestsInSeparateProcesses
*/
class FootnoteTest extends \PHPUnit_Framework_TestCase
{
/**
* Test add, get, and count footnote elements and links
*/
public function testFootnote()
{
$footnote1 = new \PhpOffice\PhpWord\Element\Footnote('default');
$footnote2 = new \PhpOffice\PhpWord\Element\Footnote('first');
$rId = Footnote::addElement($footnote1);
Footnote::setElement(1, $footnote2);
$this->assertEquals(1, $rId);
$this->assertEquals(1, count(Footnote::getElements()));
$this->assertEquals($footnote2, Footnote::getElement(1));
$this->assertNull(Footnote::getElement(2));
Footnote::resetElements();
$this->assertEquals(0, Footnote::countElements());
}
}

View File

@ -0,0 +1,39 @@
<?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\Tests;
use PhpOffice\PhpWord\Footnotes;
/**
* Test class for PhpOffice\PhpWord\Footnotes
*
* @runTestsInSeparateProcesses
*/
class FootnotesTest extends \PHPUnit_Framework_TestCase
{
/**
* Test footnote collection
*/
public function testFootnotes()
{
$footnote1 = new \PhpOffice\PhpWord\Element\Footnote();
$footnote2 = new \PhpOffice\PhpWord\Element\Footnote();
$rId = Footnotes::addElement($footnote1);
Footnotes::setElement(1, $footnote2);
$this->assertEquals(1, $rId);
$this->assertEquals(1, count(Footnotes::getElements()));
$this->assertEquals($footnote2, Footnotes::getElement(1));
$this->assertNull(Footnotes::getElement(2));
Footnotes::resetElements();
$this->assertEquals(0, Footnotes::countElements());
}
}

View File

@ -12,12 +12,11 @@ use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Tests\TestHelperDOCX;
/**
* Test class for PhpOffice\PhpWord\Writer\Word2007\Footnotes
* Test class for PhpOffice\PhpWord\Writer\Word2007\Notes
*
* @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Footnotes
* @runTestsInSeparateProcesses
*/
class FootnotesTest extends \PHPUnit_Framework_TestCase
class NotesTest extends \PHPUnit_Framework_TestCase
{
/**
* Executed before each method of the class

View File

@ -35,18 +35,19 @@ class Word2007Test extends \PHPUnit_Framework_TestCase
$object = new Word2007(new PhpWord());
$writerParts = array(
'ContentTypes',
'Rels',
'DocProps',
'Document',
'Styles',
'Header',
'Footer',
'Footnotes',
'ContentTypes' => 'ContentTypes',
'Rels' => 'Rels',
'DocProps' => 'DocProps',
'Document' => 'Document',
'Styles' => 'Styles',
'Header' => 'Header',
'Footer' => 'Footer',
'Footnotes' => 'Notes',
'Endnotes' => 'Notes',
);
foreach ($writerParts as $part) {
foreach ($writerParts as $part => $type) {
$this->assertInstanceOf(
"PhpOffice\\PhpWord\\Writer\\Word2007\\{$part}",
"PhpOffice\\PhpWord\\Writer\\Word2007\\{$type}",
$object->getWriterPart($part)
);
$this->assertInstanceOf(