Breakdown HTML Writer to head and body parts and more cyclomatic complexity reductions

This commit is contained in:
Ivan Lanin 2014-05-23 12:48:34 +07:00
parent d79ca9ae31
commit e00b551aa2
12 changed files with 391 additions and 245 deletions

View File

@ -46,6 +46,7 @@ class Autoloader
$file = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, $prefixLength));
$file = realpath(__DIR__ . (empty($file) ? '' : DIRECTORY_SEPARATOR) . $file . '.php');
if (file_exists($file)) {
/** @noinspection PhpIncludeInspection Dynamic includes */
require_once $file;
}
}

View File

@ -158,7 +158,9 @@ class Document extends AbstractPart
// Section properties
if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) {
$sectPrNode = $xmlReader->getElement('w:pPr/w:sectPr', $node);
$this->readWSectPrNode($xmlReader, $sectPrNode, $section);
if ($sectPrNode !== null) {
$this->readWSectPrNode($xmlReader, $sectPrNode, $section);
}
$section = $this->phpWord->addSection();
}
}
@ -172,10 +174,8 @@ class Document extends AbstractPart
*/
private function readWSectPrNode(XMLReader $xmlReader, \DOMElement $node, Section &$section)
{
if ($node !== null) {
$settings = $this->readSectionStyle($xmlReader, $node);
$section->setSettings($settings);
$this->readHeaderFooter($settings, $section);
}
$settings = $this->readSectionStyle($xmlReader, $node);
$section->setSettings($settings);
$this->readHeaderFooter($settings, $section);
}
}

View File

@ -86,14 +86,28 @@ class String
}
/**
* Returns unicode from UTF8 text
* Returns unicode array from UTF8 text
*
* The function is splitted to reduce cyclomatic complexity
*
* @param string $text UTF8 text
* @return string Unicode text
* @since 0.11.0
* @link http://www.randomchaos.com/documents/?source=php_and_unicode
*/
public static function toUnicode($text)
{
return self::unicodeToEntities(self::utf8ToUnicode($text));
}
/**
* Returns unicode from UTF8 text
*
* @param string $text UTF8 text
* @return array
* @since 0.11.0
* @link http://www.randomchaos.com/documents/?source=php_and_unicode
*/
private static function utf8ToUnicode($text)
{
$unicode = array();
$values = array();
@ -122,8 +136,21 @@ class String
}
}
// Converts text with utf8 characters into rtf utf8 entites preserving ascii
return $unicode;
}
/**
* Returns entites from unicode array
*
* @param array $unicode
* @return string
* @since 0.11.0
* @link http://www.randomchaos.com/documents/?source=php_and_unicode
*/
private static function unicodeToEntities($unicode)
{
$entities = '';
foreach ($unicode as $value) {
if ($value != 65279) {
$entities .= $value > 127 ? '\uc0{\u' . $value . '}' : chr($value);

View File

@ -19,15 +19,6 @@ namespace PhpOffice\PhpWord\Writer;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\Writer\HTML\Element\Container;
use PhpOffice\PhpWord\Writer\HTML\Element\TextRun as TextRunWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Font as FontStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Generic as GenericStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph as ParagraphStyleWriter;
/**
* HTML writer
@ -57,6 +48,17 @@ class HTML extends AbstractWriter implements WriterInterface
public function __construct(PhpWord $phpWord = null)
{
$this->setPhpWord($phpWord);
$this->parts = array('Head', 'Body');
foreach ($this->parts as $partName) {
$partClass = 'PhpOffice\\PhpWord\\Writer\\HTML\\Part\\' . $partName;
if (class_exists($partClass)) {
/** @var \PhpOffice\PhpWord\Writer\HTML\Part\AbstractPart $part Type hint */
$part = new $partClass();
$part->setParentWriter($this);
$this->writerParts[strtolower($partName)] = $part;
}
}
}
/**
@ -89,164 +91,13 @@ class HTML extends AbstractWriter implements WriterInterface
$content .= '<!DOCTYPE html>' . PHP_EOL;
$content .= '<!-- Generated by PHPWord -->' . PHP_EOL;
$content .= '<html>' . PHP_EOL;
$content .= '<head>' . PHP_EOL;
$content .= $this->writeHead();
$content .= '</head>' . PHP_EOL;
$content .= '<body>' . PHP_EOL;
$content .= $this->writeBody();
$content .= $this->writeNotes();
$content .= '</body>' . PHP_EOL;
$content .= $this->getWriterPart('Head')->write();
$content .= $this->getWriterPart('Body')->write();
$content .= '</html>' . PHP_EOL;
return $content;
}
/**
* Generate HTML header
*
* @return string
*/
private function writeHead()
{
$phpWord = $this->getPhpWord();
$properties = $phpWord->getDocumentProperties();
$propertiesMapping = array(
'creator' => 'author',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'company' => '',
'manager' => ''
);
$title = $properties->getTitle();
$title = ($title != '') ? $title : 'PHPWord';
$content = '';
$content .= '<meta charset="UTF-8" />' . PHP_EOL;
$content .= '<title>' . htmlspecialchars($title) . '</title>' . PHP_EOL;
foreach ($propertiesMapping as $key => $value) {
$value = ($value == '') ? $key : $value;
$method = "get" . $key;
if ($properties->$method() != '') {
$content .= '<meta name="' . $value . '" content="' .
htmlspecialchars($properties->$method()) . '" />' . PHP_EOL;
}
}
$content .= $this->writeStyles();
return $content;
}
/**
* Get content
*
* @return string
*/
private function writeBody()
{
$phpWord = $this->getPhpWord();
$content = '';
$sections = $phpWord->getSections();
$countSections = count($sections);
if ($countSections > 0) {
foreach ($sections as $section) {
$writer = new Container($this, $section);
$content .= $writer->write();
}
}
return $content;
}
/**
* Get styles
*
* @return string
*/
private function writeStyles()
{
$css = '<style>' . PHP_EOL;
// Default styles
$defaultStyles = array(
'*' => array(
'font-family' => Settings::getDefaultFontName(),
'font-size' => Settings::getDefaultFontSize() . 'pt',
),
'a.NoteRef' => array(
'text-decoration' => 'none',
),
'hr' => array(
'height' => '1px',
'padding' => '0',
'margin' => '1em 0',
'border' => '0',
'border-top' => '1px solid #CCC',
),
);
foreach ($defaultStyles as $selector => $style) {
$styleWriter = new GenericStyleWriter($style);
$css .= $selector . ' {' . $styleWriter->write() . '}' . PHP_EOL;
}
// Custom styles
$customStyles = Style::getStyles();
if (is_array($customStyles)) {
foreach ($customStyles as $name => $style) {
if ($style instanceof Font) {
$styleWriter = new FontStyleWriter($style);
if ($style->getStyleType() == 'title') {
$name = str_replace('Heading_', 'h', $name);
} else {
$name = '.' . $name;
}
$css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
} elseif ($style instanceof Paragraph) {
$styleWriter = new ParagraphStyleWriter($style);
$name = '.' . $name;
$css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
}
}
}
$css .= '</style>' . PHP_EOL;
return $css;
}
/**
* Write footnote/endnote contents as textruns
*/
private function writeNotes()
{
$phpWord = $this->getPhpWord();
$content = PHP_EOL;
if (!empty($this->notes)) {
$content .= "<hr />" . PHP_EOL;
foreach ($this->notes as $noteId => $noteMark) {
list($noteType, $noteTypeId) = explode('-', $noteMark);
$method = 'get' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes');
$collection = $phpWord->$method()->getItems();
if (array_key_exists($noteTypeId, $collection)) {
$element = $collection[$noteTypeId];
$noteAnchor = "<a name=\"note-{$noteId}\" />";
$noteAnchor .= "<a href=\"#{$noteMark}\" class=\"NoteRef\"><sup>{$noteId}</sup></a>";
$writer = new TextRunWriter($this, $element);
$writer->setOpeningText($noteAnchor);
$content .= $writer->write();
}
}
}
return $content;
}
/**
* Get is PDF
*

View File

@ -88,7 +88,7 @@ class Image extends Text
} else {
$actualSource = $source;
}
if (is_null($actualSource)) {
if ($actualSource === null) {
return null;
}
@ -100,12 +100,13 @@ class Image extends Text
$imageBinary = ob_get_contents();
ob_end_clean();
} else {
if ($fileHandle = fopen($actualSource, 'rb', false)) {
$fileHandle = fopen($actualSource, 'rb', false);
if ($fileHandle !== false) {
$imageBinary = fread($fileHandle, filesize($actualSource));
fclose($fileHandle);
}
}
if (!is_null($imageBinary)) {
if ($imageBinary !== null) {
$base64 = chunk_split(base64_encode($imageBinary));
$imageData = 'data:' . $imageType . ';base64,' . $base64;
}

View File

@ -0,0 +1,68 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2010-2014 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord\Writer\HTML\Part;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Writer\AbstractWriter;
/**
* Abstract HTML part writer
*
* @since 0.11.0
*/
abstract class AbstractPart
{
/**
* Parent writer
*
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
*/
private $parentWriter;
/**
* Write part
*
* @return string
*/
abstract public function write();
/**
* Set parent writer
*
* @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer
*/
public function setParentWriter(AbstractWriter $writer = null)
{
$this->parentWriter = $writer;
}
/**
* Get parent writer
*
* @return \PhpOffice\PhpWord\Writer\AbstractWriter
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function getParentWriter()
{
if ($this->parentWriter !== null) {
return $this->parentWriter;
} else {
throw new Exception('No parent WriterInterface assigned.');
}
}
}

View File

@ -0,0 +1,89 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2010-2014 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord\Writer\HTML\Part;
use PhpOffice\PhpWord\Writer\HTML\Element\Container;
use PhpOffice\PhpWord\Writer\HTML\Element\TextRun as TextRunWriter;
/**
* RTF body part writer
*
* @since 0.11.0
*/
class Body extends AbstractPart
{
/**
* Write part
*
* @return string
*/
public function write()
{
$phpWord = $this->getParentWriter()->getPhpWord();
$content = '';
$content .= '<body>' . PHP_EOL;
$sections = $phpWord->getSections();
foreach ($sections as $section) {
$writer = new Container($this->getParentWriter(), $section);
$content .= $writer->write();
}
$content .= $this->writeNotes();
$content .= '</body>' . PHP_EOL;
return $content;
}
/**
* Write footnote/endnote contents as textruns
*
* @return string
*/
private function writeNotes()
{
/** @var \PhpOffice\PhpWord\Writer\HTML $parentWriter Type hint */
$parentWriter = $this->getParentWriter();
$phpWord = $parentWriter->getPhpWord();
$notes = $parentWriter->getNotes();
$content = '';
if (!empty($notes)) {
$content .= "<hr />" . PHP_EOL;
foreach ($notes as $noteId => $noteMark) {
list($noteType, $noteTypeId) = explode('-', $noteMark);
$method = 'get' . ($noteType == 'endnote' ? 'Endnotes' : 'Footnotes');
$collection = $phpWord->$method()->getItems();
if (array_key_exists($noteTypeId, $collection)) {
$element = $collection[$noteTypeId];
$noteAnchor = "<a name=\"note-{$noteId}\" />";
$noteAnchor .= "<a href=\"#{$noteMark}\" class=\"NoteRef\"><sup>{$noteId}</sup></a>";
$writer = new TextRunWriter($this->getParentWriter(), $element);
$writer->setOpeningText($noteAnchor);
$content .= $writer->write();
}
}
}
return $content;
}
}

View File

@ -0,0 +1,129 @@
<?php
/**
* This file is part of PHPWord - A pure PHP library for reading and writing
* word processing documents.
*
* PHPWord is free software distributed under the terms of the GNU Lesser
* General Public License version 3 as published by the Free Software Foundation.
*
* For the full copyright and license information, please read the LICENSE
* file that was distributed with this source code. For the full list of
* contributors, visit https://github.com/PHPOffice/PHPWord/contributors.
*
* @link https://github.com/PHPOffice/PHPWord
* @copyright 2010-2014 PHPWord contributors
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
namespace PhpOffice\PhpWord\Writer\HTML\Part;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWord\Writer\HTML\Style\Font as FontStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Generic as GenericStyleWriter;
use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph as ParagraphStyleWriter;
/**
* RTF head part writer
*
* @since 0.11.0
*/
class Head extends AbstractPart
{
/**
* Write part
*
* @return string
*/
public function write()
{
$docProps = $this->getParentWriter()->getPhpWord()->getDocumentProperties();
$propertiesMapping = array(
'creator' => 'author',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'company' => '',
'manager' => ''
);
$title = $docProps->getTitle();
$title = ($title != '') ? $title : 'PHPWord';
$content = '';
$content .= '<head>' . PHP_EOL;
$content .= '<meta charset="UTF-8" />' . PHP_EOL;
$content .= '<title>' . htmlspecialchars($title) . '</title>' . PHP_EOL;
foreach ($propertiesMapping as $key => $value) {
$value = ($value == '') ? $key : $value;
$method = "get" . $key;
if ($docProps->$method() != '') {
$content .= '<meta name="' . $value . '" content="' .
htmlspecialchars($docProps->$method()) . '" />' . PHP_EOL;
}
}
$content .= $this->writeStyles();
$content .= '</head>' . PHP_EOL;
return $content;
}
/**
* Get styles
*
* @return string
*/
private function writeStyles()
{
$css = '<style>' . PHP_EOL;
// Default styles
$defaultStyles = array(
'*' => array(
'font-family' => Settings::getDefaultFontName(),
'font-size' => Settings::getDefaultFontSize() . 'pt',
),
'a.NoteRef' => array(
'text-decoration' => 'none',
),
'hr' => array(
'height' => '1px',
'padding' => '0',
'margin' => '1em 0',
'border' => '0',
'border-top' => '1px solid #CCC',
),
);
foreach ($defaultStyles as $selector => $style) {
$styleWriter = new GenericStyleWriter($style);
$css .= $selector . ' {' . $styleWriter->write() . '}' . PHP_EOL;
}
// Custom styles
$customStyles = Style::getStyles();
if (is_array($customStyles)) {
foreach ($customStyles as $name => $style) {
if ($style instanceof Font) {
$styleWriter = new FontStyleWriter($style);
if ($style->getStyleType() == 'title') {
$name = str_replace('Heading_', 'h', $name);
} else {
$name = '.' . $name;
}
$css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
} elseif ($style instanceof Paragraph) {
$styleWriter = new ParagraphStyleWriter($style);
$name = '.' . $name;
$css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
}
}
}
$css .= '</style>' . PHP_EOL;
return $css;
}
}

View File

@ -84,6 +84,7 @@ abstract class AbstractRenderer extends HTML
parent::__construct($phpWord);
$includeFile = Settings::getPdfRendererPath() . '/' . $this->includeFile;
if (file_exists($includeFile)) {
/** @noinspection PhpIncludeInspection Dynamic includes */
require_once $includeFile;
} else {
throw new Exception('Unable to load PDF Rendering library');

View File

@ -35,7 +35,8 @@ class RTF extends AbstractWriter implements WriterInterface
private $lastParagraphStyle;
/**
* Create new RTF writer
* Create new instance
*
* @param \PhpOffice\PhpWord\PhpWord $phpWord
*/
public function __construct(PhpWord $phpWord = null)
@ -52,7 +53,6 @@ class RTF extends AbstractWriter implements WriterInterface
$this->writerParts[strtolower($partName)] = $part;
}
}
}
/**

View File

@ -17,52 +17,13 @@
namespace PhpOffice\PhpWord\Writer\RTF\Part;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Writer\AbstractWriter;
use PhpOffice\PhpWord\Writer\HTML\Part\AbstractPart as HTMLAbstractPart;
/**
* Abstract RTF part writer
*
* @since 0.11.0
*/
abstract class AbstractPart
abstract class AbstractPart extends HTMLAbstractPart
{
/**
* Parent writer
*
* @var \PhpOffice\PhpWord\Writer\AbstractWriter
*/
private $parentWriter;
/**
* Write part
*
* @return string
*/
abstract public function write();
/**
* Set parent writer
*
* @param \PhpOffice\PhpWord\Writer\AbstractWriter $writer
*/
public function setParentWriter(AbstractWriter $writer = null)
{
$this->parentWriter = $writer;
}
/**
* Get parent writer
*
* @return \PhpOffice\PhpWord\Writer\AbstractWriter
* @throws \PhpOffice\PhpWord\Exception\Exception
*/
public function getParentWriter()
{
if ($this->parentWriter !== null) {
return $this->parentWriter;
} else {
throw new Exception('No parent WriterInterface assigned.');
}
}
}

View File

@ -17,8 +17,10 @@
namespace PhpOffice\PhpWord\Writer\Word2007\Element;
use PhpOffice\PhpWord\Element\AbstractElement as Element;
use PhpOffice\PhpWord\Element\AbstractContainer as ContainerElement;
use PhpOffice\PhpWord\Element\TextBreak as TextBreakElement;
use PhpOffice\PhpWord\Shared\XMLWriter;
/**
* Container element writer (section, textrun, header, footnote, cell, etc.)
@ -51,36 +53,52 @@ class Container extends AbstractElement
$elements = $container->getElements();
$elementClass = '';
foreach ($elements as $element) {
$elementClass = substr(get_class($element), strrpos(get_class($element), '\\') + 1);
$writerClass = $this->namespace . '\\' . $elementClass;
// Check it's a page break. No need to write it, instead, flag containers' pageBreakBefore
// to be assigned to the next element
if ($elementClass == 'PageBreak') {
$this->setPageBreakBefore(true);
continue;
}
if (class_exists($writerClass)) {
// Get container's page break before and reset it
$pageBreakBefore = $this->hasPageBreakBefore();
$this->setPageBreakBefore(false);
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
$writer = new $writerClass($xmlWriter, $element, $withoutP);
$writer->setPageBreakBefore($pageBreakBefore);
$writer->write();
}
$elementClass = $this->writeElement($xmlWriter, $element, $withoutP);
}
// Special case for Cell: They have to contain a w:p element at the end. The $elementClass contains
// the last element name. If it's empty string or Table, the last element is not w:p
if ($containerClass == 'Cell') {
if ($elementClass == '' || $elementClass == 'Table') {
$writerClass = $this->namespace . '\\TextBreak';
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
$writer = new $writerClass($xmlWriter, new TextBreakElement(), $withoutP);
$writer->write();
}
// Special case for Cell: They have to contain a w:p element at the end.
// The $elementClass contains the last element name. If it's empty string
// or Table, the last element is not w:p
$writeLastTextBreak = ($containerClass == 'Cell') && ($elementClass == '' || $elementClass == 'Table');
if ($writeLastTextBreak) {
$writerClass = $this->namespace . '\\TextBreak';
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
$writer = new $writerClass($xmlWriter, new TextBreakElement(), $withoutP);
$writer->write();
}
}
/**
* Write individual element
*
* @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter
* @param \PhpOffice\PhpWord\Element\AbstractElement $element
* @param bool $withoutP
* @return string
*/
private function writeElement(XMLWriter $xmlWriter, Element $element, $withoutP)
{
$elementClass = substr(get_class($element), strrpos(get_class($element), '\\') + 1);
$writerClass = $this->namespace . '\\' . $elementClass;
// Check it's a page break. No need to write it, instead, flag containers'
// pageBreakBefore to be assigned to the next element
if ($elementClass == 'PageBreak') {
$this->setPageBreakBefore(true);
return $elementClass;
}
if (class_exists($writerClass)) {
// Get container's page break before and reset it
$pageBreakBefore = $this->hasPageBreakBefore();
$this->setPageBreakBefore(false);
/** @var \PhpOffice\PhpWord\Writer\Word2007\Element\AbstractElement $writer Type hint */
$writer = new $writerClass($xmlWriter, $element, $withoutP);
$writer->setPageBreakBefore($pageBreakBefore);
$writer->write();
}
return $elementClass;
}
}