From 3b3f49bfa78309745f80dc9a0fe5c33e39b3a79b Mon Sep 17 00:00:00 2001 From: Deds Castillo Date: Mon, 26 Nov 2012 11:15:26 +0800 Subject: [PATCH] initial addition of basic footnote support --- src/Examples/Footnote.php | 38 +++++ src/PHPWord/Footnote.php | 126 +++++++++++++++ src/PHPWord/Section.php | 17 +- src/PHPWord/Section/Footnote.php | 151 ++++++++++++++++++ src/PHPWord/Section/TextRun.php | 14 ++ src/PHPWord/Writer/Word2007.php | 22 ++- src/PHPWord/Writer/Word2007/Base.php | 58 +++++++ src/PHPWord/Writer/Word2007/Document.php | 2 + src/PHPWord/Writer/Word2007/Footnotes.php | 83 ++++++++++ src/PHPWord/Writer/Word2007/FootnotesRels.php | 91 +++++++++++ 10 files changed, 597 insertions(+), 5 deletions(-) create mode 100644 src/Examples/Footnote.php create mode 100644 src/PHPWord/Footnote.php create mode 100644 src/PHPWord/Section/Footnote.php create mode 100644 src/PHPWord/Writer/Word2007/Footnotes.php create mode 100644 src/PHPWord/Writer/Word2007/FootnotesRels.php diff --git a/src/Examples/Footnote.php b/src/Examples/Footnote.php new file mode 100644 index 0000000000..468ec1c42d --- /dev/null +++ b/src/Examples/Footnote.php @@ -0,0 +1,38 @@ +createSection(); + +// Add style definitions +$PHPWord->addParagraphStyle('pStyle', array('spacing'=>100)); +$PHPWord->addFontStyle('BoldText', array('bold'=>true)); +$PHPWord->addFontStyle('ColoredText', array('color'=>'FF8080')); +$PHPWord->addLinkStyle('NLink', array('color'=>'0000FF', 'underline'=>PHPWord_Style_Font::UNDERLINE_SINGLE)); + +// Add text elements +$textrun = $section->createTextRun('pStyle'); +$textrun->addText('This is some lead text in a paragraph with a following footnote. ','pStyle'); + +$footnote = $textrun->createFootnote(); +$footnote->addText('Just like a textrun a footnote can contain native text and link elements.'); +$footnote->addText(' No break is placed after adding an element.', 'BoldText'); +$footnote->addText(' All elements are placed inside a paragraph.', 'ColoredText'); +$footnote->addText(' The best search engine: '); +$footnote->addLink('http://www.google.com', null, 'NLink'); +$footnote->addText('. Also not bad: '); +$footnote->addLink('http://www.bing.com', null, 'NLink'); + +$textrun->addText('The trailing text in the paragraph.'); + +$section->addText('You can also create the footnote directly from the section making it wrap in a paragraph like the footnote below this paragraph. But is is best used from within a textrun.'); +$footnote = $section->createFootnote(); +$footnote->addText('The reference for this is wrapped in its own line'); + +// Save File +$objWriter = PHPWord_IOFactory::createWriter($PHPWord, 'Word2007'); +$objWriter->save('Footnote.docx'); +?> \ No newline at end of file diff --git a/src/PHPWord/Footnote.php b/src/PHPWord/Footnote.php new file mode 100644 index 0000000000..a3304c6544 --- /dev/null +++ b/src/PHPWord/Footnote.php @@ -0,0 +1,126 @@ + diff --git a/src/PHPWord/Section.php b/src/PHPWord/Section.php index 10d1ce6d0d..05eb19b60a 100644 --- a/src/PHPWord/Section.php +++ b/src/PHPWord/Section.php @@ -69,8 +69,7 @@ class PHPWord_Section { * @var PHPWord_Section_Footer */ private $_footer = null; - - + /** * Create a new Section * @@ -367,5 +366,19 @@ public function createFooter() { public function getFooter() { return $this->_footer; } + + /** + * Create a new Footnote Element + * + * @param string $text + * @return PHPWord_Section_Footnote + */ + public function createFootnote($styleParagraph = null) { + $footnote = new PHPWord_Section_Footnote($styleParagraph); + $refID = PHPWord_Footnote::addFootnoteElement($footnote); + $footnote->setReferenceId($refID); + $this->_elementCollection[] = $footnote; + return $footnote; + } } ?> \ No newline at end of file diff --git a/src/PHPWord/Section/Footnote.php b/src/PHPWord/Section/Footnote.php new file mode 100644 index 0000000000..f9b21d8b2b --- /dev/null +++ b/src/PHPWord/Section/Footnote.php @@ -0,0 +1,151 @@ +_elementCollection = array(); + + // Set paragraph style + if(is_array($styleParagraph)) { + $this->_styleParagraph = new PHPWord_Style_Paragraph(); + + foreach($styleParagraph as $key => $value) { + if(substr($key, 0, 1) != '_') { + $key = '_'.$key; + } + $this->_styleParagraph->setStyleValue($key, $value); + } + } else { + $this->_styleParagraph = $styleParagraph; + } + } + + + /** + * Add a Text Element + * + * @var string $text + * @var mixed $styleFont + * @return PHPWord_Section_Text + */ + public function addText($text = null, $styleFont = null) { + $givenText = $text; + $text = new PHPWord_Section_Text($givenText, $styleFont); + $this->_elementCollection[] = $text; + return $text; + } + + /** + * Add a Link Element + * + * @param string $linkSrc + * @param string $linkName + * @param mixed $styleFont + * @return PHPWord_Section_Link + */ + public function addLink($linkSrc, $linkName = null, $styleFont = null) { + + $link = new PHPWord_Section_Link($linkSrc, $linkName, $styleFont); + $rID = PHPWord_Footnote::addFootnoteLinkElement($linkSrc); + $link->setRelationId($rID); + + $this->_elementCollection[] = $link; + return $link; + } + + /** + * Get Footnote content + * + * @return string + */ + public function getElements() { + return $this->_elementCollection; + } + + /** + * Get Paragraph style + * + * @return PHPWord_Style_Paragraph + */ + public function getParagraphStyle() { + return $this->_styleParagraph; + } + + /** + * Get Footnote Reference ID + * + * @return int + */ + public function getReferenceId() { + return $this->_refId; + } + + /** + * Set Footnote Reference ID + * + * @param int $refId + */ + public function setReferenceId($refId) { + $this->_refId = $refId; + } + + +} +?> \ No newline at end of file diff --git a/src/PHPWord/Section/TextRun.php b/src/PHPWord/Section/TextRun.php index 4f5eece91a..3ff1ebe539 100644 --- a/src/PHPWord/Section/TextRun.php +++ b/src/PHPWord/Section/TextRun.php @@ -108,6 +108,20 @@ public function addLink($linkSrc, $linkName = null, $styleFont = null) { return $link; } + /** + * Create a new Footnote Element + * + * @param string $text + * @return PHPWord_Section_Footnote + */ + public function createFootnote($styleParagraph = null) { + $footnote = new PHPWord_Section_Footnote($styleParagraph); + $refID = PHPWord_Footnote::addFootnoteElement($footnote); + $footnote->setReferenceId($refID); + $this->_elementCollection[] = $footnote; + return $footnote; + } + /** * Get TextRun content * diff --git a/src/PHPWord/Writer/Word2007.php b/src/PHPWord/Writer/Word2007.php index 0ad491dde2..890fc1e6e4 100644 --- a/src/PHPWord/Writer/Word2007.php +++ b/src/PHPWord/Writer/Word2007.php @@ -48,6 +48,8 @@ public function __construct(PHPWord $PHPWord = null) { $this->_writerParts['styles'] = new PHPWord_Writer_Word2007_Styles(); $this->_writerParts['header'] = new PHPWord_Writer_Word2007_Header(); $this->_writerParts['footer'] = new PHPWord_Writer_Word2007_Footer(); + $this->_writerParts['footnotes'] = new PHPWord_Writer_Word2007_Footnotes(); + $this->_writerParts['footnotesrels'] = new PHPWord_Writer_Word2007_FootnotesRels(); foreach($this->_writerParts as $writer) { $writer->setParentWriter($this); @@ -106,8 +108,12 @@ public function save($pFilename = null) { } } - - + $footnoteLinks = array(); + $_footnoteElements = PHPWord_Footnote::getFootnoteLinkElements(); + foreach($_footnoteElements as $element) { // loop through footnote link elements + $footnoteLinks[] = $element; + } + $_cHdrs = 0; $_cFtrs = 0; $rID = PHPWord_Media::countSectionMediaElements() + 6; @@ -134,7 +140,17 @@ public function save($pFilename = null) { $objZip->addFromString('word/'.$_footerFile, $this->getWriterPart('footer')->writeFooter($_footer)); } } - + + if (PHPWord_Footnote::countFootnoteElements() > 0) { + $_allFootnotesCollection = PHPWord_Footnote::getFootnoteElements(); + $_footnoteFile = 'footnotes.xml'; + $sectionElements[] = array('target'=>$_footnoteFile, 'type'=>'footnotes', 'rID'=>++$rID); + $objZip->addFromString('word/'.$_footnoteFile, $this->getWriterPart('footnotes')->writeFootnotes($_allFootnotesCollection)); + if (count($footnoteLinks) > 0) { + $objZip->addFromString('word/_rels/footnotes.xml.rels', $this->getWriterPart('footnotesrels')->writeFootnotesRels($footnoteLinks)); + } + } + // build docx file // Write dynamic files $objZip->addFromString('[Content_Types].xml', $this->getWriterPart('contenttypes')->writeContentTypes($this->_imageTypes, $this->_objectTypes, $_cHdrs, $_cFtrs)); diff --git a/src/PHPWord/Writer/Word2007/Base.php b/src/PHPWord/Writer/Word2007/Base.php index 1ed5bdeefa..fddf7648c2 100644 --- a/src/PHPWord/Writer/Word2007/Base.php +++ b/src/PHPWord/Writer/Word2007/Base.php @@ -101,6 +101,8 @@ protected function _writeTextRun(PHPWord_Shared_XMLWriter $objWriter = null, PHP $this->_writeText($objWriter, $element, true); } elseif($element instanceof PHPWord_Section_Link) { $this->_writeLink($objWriter, $element, true); + } elseif($element instanceof PHPWord_Section_Footnote) { + $this->_writeFootnoteReference($objWriter, $element, true); } } } @@ -702,5 +704,61 @@ protected function _writeTitle(PHPWord_Shared_XMLWriter $objWriter = null, PHPWo $objWriter->endElement(); } + + protected function _writeFootnote(PHPWord_Shared_XMLWriter $objWriter = null, PHPWord_Section_Footnote $footnote) { + + $objWriter->startElement('w:footnote'); + $objWriter->writeAttribute('w:id', $footnote->getReferenceId()); + + $elements = $footnote->getElements(); + $styleParagraph = $footnote->getParagraphStyle(); + + $SpIsObject = ($styleParagraph instanceof PHPWord_Style_Paragraph) ? true : false; + + $objWriter->startElement('w:p'); + + if($SpIsObject) { + $this->_writeParagraphStyle($objWriter, $styleParagraph); + } elseif(!$SpIsObject && !is_null($styleParagraph)) { + $objWriter->startElement('w:pPr'); + $objWriter->startElement('w:pStyle'); + $objWriter->writeAttribute('w:val', $styleParagraph); + $objWriter->endElement(); + $objWriter->endElement(); + } + + if(count($elements) > 0) { + foreach($elements as $element) { + if($element instanceof PHPWord_Section_Text) { + $this->_writeText($objWriter, $element, true); + } elseif($element instanceof PHPWord_Section_Link) { + $this->_writeLink($objWriter, $element, true); + } + } + } + + $objWriter->endElement(); // w:p + $objWriter->endElement(); // w:footnote + + } + + protected function _writeFootnoteReference(PHPWord_Shared_XMLWriter $objWriter = null, PHPWord_Section_Footnote $footnote, $withoutP = false) { + + if (!$withoutP) { + $objWriter->startElement('w:p'); + } + + $objWriter->startElement('w:r'); + + $objWriter->startElement('w:footnoteReference'); + $objWriter->writeAttribute('w:id', $footnote->getReferenceId()); + $objWriter->endElement(); // w:footnoteReference + + $objWriter->endElement(); // w:r + + if (!$withoutP) { + $objWriter->endElement(); // w:p + } + } } ?> \ No newline at end of file diff --git a/src/PHPWord/Writer/Word2007/Document.php b/src/PHPWord/Writer/Word2007/Document.php index b07e2698c8..279fdb73e5 100644 --- a/src/PHPWord/Writer/Word2007/Document.php +++ b/src/PHPWord/Writer/Word2007/Document.php @@ -89,6 +89,8 @@ public function writeDocument(PHPWord $pPHPWord = null) { $this->_writeObject($objWriter, $element); } elseif($element instanceof PHPWord_TOC) { $this->_writeTOC($objWriter); + } elseif($element instanceof PHPWord_Section_Footnote) { + $this->_writeFootnoteReference($objWriter, $element); } } diff --git a/src/PHPWord/Writer/Word2007/Footnotes.php b/src/PHPWord/Writer/Word2007/Footnotes.php new file mode 100644 index 0000000000..900cfc35c6 --- /dev/null +++ b/src/PHPWord/Writer/Word2007/Footnotes.php @@ -0,0 +1,83 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPWord_Shared_XMLWriter(PHPWord_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPWord_Shared_XMLWriter(PHPWord_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0', 'UTF-8', 'yes'); + + $objWriter->startElement('w:footnotes'); + $objWriter->writeAttribute('xmlns:r','http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $objWriter->writeAttribute('xmlns:w','http://schemas.openxmlformats.org/wordprocessingml/2006/main'); + + // write separator and continuation separator + $objWriter->startElement('w:footnote'); + $objWriter->writeAttribute('w:id', 0); + $objWriter->writeAttribute('w:type', 'separator'); + $objWriter->startElement('w:p'); + $objWriter->startElement('w:r'); + $objWriter->startElement('w:separator'); + $objWriter->endElement(); // w:separator + $objWriter->endElement(); // w:r + $objWriter->endElement(); // w:p + $objWriter->endElement(); // w:footnote + + $objWriter->startElement('w:footnote'); + $objWriter->writeAttribute('w:id', 1); + $objWriter->writeAttribute('w:type', 'continuationSeparator'); + $objWriter->startElement('w:p'); + $objWriter->startElement('w:r'); + $objWriter->startElement('w:continuationSeparator'); + $objWriter->endElement(); // w:continuationSeparator + $objWriter->endElement(); // w:r + $objWriter->endElement(); // w:p + $objWriter->endElement(); // w:footnote + + + foreach($allFootnotesCollection as $footnote) { + if($footnote instanceof PHPWord_Section_Footnote) { + $this->_writeFootnote($objWriter, $footnote); + } + } + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } +} +?> diff --git a/src/PHPWord/Writer/Word2007/FootnotesRels.php b/src/PHPWord/Writer/Word2007/FootnotesRels.php new file mode 100644 index 0000000000..efff47e006 --- /dev/null +++ b/src/PHPWord/Writer/Word2007/FootnotesRels.php @@ -0,0 +1,91 @@ +getParentWriter()->getUseDiskCaching()) { + $objWriter = new PHPWord_Shared_XMLWriter(PHPWord_Shared_XMLWriter::STORAGE_DISK, $this->getParentWriter()->getDiskCachingDirectory()); + } else { + $objWriter = new PHPWord_Shared_XMLWriter(PHPWord_Shared_XMLWriter::STORAGE_MEMORY); + } + + // XML header + $objWriter->startDocument('1.0','UTF-8','yes'); + + // Relationships + $objWriter->startElement('Relationships'); + $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships'); + + // Relationships to Links + foreach($_relsCollection as $relation) { + $relationType = $relation['type']; + $relationName = $relation['target']; + $relationId = $relation['rID']; + $targetMode = ($relationType == 'hyperlink') ? 'External' : ''; + + $this->_writeRelationship( + $objWriter, + $relationId, + 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/'.$relationType, + $relationName, + $targetMode + ); + } + + $objWriter->endElement(); + + // Return + return $objWriter->getData(); + } + + private function _writeRelationship(PHPWord_Shared_XMLWriter $objWriter = null, $pId = 1, $pType = '', $pTarget = '', $pTargetMode = '') { + if($pType != '' && $pTarget != '') { + if(strpos($pId, 'rId') === false) { + $pId = 'rId' . $pId; + } + + // Write relationship + $objWriter->startElement('Relationship'); + $objWriter->writeAttribute('Id', $pId); + $objWriter->writeAttribute('Type', $pType); + $objWriter->writeAttribute('Target', $pTarget); + + if($pTargetMode != '') { + $objWriter->writeAttribute('TargetMode', $pTargetMode); + } + + $objWriter->endElement(); + } else { + throw new Exception("Invalid parameters passed."); + } + } +} +?> \ No newline at end of file