Merge pull request #204 from ivanlanin/pdf-writer

Basic PDF Writer. It will be better if we can detect DomPDF installed by composer. Please help. Thanks.
This commit is contained in:
Ivan Lanin 2014-04-14 12:42:14 +07:00
commit 6eb2c661e3
9 changed files with 474 additions and 28 deletions

View File

@ -4,7 +4,7 @@ This is the changelog between releases of PHPWord. Releases are listed in revers
## 0.10.0 - Not yet released ## 0.10.0 - 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`. Word2007 reader capability is greatly enhanced. Endnote is introduced. List numbering is now customizable. Basic HTML support is enabled. 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. List numbering is now customizable. Basic HTML and PDF writing support is enabled.
### Features ### Features
@ -33,6 +33,7 @@ This release marked heavy refactorings on internal code structure with the creat
- ODT Writer: Basic table writing support - @ivanlanin - ODT Writer: Basic table writing support - @ivanlanin
- Image: Keep image aspect ratio if only 1 dimension styled - @japonicus GH-194 - Image: Keep image aspect ratio if only 1 dimension styled - @japonicus GH-194
- HTML Writer: Basic HTML writer initiated - @ivanlanin GH-203 GH-67 GH-147 - HTML Writer: Basic HTML writer initiated - @ivanlanin GH-203 GH-67 GH-147
- PDF Writer: Basic PDF writer initiated using DomPDF - @ivanlanin GH-68
### Bugfixes ### Bugfixes

View File

@ -11,6 +11,17 @@ define('IS_INDEX', SCRIPT_FILENAME == 'index');
require_once '../src/PhpWord/Autoloader.php'; require_once '../src/PhpWord/Autoloader.php';
\PhpOffice\PhpWord\Autoloader::register(); \PhpOffice\PhpWord\Autoloader::register();
// Set writers
$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf');
// Set PDF renderer
$rendererName = \PhpOffice\PhpWord\Settings::PDF_RENDERER_DOMPDF;
$rendererLibraryPath = ''; // DomPDF library path
if (!\PhpOffice\PhpWord\Settings::setPdfRenderer($rendererName, $rendererLibraryPath)) {
$writers['PDF'] = null;
}
// Return to the caller script when runs by CLI // Return to the caller script when runs by CLI
if (CLI) { if (CLI) {
return; return;
@ -22,9 +33,6 @@ $pageTitle = IS_INDEX ? 'Welcome to ' : "{$pageHeading} - ";
$pageTitle .= 'PHPWord'; $pageTitle .= 'PHPWord';
$pageHeading = IS_INDEX ? '' : "<h1>{$pageHeading}</h1>"; $pageHeading = IS_INDEX ? '' : "<h1>{$pageHeading}</h1>";
// Set writers
$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html');
// Populate samples // Populate samples
$files = ''; $files = '';
if ($handle = opendir('.')) { if ($handle = opendir('.')) {
@ -51,10 +59,15 @@ function write($phpWord, $filename, $writers)
// Write // Write
foreach ($writers as $writer => $extension) { foreach ($writers as $writer => $extension) {
$result .= date('H:i:s') . " Write to {$writer} format" . EOL; $result .= date('H:i:s') . " Write to {$writer} format";
$xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); if (!is_null($extension)) {
$xmlWriter->save("{$filename}.{$extension}"); $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer);
rename("{$filename}.{$extension}", "results/{$filename}.{$extension}"); $xmlWriter->save("{$filename}.{$extension}");
rename("{$filename}.{$extension}", "results/{$filename}.{$extension}");
} else {
$result .= ' ... NOT DONE!';
}
$result .= EOL;
} }
// Do not show execution time for index // Do not show execution time for index

View File

@ -28,7 +28,7 @@ abstract class IOFactory
*/ */
public static function createWriter(PhpWord $phpWord, $name = 'Word2007') public static function createWriter(PhpWord $phpWord, $name = 'Word2007')
{ {
if (!in_array($name, array('WriterInterface', 'Word2007', 'ODText', 'RTF', 'HTML'))) { if (!in_array($name, array('WriterInterface', 'Word2007', 'ODText', 'RTF', 'HTML', 'PDF'))) {
throw new Exception("\"{$name}\" is not a valid writer."); throw new Exception("\"{$name}\" is not a valid writer.");
} }

View File

@ -14,10 +14,13 @@ namespace PhpOffice\PhpWord;
*/ */
class Settings class Settings
{ {
/** Available Zip library classes */ /** Available Zip library classes */
const PCLZIP = 'PhpOffice\\PhpWord\\Shared\\ZipArchive'; const PCLZIP = 'PhpOffice\\PhpWord\\Shared\\ZipArchive';
const ZIPARCHIVE = 'ZipArchive'; const ZIPARCHIVE = 'ZipArchive';
/** Optional PDF Rendering libraries */
const PDF_RENDERER_DOMPDF = 'DomPDF';
/** /**
* Compatibility option for XMLWriter * Compatibility option for XMLWriter
* *
@ -27,13 +30,32 @@ class Settings
/** /**
* Name of the class used for Zip file management * Name of the class used for Zip file management
* e.g.
* ZipArchive
* *
* @var string * @var string
*/ */
private static $zipClass = self::ZIPARCHIVE; private static $zipClass = self::ZIPARCHIVE;
/**
* Name of the classes used for PDF renderer
*
* @var array
*/
private static $pdfRenderers = array(self::PDF_RENDERER_DOMPDF);
/**
* Name of the external Library used for rendering PDF files
*
* @var string
*/
private static $pdfRendererName = null;
/**
* Directory Path to the external Library used for rendering PDF files
*
* @var string
*/
private static $pdfRendererPath = null;
/** /**
* Set the compatibility option used by the XMLWriter * Set the compatibility option used by the XMLWriter
* *
@ -74,7 +96,7 @@ class Settings
return true; return true;
} }
return false; return false;
} // function setZipClass() }
/** /**
* Return the name of the Zip handler Class that PHPWord is configured to use (PCLZip or ZipArchive) * Return the name of the Zip handler Class that PHPWord is configured to use (PCLZip or ZipArchive)
@ -87,5 +109,71 @@ class Settings
public static function getZipClass() public static function getZipClass()
{ {
return self::$zipClass; return self::$zipClass;
} // function getZipClass() }
/**
* Set details of the external library for rendering PDF files
*
* @param string $libraryName
* @param string $libraryBaseDir
* @return boolean Success or failure
*/
public static function setPdfRenderer($libraryName, $libraryBaseDir)
{
if (!self::setPdfRendererName($libraryName)) {
return false;
}
return self::setPdfRendererPath($libraryBaseDir);
}
/**
* Return the PDF Rendering Library
*/
public static function getPdfRendererName()
{
return self::$pdfRendererName;
}
/**
* Identify the external library to use for rendering PDF files
*
* @param string $libraryName
* @return boolean Success or failure
*/
public static function setPdfRendererName($libraryName)
{
if (!in_array($libraryName, self::$pdfRenderers)) {
return false;
}
self::$pdfRendererName = $libraryName;
return true;
}
/**
* Return the directory path to the PDF Rendering Library
*/
public static function getPdfRendererPath()
{
return self::$pdfRendererPath;
}
/**
* Location of external library to use for rendering PDF files
*
* @param string $libraryBaseDir Directory path to the library's base folder
* @return boolean Success or failure
*/
public static function setPdfRendererPath($libraryBaseDir)
{
if ((file_exists($libraryBaseDir) === false) || (is_readable($libraryBaseDir) === false)) {
return false;
}
self::$pdfRendererPath = $libraryBaseDir;
return true;
}
} }

View File

@ -16,6 +16,7 @@ use PhpOffice\PhpWord\Element\Link;
use PhpOffice\PhpWord\Element\ListItem; use PhpOffice\PhpWord\Element\ListItem;
use PhpOffice\PhpWord\Element\Object; use PhpOffice\PhpWord\Element\Object;
use PhpOffice\PhpWord\Element\PageBreak; use PhpOffice\PhpWord\Element\PageBreak;
use PhpOffice\PhpWord\Element\PreserveText;
use PhpOffice\PhpWord\Element\Table; use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\Element\Text; use PhpOffice\PhpWord\Element\Text;
use PhpOffice\PhpWord\Element\TextBreak; use PhpOffice\PhpWord\Element\TextBreak;
@ -65,7 +66,7 @@ class HTML extends AbstractWriter implements WriterInterface
* *
* @return string * @return string
*/ */
private function writeDocument() public function writeDocument()
{ {
$html = ''; $html = '';
$html .= '<!DOCTYPE html>' . PHP_EOL; $html .= '<!DOCTYPE html>' . PHP_EOL;
@ -87,7 +88,7 @@ class HTML extends AbstractWriter implements WriterInterface
* *
* @return string * @return string
*/ */
public function writeHTMLHead() private function writeHTMLHead()
{ {
$properties = $this->getPhpWord()->getDocumentProperties(); $properties = $this->getPhpWord()->getDocumentProperties();
$propertiesMapping = array( $propertiesMapping = array(
@ -124,7 +125,7 @@ class HTML extends AbstractWriter implements WriterInterface
* *
* @return string * @return string
*/ */
public function writeHTMLBody() private function writeHTMLBody()
{ {
$phpWord = $this->getPhpWord(); $phpWord = $this->getPhpWord();
$html = ''; $html = '';
@ -136,8 +137,8 @@ class HTML extends AbstractWriter implements WriterInterface
if ($countSections > 0) { if ($countSections > 0) {
foreach ($sections as $section) { foreach ($sections as $section) {
$pSection++; $pSection++;
$cellContents = $section->getElements(); $contents = $section->getElements();
foreach ($cellContents as $element) { foreach ($contents as $element) {
if ($element instanceof Text) { if ($element instanceof Text) {
$html .= $this->writeText($element); $html .= $this->writeText($element);
} elseif ($element instanceof TextRun) { } elseif ($element instanceof TextRun) {
@ -161,9 +162,9 @@ class HTML extends AbstractWriter implements WriterInterface
} elseif ($element instanceof Object) { } elseif ($element instanceof Object) {
$html .= $this->writeObject($element); $html .= $this->writeObject($element);
} elseif ($element instanceof Footnote) { } elseif ($element instanceof Footnote) {
$html .= $this->writeFootnote($element, true); $html .= $this->writeFootnote($element);
} elseif ($element instanceof Endnote) { } elseif ($element instanceof Endnote) {
$html .= $this->writeEndnote($element, true); $html .= $this->writeEndnote($element);
} }
} }
} }
@ -248,9 +249,9 @@ class HTML extends AbstractWriter implements WriterInterface
} elseif ($element instanceof Image) { } elseif ($element instanceof Image) {
$html .= $this->writeImage($element, true); $html .= $this->writeImage($element, true);
} elseif ($element instanceof Footnote) { } elseif ($element instanceof Footnote) {
$html .= $this->writeFootnote($element, true); $html .= $this->writeFootnote($element);
} elseif ($element instanceof Endnote) { } elseif ($element instanceof Endnote) {
$html .= $this->writeEndnote($element, true); $html .= $this->writeEndnote($element);
} }
} }
$html .= '</p>' . PHP_EOL; $html .= '</p>' . PHP_EOL;
@ -263,6 +264,7 @@ class HTML extends AbstractWriter implements WriterInterface
* Write link * Write link
* *
* @param Link $element * @param Link $element
* @param boolean $withoutP
* @return string * @return string
*/ */
private function writeLink($element, $withoutP = false) private function writeLink($element, $withoutP = false)
@ -300,6 +302,7 @@ class HTML extends AbstractWriter implements WriterInterface
* Write preserve text * Write preserve text
* *
* @param PreserveText $element * @param PreserveText $element
* @param boolean $withoutP
* @return string * @return string
*/ */
private function writePreserveText($element, $withoutP = false) private function writePreserveText($element, $withoutP = false)
@ -350,7 +353,7 @@ class HTML extends AbstractWriter implements WriterInterface
/** /**
* Write table * Write table
* *
* @param Title $element * @param Table $element
* @return string * @return string
*/ */
private function writeTable($element) private function writeTable($element)
@ -361,7 +364,7 @@ class HTML extends AbstractWriter implements WriterInterface
if ($cRows > 0) { if ($cRows > 0) {
$html .= "<table>" . PHP_EOL; $html .= "<table>" . PHP_EOL;
foreach ($rows as $row) { foreach ($rows as $row) {
$height = $row->getHeight(); // $height = $row->getHeight();
$rowStyle = $row->getStyle(); $rowStyle = $row->getStyle();
$tblHeader = $rowStyle->getTblHeader(); $tblHeader = $rowStyle->getTblHeader();
$html .= "<tr>" . PHP_EOL; $html .= "<tr>" . PHP_EOL;
@ -388,13 +391,13 @@ class HTML extends AbstractWriter implements WriterInterface
} elseif ($content instanceof Object) { } elseif ($content instanceof Object) {
$html .= $this->writeObject($content); $html .= $this->writeObject($content);
} elseif ($element instanceof Footnote) { } elseif ($element instanceof Footnote) {
$html .= $this->writeFootnote($element, true); $html .= $this->writeFootnote($element);
} elseif ($element instanceof Endnote) { } elseif ($element instanceof Endnote) {
$html .= $this->writeEndnote($element, true); $html .= $this->writeEndnote($element);
} }
} }
} else { } else {
$this->writeTextBreak($content); $html .= $this->writeTextBreak(new TextBreak());
} }
$html .= "</td>" . PHP_EOL; $html .= "</td>" . PHP_EOL;
} }
@ -410,6 +413,7 @@ class HTML extends AbstractWriter implements WriterInterface
* Write image * Write image
* *
* @param Image $element * @param Image $element
* @param boolean $withoutP
* @return string * @return string
*/ */
private function writeImage($element, $withoutP = false) private function writeImage($element, $withoutP = false)
@ -421,6 +425,7 @@ class HTML extends AbstractWriter implements WriterInterface
* Write object * Write object
* *
* @param Object $element * @param Object $element
* @param boolean $withoutP
* @return string * @return string
*/ */
private function writeObject($element, $withoutP = false) private function writeObject($element, $withoutP = false)
@ -478,11 +483,14 @@ class HTML extends AbstractWriter implements WriterInterface
*/ */
private function writeStyles() private function writeStyles()
{ {
$bodyCss = array();
$css = '<style>' . PHP_EOL; $css = '<style>' . PHP_EOL;
// Default styles // Default styles
$bodyCss['font-family'] = "'" . $this->getPhpWord()->getDefaultFontName() . "'"; $bodyCss['font-family'] = "'" . $this->getPhpWord()->getDefaultFontName() . "'";
$bodyCss['font-size'] = $this->getPhpWord()->getDefaultFontSize() . 'pt'; $bodyCss['font-size'] = $this->getPhpWord()->getDefaultFontSize() . 'pt';
$css .= '* ' . $this->assembleCss($bodyCss, true) . PHP_EOL; $css .= '* ' . $this->assembleCss($bodyCss, true) . PHP_EOL;
// Custom styles // Custom styles
$styles = Style::getStyles(); $styles = Style::getStyles();
if (is_array($styles)) { if (is_array($styles)) {

View File

@ -0,0 +1,66 @@
<?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\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
/**
* PDF Writer
*/
class PDF
{
/**
* The wrapper for the requested PDF rendering engine
*
* @var \PhpOffice\PhpWord\Writer\PDF\AbstractRenderer
*/
private $renderer = null;
/**
* Instantiate a new renderer of the configured type within this container class
*
* @param \PhpOffice\PhpWord\PhpWord $phpWord
*/
public function __construct(PhpWord $phpWord)
{
$pdfLibraryName = Settings::getPdfRendererName();
$pdfLibraryPath = Settings::getPdfRendererPath();
if (is_null($pdfLibraryName) || is_null($pdfLibraryPath)) {
throw new Exception("PDF rendering library or library path has not been defined.");
}
$includePath = str_replace('\\', '/', get_include_path());
$rendererPath = str_replace('\\', '/', $pdfLibraryPath);
if (strpos($rendererPath, $includePath) === false) {
set_include_path(get_include_path() . PATH_SEPARATOR . $pdfLibraryPath);
}
$rendererName = 'PhpOffice\\PhpWord\\Writer\\PDF\\' . $pdfLibraryName;
$this->renderer = new $rendererName($phpWord);
}
/**
* Magic method to handle direct calls to the configured PDF renderer wrapper class.
*
* @param string $name Renderer library method name
* @param mixed[] $arguments Array of arguments to pass to the renderer method
* @return mixed Returned data from the PDF renderer wrapper method
*/
public function __call($name, $arguments)
{
if ($this->renderer === null) {
throw new Exception("PDF Rendering library has not been defined.");
}
return call_user_func_array(array($this->renderer, $name), $arguments);
}
}

View File

@ -0,0 +1,194 @@
<?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\PDF;
use PhpOffice\PhpWord\Exception\Exception;
use PhpOffice\PhpWord\PhpWord;
/**
* Abstract PDF renderer
*/
abstract class AbstractRenderer extends \PhpOffice\PhpWord\Writer\HTML
{
/**
* Temporary storage directory
*
* @var string
*/
protected $tempDir = '';
/**
* Font
*
* @var string
*/
protected $font;
/**
* Paper size
*
* @var int
*/
protected $paperSize = null;
/**
* Orientation
*
* @var string
*/
protected $orientation = null;
/**
* Temporary storage for Save Array Return type
*
* @var string
*/
private $saveArrayReturnType;
/**
* Paper Sizes xRef List
*
* @var array
*/
protected static $paperSizes = array(
9 => 'A4', // (210 mm by 297 mm)
);
/**
* Create new instance
*
* @param PhpWord $phpWord PhpWord object
*/
public function __construct(PhpWord $phpWord)
{
parent::__construct($phpWord);
$this->tempDir = sys_get_temp_dir();
}
/**
* Get Font
*
* @return string
*/
public function getFont()
{
return $this->font;
}
/**
* Set font. Examples:
* 'arialunicid0-chinese-simplified'
* 'arialunicid0-chinese-traditional'
* 'arialunicid0-korean'
* 'arialunicid0-japanese'
*
* @param string $fontName
*/
public function setFont($fontName)
{
$this->font = $fontName;
return $this;
}
/**
* Get Paper Size
*
* @return int
*/
public function getPaperSize()
{
return $this->paperSize;
}
/**
* Set Paper Size
*
* @param string $pValue Paper size = PAPERSIZE_A4
* @return self
*/
public function setPaperSize($pValue = 9)
{
$this->paperSize = $pValue;
return $this;
}
/**
* Get Orientation
*
* @return string
*/
public function getOrientation()
{
return $this->orientation;
}
/**
* Set Orientation
*
* @param string $pValue Page orientation ORIENTATION_DEFAULT
* @return self
*/
public function setOrientation($pValue = 'default')
{
$this->orientation = $pValue;
return $this;
}
/**
* Get temporary storage directory
*
* @return string
*/
public function getTempDir()
{
return $this->tempDir;
}
/**
* Set temporary storage directory
*
* @param string $pValue Temporary storage directory
* @return self
*/
public function setTempDir($pValue = '')
{
if (is_dir($pValue)) {
$this->tempDir = $pValue;
} else {
throw new Exception("Directory does not exist: $pValue");
}
return $this;
}
/**
* Save PhpWord to PDF file, pre-save
*
* @param string $pFilename Name of the file to save as
*/
protected function prepareForSave($pFilename = null)
{
$fileHandle = fopen($pFilename, 'w');
if ($fileHandle === false) {
throw new Exception("Could not open file $pFilename for writing.");
}
return $fileHandle;
}
/**
* Save PhpWord to PDF file, post-save
*
* @param resource $fileHandle
* @throws Exception
*/
protected function restoreStateAfterSave($fileHandle)
{
fclose($fileHandle);
}
}

View File

@ -0,0 +1,72 @@
<?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\PDF;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Exception\Exception;
/** Require DomPDF library */
$pdfRendererClassFile = Settings::getPdfRendererPath() . '/dompdf_config.inc.php';
if (file_exists($pdfRendererClassFile)) {
require_once $pdfRendererClassFile;
} else {
throw new Exception('Unable to load PDF Rendering library');
}
/**
* DomPDF writer
*/
class DomPDF extends AbstractRenderer implements \PhpOffice\PhpWord\Writer\WriterInterface
{
/**
* Create new instance
*
* @param PhpWord $phpWord PhpWord object
*/
public function __construct(PhpWord $phpWord)
{
parent::__construct($phpWord);
}
/**
* Save PhpWord to file
*
* @param string $pFilename Name of the file to save as
* @throws Exception
*/
public function save($pFilename = null)
{
$fileHandle = parent::prepareForSave($pFilename);
// Default PDF paper size
$paperSize = 'A4';
$orientation = 'P';
$printPaperSize = 9;
if (isset(self::$paperSizes[$printPaperSize])) {
$paperSize = self::$paperSizes[$printPaperSize];
}
$orientation = ($orientation == 'L') ? 'landscape' : 'portrait';
// Create PDF
$pdf = new \DOMPDF();
$pdf->set_paper(strtolower($paperSize), $orientation);
$pdf->load_html($this->writeDocument());
$pdf->render();
// Write to file
fwrite($fileHandle, $pdf->output());
parent::restoreStateAfterSave($fileHandle);
}
}

View File

@ -55,6 +55,10 @@ class AbstractStyleTest extends \PHPUnit_Framework_TestCase
/** /**
* Helper function to call protected method * Helper function to call protected method
*
* @param mixed $object
* @param string $method
* @param array $args
*/ */
public static function callProtectedMethod($object, $method, array $args = array()) public static function callProtectedMethod($object, $method, array $args = array())
{ {