748 lines
23 KiB
PHP
Raw Normal View History

2014-04-12 10:12:24 +07:00
<?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\Writer;
use PhpOffice\PhpWord\Element\Endnote;
use PhpOffice\PhpWord\Element\Footnote;
use PhpOffice\PhpWord\Element\Image;
use PhpOffice\PhpWord\Element\Link;
use PhpOffice\PhpWord\Element\ListItem;
use PhpOffice\PhpWord\Element\Object;
use PhpOffice\PhpWord\Element\PageBreak;
2014-04-13 23:17:39 +07:00
use PhpOffice\PhpWord\Element\PreserveText;
2014-04-12 10:12:24 +07:00
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\Element\Text;
use PhpOffice\PhpWord\Element\TextBreak;
use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\Element\Title;
use PhpOffice\PhpWord\Endnotes;
2014-04-12 10:12:24 +07:00
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\Footnotes;
2014-04-12 10:12:24 +07:00
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font;
use PhpOffice\PhpWord\Style\Paragraph;
/**
* HTML writer
*
* @since 0.10.0
*/
class HTML extends AbstractWriter implements WriterInterface
{
2014-04-16 12:12:32 +07:00
/**
* Is the current writer creating PDF?
*
* @var boolean
*/
protected $isPdf = false;
/**
* Footnotes and endnotes collection
*
* @var array
*/
protected $notes = array();
2014-04-12 10:12:24 +07:00
/**
* Create new instance
*/
public function __construct(PhpWord $phpWord = null)
{
$this->setPhpWord($phpWord);
}
/**
* Save PhpWord to file
*
* @param string $filename
* @throws Exception
*/
public function save($filename = null)
{
if (!is_null($this->getPhpWord())) {
2014-04-16 13:37:48 +07:00
$this->setTempDir(sys_get_temp_dir() . '/PHPWordWriter/');
2014-04-12 10:12:24 +07:00
$hFile = fopen($filename, 'w') or die("can't open file");
fwrite($hFile, $this->writeDocument());
fclose($hFile);
2014-04-16 12:12:32 +07:00
$this->clearTempDir();
2014-04-12 10:12:24 +07:00
} else {
throw new Exception("No PHPWord assigned.");
}
}
/**
* Get phpWord data
*
* @return string
*/
2014-04-13 23:17:39 +07:00
public function writeDocument()
2014-04-12 10:12:24 +07:00
{
$html = '';
$html .= '<!DOCTYPE html>' . PHP_EOL;
$html .= '<!-- Generated by PHPWord -->' . PHP_EOL;
$html .= '<html>' . PHP_EOL;
$html .= '<head>' . PHP_EOL;
$html .= $this->writeHTMLHead();
$html .= '</head>' . PHP_EOL;
$html .= '<body>' . PHP_EOL;
$html .= $this->writeHTMLBody();
$html .= $this->writeNotes();
2014-04-12 10:12:24 +07:00
$html .= '</body>' . PHP_EOL;
$html .= '</html>' . PHP_EOL;
return $html;
}
/**
* Generate HTML header
*
* @return string
*/
2014-04-13 23:17:39 +07:00
private function writeHTMLHead()
2014-04-12 10:12:24 +07:00
{
$properties = $this->getPhpWord()->getDocumentProperties();
$propertiesMapping = array(
'creator' => 'author',
'title' => '',
'description' => '',
'subject' => '',
'keywords' => '',
'category' => '',
'company' => '',
'manager' => ''
);
$title = $properties->getTitle();
$title = ($title != '') ? $title : 'PHPWord';
$html = '';
$html .= '<meta charset="UTF-8" />' . PHP_EOL;
$html .= '<title>' . htmlspecialchars($title) . '</title>' . PHP_EOL;
foreach ($propertiesMapping as $key => $value) {
$value = ($value == '') ? $key : $value;
$method = "get" . $key;
if ($properties->$method() != '') {
$html .= '<meta name="' . $value . '" content="' .
htmlspecialchars($properties->$method()) . '" />' . PHP_EOL;
}
}
$html .= $this->writeStyles();
return $html;
}
/**
* Get content
*
* @return string
*/
2014-04-13 23:17:39 +07:00
private function writeHTMLBody()
2014-04-12 10:12:24 +07:00
{
$phpWord = $this->getPhpWord();
$html = '';
$sections = $phpWord->getSections();
$countSections = count($sections);
$pSection = 0;
if ($countSections > 0) {
foreach ($sections as $section) {
$pSection++;
2014-04-13 23:17:39 +07:00
$contents = $section->getElements();
foreach ($contents as $element) {
2014-04-12 10:12:24 +07:00
if ($element instanceof Text) {
$html .= $this->writeText($element);
} elseif ($element instanceof TextRun) {
$html .= $this->writeTextRun($element);
} elseif ($element instanceof Link) {
$html .= $this->writeLink($element);
} elseif ($element instanceof Title) {
$html .= $this->writeTitle($element);
} elseif ($element instanceof PreserveText) {
$html .= $this->writePreserveText($element);
} elseif ($element instanceof TextBreak) {
$html .= $this->writeTextBreak($element);
} elseif ($element instanceof PageBreak) {
$html .= $this->writePageBreak($element);
} elseif ($element instanceof Table) {
$html .= $this->writeTable($element);
} elseif ($element instanceof ListItem) {
$html .= $this->writeListItem($element);
} elseif ($element instanceof Image) {
$html .= $this->writeImage($element);
} elseif ($element instanceof Object) {
$html .= $this->writeObject($element);
} elseif ($element instanceof Endnote) {
2014-04-13 23:17:39 +07:00
$html .= $this->writeEndnote($element);
} elseif ($element instanceof Footnote) {
$html .= $this->writeFootnote($element);
2014-04-12 10:12:24 +07:00
}
}
}
}
return $html;
}
/**
* Write footnote/endnote contents
*/
private function writeNotes()
{
$footnote = Footnotes::getElements();
$endnote = Endnotes::getElements();
$html = '';
if (count($this->notes) > 0) {
$html .= "<hr />";
foreach ($this->notes as $noteId => $noteMark) {
$noteAnchor = "note-{$noteId}";
list($noteType, $noteTypeId) = explode('-', $noteMark);
$collection = $$noteType;
if (array_key_exists($noteTypeId, $collection)) {
$element = $collection[$noteTypeId];
$content = "<a href=\"#{$noteMark}\" class=\"NoteRef\"><sup>{$noteId}</sup></a>" . $this->writeTextRun($element, true);
$html .= "<p><a name=\"{$noteAnchor}\" />{$content}</p>" . PHP_EOL;
}
}
}
return $html;
}
2014-04-12 10:12:24 +07:00
/**
* Get text
*
* @param \PhpOffice\PhpWord\Element\Text $text
2014-04-12 10:12:24 +07:00
* @param boolean $withoutP
* @return string
*/
private function writeText($text, $withoutP = false)
{
$html = '';
$paragraphStyle = $text->getParagraphStyle();
$spIsObject = ($paragraphStyle instanceof Paragraph);
$fontStyle = $text->getFontStyle();
$sfIsObject = ($fontStyle instanceof Font);
if ($paragraphStyle && !$withoutP) {
$html .= '<p';
if (!$spIsObject) {
$html .= ' class="' . $paragraphStyle . '"';
} else {
$html .= ' style="' . $this->writeParagraphStyle($paragraphStyle) . '"';
}
$html .= '>';
}
if ($fontStyle) {
$html .= '<span';
if (!$sfIsObject) {
$html .= ' class="' . $fontStyle . '"';
} else {
$html .= ' style="' . $this->writeFontStyle($fontStyle) . '"';
}
$html .= '>';
}
$html .= htmlspecialchars($text->getText());
if ($fontStyle) {
$html .= '</span>';
}
if ($paragraphStyle && !$withoutP) {
$html .= '</p>' . PHP_EOL;
}
return $html;
}
/**
* Write text run content
2014-04-12 10:12:24 +07:00
*
* @param mixed $textrun
* @param boolean $withoutP
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeTextRun($textrun, $withoutP = false)
2014-04-12 10:12:24 +07:00
{
$html = '';
$elements = $textrun->getElements();
if (count($elements) > 0) {
$paragraphStyle = $textrun->getParagraphStyle();
$spIsObject = ($paragraphStyle instanceof Paragraph);
$html .= $withoutP ? '<span' : '<p';
2014-04-12 10:12:24 +07:00
if ($paragraphStyle) {
if (!$spIsObject) {
$html .= ' class="' . $paragraphStyle . '"';
} else {
$html .= ' style="' . $this->writeParagraphStyle($paragraphStyle) . '"';
}
}
$html .= '>';
foreach ($elements as $element) {
if ($element instanceof Text) {
$html .= $this->writeText($element, true);
} elseif ($element instanceof Link) {
$html .= $this->writeLink($element, true);
} elseif ($element instanceof TextBreak) {
$html .= $this->writeTextBreak($element, true);
} elseif ($element instanceof Image) {
$html .= $this->writeImage($element, true);
} elseif ($element instanceof Endnote) {
2014-04-13 23:17:39 +07:00
$html .= $this->writeEndnote($element);
} elseif ($element instanceof Footnote) {
$html .= $this->writeFootnote($element);
2014-04-12 10:12:24 +07:00
}
}
$html .= $withoutP ? '</span>' : '</p>';
$html .= PHP_EOL;
2014-04-12 10:12:24 +07:00
}
return $html;
}
/**
* Write link
*
* @param \PhpOffice\PhpWord\Element\Link $element
2014-04-13 23:17:39 +07:00
* @param boolean $withoutP
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeLink($element, $withoutP = false)
{
$url = $element->getLinkSrc();
$text = $element->getLinkName();
if ($text == '') {
$text = $url;
}
2014-04-12 10:12:24 +07:00
$html = '';
if (!$withoutP) {
$html .= "<p>" . PHP_EOL;
}
$html .= "<a href=\"{$url}\">{$text}</a>" . PHP_EOL;
2014-04-12 10:12:24 +07:00
if (!$withoutP) {
$html .= "</p>" . PHP_EOL;
}
return $html;
}
/**
* Write heading
*
* @param \PhpOffice\PhpWord\Element\Title $element
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeTitle($element)
{
$tag = 'h' . $element->getDepth();
$text = htmlspecialchars($element->getText());
$html = "<{$tag}>{$text}</{$tag}>" . PHP_EOL;
return $html;
}
/**
* Write preserve text
*
* @param \PhpOffice\PhpWord\Element\PreserveText $element
2014-04-13 23:17:39 +07:00
* @param boolean $withoutP
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writePreserveText($element, $withoutP = false)
{
return $this->writeUnsupportedElement($element, $withoutP);
}
/**
* Get text break
*
* @param \PhpOffice\PhpWord\Element\TextBreak $element
2014-04-12 10:12:24 +07:00
* @param boolean $withoutP
* @return string
*/
private function writeTextBreak($element, $withoutP = false)
{
if ($withoutP) {
$html = '<br />' . PHP_EOL;
} else {
$html = '<p>&nbsp;</p>' . PHP_EOL;
}
return $html;
}
/**
* Write page break
*
* @param \PhpOffice\PhpWord\Element\PageBreak $element
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writePageBreak($element)
{
return $this->writeUnsupportedElement($element, false);
}
/**
* Write list item
*
* @param \PhpOffice\PhpWord\Element\ListItem $element
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeListItem($element)
{
$text = htmlspecialchars($element->getTextObject()->getText());
$html = '<p>' . $text . '</{$p}>' . PHP_EOL;
return $html;
2014-04-12 10:12:24 +07:00
}
/**
* Write table
*
* @param \PhpOffice\PhpWord\Element\Table $element
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeTable($element)
{
$html = '';
$rows = $element->getRows();
$cRows = count($rows);
if ($cRows > 0) {
$html .= "<table>" . PHP_EOL;
foreach ($rows as $row) {
2014-04-13 23:17:39 +07:00
// $height = $row->getHeight();
2014-04-12 10:12:24 +07:00
$rowStyle = $row->getStyle();
$tblHeader = $rowStyle->getTblHeader();
$html .= "<tr>" . PHP_EOL;
foreach ($row->getCells() as $cell) {
$cellTag = $tblHeader ? 'th' : 'td';
$cellContents = $cell->getElements();
$html .= "<{$cellTag}>" . PHP_EOL;
if (count($cellContents) > 0) {
foreach ($cellContents as $content) {
if ($content instanceof Text) {
$html .= $this->writeText($content);
} elseif ($content instanceof TextRun) {
$html .= $this->writeTextRun($content);
} elseif ($content instanceof Link) {
$html .= $this->writeLink($content);
} elseif ($content instanceof PreserveText) {
$html .= $this->writePreserveText($content);
} elseif ($content instanceof TextBreak) {
$html .= $this->writeTextBreak($content);
} elseif ($content instanceof ListItem) {
$html .= $this->writeListItem($content);
} elseif ($content instanceof Image) {
$html .= $this->writeImage($content);
} elseif ($content instanceof Object) {
$html .= $this->writeObject($content);
} elseif ($element instanceof Endnote) {
2014-04-13 23:17:39 +07:00
$html .= $this->writeEndnote($element);
} elseif ($element instanceof Footnote) {
$html .= $this->writeFootnote($element);
2014-04-12 10:12:24 +07:00
}
}
} else {
2014-04-13 23:17:39 +07:00
$html .= $this->writeTextBreak(new TextBreak());
2014-04-12 10:12:24 +07:00
}
$html .= "</td>" . PHP_EOL;
}
$html .= "</tr>" . PHP_EOL;
}
$html .= "</table>" . PHP_EOL;
}
return $html;
}
/**
* Write image
*
* @param \PhpOffice\PhpWord\Element\Image $element
2014-04-13 23:17:39 +07:00
* @param boolean $withoutP
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeImage($element, $withoutP = false)
{
2014-04-16 12:12:32 +07:00
$html = $this->writeUnsupportedElement($element, $withoutP);
if (!$this->isPdf) {
$imageData = $this->getBase64ImageData($element);
if (!is_null($imageData)) {
$style = $this->assembleCss(array(
'width' => $element->getStyle()->getWidth() . 'px',
'height' => $element->getStyle()->getHeight() . 'px',
));
$html = "<img border=\"0\" style=\"{$style}\" src=\"{$imageData}\"/>";
2014-04-16 12:12:32 +07:00
if (!$withoutP) {
$html = "<p>{$html}</p>" . PHP_EOL;
2014-04-16 12:12:32 +07:00
}
}
}
return $html;
2014-04-12 10:12:24 +07:00
}
/**
* Write object
*
* @param \PhpOffice\PhpWord\Element\Object $element
2014-04-13 23:17:39 +07:00
* @param boolean $withoutP
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeObject($element, $withoutP = false)
{
return $this->writeUnsupportedElement($element, $withoutP);
}
/**
* Write footnote
*
* @param \PhpOffice\PhpWord\Element\Footnote $element
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeFootnote($element)
{
return $this->writeNote($element);
2014-04-12 10:12:24 +07:00
}
/**
* Write endnote
*
* @param \PhpOffice\PhpWord\Element\Endnote $element
2014-04-12 10:12:24 +07:00
* @return string
*/
private function writeEndnote($element)
{
return $this->writeNote($element);
}
/**
* Write footnote/endnote marks
*
* @param mixed $element
* @return string
*/
private function writeNote($element)
{
$index = count($this->notes) + 1;
$prefix = ($element instanceof Endnote) ? 'endnote' : 'footnote';
$noteMark = $prefix . '-' . $element->getRelationId();
$noteAnchor = "note-{$index}";
$this->notes[$index] = $noteMark;
$html = "<a name=\"{$noteMark}\"><a href=\"#{$noteAnchor}\" class=\"NoteRef\"><sup>{$index}</sup></a>";
return $html;
2014-04-12 10:12:24 +07:00
}
/**
* Write unsupported element
*
* @param mixed $element
* @param boolean $withoutP
* @return string
*/
private function writeUnsupportedElement($element, $withoutP = false)
{
$elementClass = get_class($element);
$elementMark = str_replace('PhpOffice\\PhpWord\\Element\\', '', $elementClass);
$elementMark = htmlentities("<{$elementMark}>");
if ($withoutP) {
$html = "<span class=\"other-elm\">{$elementMark}</span>" . PHP_EOL;
} else {
$html = "<p>{$elementMark}</p>" . PHP_EOL;
}
return $html;
}
/**
* Get styles
*
* @return string
*/
private function writeStyles()
{
$css = '<style>' . PHP_EOL;
2014-04-13 23:17:39 +07:00
2014-04-12 10:12:24 +07:00
// Default styles
$defaultStyles = array(
'*' => array(
'font-family' => $this->getPhpWord()->getDefaultFontName(),
'font-size' => $this->getPhpWord()->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) {
$css .= $selector . ' ' . $this->assembleCss($style, true) . PHP_EOL;
}
2014-04-13 23:17:39 +07:00
2014-04-12 10:12:24 +07:00
// Custom styles
$customStyles = Style::getStyles();
if (is_array($customStyles)) {
foreach ($customStyles as $name => $style) {
2014-04-12 10:12:24 +07:00
if ($style instanceof Font) {
if ($style->getStyleType() == 'title') {
$name = str_replace('Heading_', 'h', $name);
} else {
$name = '.' . $name;
}
$css .= "{$name} " . $this->writeFontStyle($style, true) . PHP_EOL;
} elseif ($style instanceof Paragraph) {
$name = '.' . $name;
$css .= "{$name} " . $this->writeParagraphStyle($style, true) . PHP_EOL;
}
}
}
$css .= '</style>' . PHP_EOL;
return $css;
}
/**
* Get font style
*
* @param \PhpOffice\PhpWord\Style\Font $style
2014-04-12 10:12:24 +07:00
* @param boolean $curlyBracket
* @return string
*/
private function writeFontStyle($style, $curlyBracket = false)
{
$css = array();
if (PHPWord::DEFAULT_FONT_NAME != $style->getName()) {
$css['font-family'] = "'" . $style->getName() . "'";
}
if (PHPWord::DEFAULT_FONT_SIZE != $style->getSize()) {
$css['font-size'] = $style->getSize() . 'pt';
}
if (PHPWord::DEFAULT_FONT_COLOR != $style->getColor()) {
$css['color'] = '#' . $style->getColor();
}
$css['background'] = $style->getFgColor();
if ($style->getBold()) {
$css['font-weight'] = 'bold';
}
if ($style->getItalic()) {
$css['font-style'] = 'italic';
}
if ($style->getSuperScript()) {
$css['vertical-align'] = 'super';
} elseif ($style->getSubScript()) {
$css['vertical-align'] = 'sub';
}
$css['text-decoration'] = '';
if ($style->getUnderline() != Font::UNDERLINE_NONE) {
$css['text-decoration'] .= 'underline ';
}
if ($style->getStrikethrough()) {
$css['text-decoration'] .= 'line-through ';
}
return $this->assembleCss($css, $curlyBracket);
}
/**
* Get paragraph style
*
* @param \PhpOffice\PhpWord\Style\Paragraph $style
2014-04-12 10:12:24 +07:00
* @param boolean $curlyBracket
* @return string
*/
private function writeParagraphStyle($style, $curlyBracket = false)
{
$css = array();
if ($style->getAlign()) {
$css['text-align'] = $style->getAlign();
}
return $this->assembleCss($css, $curlyBracket);
}
/**
* Takes array where of CSS properties / values and converts to CSS string
*
* @param array $css
* @param boolean $curlyBracket
* @return string
*/
private function assembleCss($css, $curlyBracket = false)
{
$pairs = array();
foreach ($css as $key => $value) {
if ($value != '') {
$pairs[] = $key . ': ' . $value;
}
}
$string = implode('; ', $pairs);
if ($curlyBracket) {
$string = '{ ' . $string . ' }';
}
return $string;
}
2014-04-16 12:12:32 +07:00
/**
* Get Base64 image data
*
* @return string|null
*/
private function getBase64ImageData(Image $element)
{
$imageData = null;
$imageBinary = null;
2014-04-16 12:12:32 +07:00
$source = $element->getSource();
$imageType = $element->getImageType();
// Get actual source from archive image
if ($element->getSourceType() == Image::SOURCE_ARCHIVE) {
2014-04-16 12:12:32 +07:00
$source = substr($source, 6);
list($zipFilename, $imageFilename) = explode('#', $source);
$zip = new \ZipArchive();
if ($zip->open($zipFilename) !== false) {
if ($zip->locateName($imageFilename)) {
$zip->extractTo($this->getTempDir(), $imageFilename);
$actualSource = $this->getTempDir() . DIRECTORY_SEPARATOR . $imageFilename;
}
}
$zip->close();
} else {
$actualSource = $source;
}
// Read image binary data and convert into Base64
if ($element->getSourceType() == Image::SOURCE_GD) {
$imageResource = call_user_func($element->getImageCreateFunction(), $actualSource);
ob_start();
call_user_func($element->getImageFunction(), $imageResource);
$imageBinary = ob_get_contents();
ob_end_clean();
} else {
if ($fp = fopen($actualSource, 'rb', false)) {
$imageBinary = fread($fp, filesize($actualSource));
fclose($fp);
}
}
if (!is_null($imageBinary)) {
$base64 = chunk_split(base64_encode($imageBinary));
2014-04-16 12:12:32 +07:00
$imageData = 'data:' . $imageType . ';base64,' . $base64;
}
return $imageData;
}
2014-04-12 10:12:24 +07:00
}