From 86abe7e275eaffb3f817cd0e06b1603a25264157 Mon Sep 17 00:00:00 2001 From: Progi1984 Date: Fri, 7 Mar 2014 23:08:09 +0100 Subject: [PATCH] GH-16 : Initial addition of basic footnote support --- Classes/PHPWord/Footnote.php | 125 +++++++++++++++ Classes/PHPWord/Section.php | 14 ++ Classes/PHPWord/Section/Footnote.php | 148 ++++++++++++++++++ Classes/PHPWord/Section/TextRun.php | 14 ++ Classes/PHPWord/Writer/Word2007.php | 17 ++ Classes/PHPWord/Writer/Word2007/Base.php | 54 +++++++ Classes/PHPWord/Writer/Word2007/Document.php | 2 + Classes/PHPWord/Writer/Word2007/Footnotes.php | 81 ++++++++++ .../PHPWord/Writer/Word2007/FootnotesRels.php | 89 +++++++++++ changelog.txt | 1 + samples/Sample_01_SimpleText.php | 3 +- samples/Sample_08_Footnote.php | 54 +++++++ 12 files changed, 600 insertions(+), 2 deletions(-) create mode 100644 Classes/PHPWord/Footnote.php create mode 100644 Classes/PHPWord/Section/Footnote.php create mode 100644 Classes/PHPWord/Writer/Word2007/Footnotes.php create mode 100644 Classes/PHPWord/Writer/Word2007/FootnotesRels.php create mode 100755 samples/Sample_08_Footnote.php diff --git a/Classes/PHPWord/Footnote.php b/Classes/PHPWord/Footnote.php new file mode 100644 index 00000000..81054843 --- /dev/null +++ b/Classes/PHPWord/Footnote.php @@ -0,0 +1,125 @@ +_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/Classes/PHPWord/Section/Footnote.php b/Classes/PHPWord/Section/Footnote.php new file mode 100644 index 00000000..7a7d8d6c --- /dev/null +++ b/Classes/PHPWord/Section/Footnote.php @@ -0,0 +1,148 @@ +_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/Classes/PHPWord/Section/TextRun.php b/Classes/PHPWord/Section/TextRun.php index 44473e63..7c03138b 100755 --- a/Classes/PHPWord/Section/TextRun.php +++ b/Classes/PHPWord/Section/TextRun.php @@ -130,6 +130,20 @@ class PHPWord_Section_TextRun } } + /** + * 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/Classes/PHPWord/Writer/Word2007.php b/Classes/PHPWord/Writer/Word2007.php index 6dfc95d3..b55fb556 100755 --- a/Classes/PHPWord/Writer/Word2007.php +++ b/Classes/PHPWord/Writer/Word2007.php @@ -52,6 +52,8 @@ class PHPWord_Writer_Word2007 implements PHPWord_Writer_IWriter $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); @@ -111,6 +113,11 @@ class PHPWord_Writer_Word2007 implements PHPWord_Writer_IWriter } } + $footnoteLinks = array(); + $_footnoteElements = PHPWord_Footnote::getFootnoteLinkElements(); + foreach($_footnoteElements as $element) { // loop through footnote link elements + $footnoteLinks[] = $element; + } $_cHdrs = 0; $_cFtrs = 0; @@ -138,6 +145,16 @@ class PHPWord_Writer_Word2007 implements PHPWord_Writer_IWriter } } + 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/Classes/PHPWord/Writer/Word2007/Base.php b/Classes/PHPWord/Writer/Word2007/Base.php index d9d2cb09..47c31a81 100755 --- a/Classes/PHPWord/Writer/Word2007/Base.php +++ b/Classes/PHPWord/Writer/Word2007/Base.php @@ -108,6 +108,8 @@ class PHPWord_Writer_Word2007_Base extends PHPWord_Writer_Word2007_WriterPart $this->_writeLink($objWriter, $element, true); } elseif ($element instanceof PHPWord_Section_Image) { $this->_writeImage($objWriter, $element, true); + } elseif($element instanceof PHPWord_Section_Footnote) { + $this->_writeFootnoteReference($objWriter, $element, true); } } } @@ -827,4 +829,56 @@ class PHPWord_Writer_Word2007_Base extends PHPWord_Writer_Word2007_WriterPart $objWriter->endElement(); } + + protected function _writeFootnote(PHPWord_Shared_XMLWriter $objWriter = null, PHPWord_Section_Footnote $footnote) { + $objWriter->startElement('w:footnote'); + $objWriter->writeAttribute('w:id', $footnote->getReferenceId()); + + $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(); + } + + $elements = $footnote->getElements(); + 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/Classes/PHPWord/Writer/Word2007/Document.php b/Classes/PHPWord/Writer/Word2007/Document.php index 9fc2160a..044dbcec 100755 --- a/Classes/PHPWord/Writer/Word2007/Document.php +++ b/Classes/PHPWord/Writer/Word2007/Document.php @@ -94,6 +94,8 @@ class PHPWord_Writer_Word2007_Document extends PHPWord_Writer_Word2007_Base $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/Classes/PHPWord/Writer/Word2007/Footnotes.php b/Classes/PHPWord/Writer/Word2007/Footnotes.php new file mode 100644 index 00000000..1bf27ee4 --- /dev/null +++ b/Classes/PHPWord/Writer/Word2007/Footnotes.php @@ -0,0 +1,81 @@ +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(); + } +} \ No newline at end of file diff --git a/Classes/PHPWord/Writer/Word2007/FootnotesRels.php b/Classes/PHPWord/Writer/Word2007/FootnotesRels.php new file mode 100644 index 00000000..22533815 --- /dev/null +++ b/Classes/PHPWord/Writer/Word2007/FootnotesRels.php @@ -0,0 +1,89 @@ +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 diff --git a/changelog.txt b/changelog.txt index 381b4708..c5c66fc3 100755 --- a/changelog.txt +++ b/changelog.txt @@ -43,6 +43,7 @@ Changes in branch for release 0.7.1 : - Feature: (ivanlanin) GH-87 - Paragraph: Ability to define normal paragraph style with PHPWord::setNormalStyle() - Feature: (ivanlanin) GH-87 - Paragraph: Ability to define parent style (basedOn) and style for following paragraph (next) - Feature: (jeroenmoors) GH-44 GH-88 - Clone table rows on the fly when using a template document +- Feature: (deds) GH-16 - Initial addition of basic footnote support - QA: (Progi1984) - UnitTests Changes in branch for release 0.7.0 : diff --git a/samples/Sample_01_SimpleText.php b/samples/Sample_01_SimpleText.php index b2d6593a..4b055cca 100755 --- a/samples/Sample_01_SimpleText.php +++ b/samples/Sample_01_SimpleText.php @@ -4,8 +4,7 @@ error_reporting(E_ALL); if(php_sapi_name() == 'cli' && empty($_SERVER['REMOTE_ADDR'])) { define('EOL', PHP_EOL); -} -else { +} else { define('EOL', '
'); } diff --git a/samples/Sample_08_Footnote.php b/samples/Sample_08_Footnote.php new file mode 100755 index 00000000..123dedcc --- /dev/null +++ b/samples/Sample_08_Footnote.php @@ -0,0 +1,54 @@ +'); +} +require_once '../Classes/PHPWord.php'; + +// New Word Document +echo date('H:i:s') , " Create new PHPWord object" , EOL; +$PHPWord = new PHPWord(); + +// New portrait section +$section = $PHPWord->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 +echo date('H:i:s') , " Write to Word2007 format" , EOL; +$objWriter = PHPWord_IOFactory::createWriter($PHPWord, 'Word2007'); +$objWriter->save(str_replace('.php', '.docx', __FILE__)); + + +// Echo memory peak usage +echo date('H:i:s') , " Peak memory usage: " , (memory_get_peak_usage(true) / 1024 / 1024) , " MB" , EOL; + +// Echo done +echo date('H:i:s') , " Done writing file" , EOL;