diff --git a/.travis.yml b/.travis.yml index d4f511da..63165dbd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,8 +22,6 @@ before_script: - sudo apt-get -qq install graphviz > /dev/null ## Composer - composer self-update - - composer require dompdf/dompdf:0.6.* - - composer update --prefer-source --dev - composer install --prefer-source --dev ## PHP Copy/Paste Detector - curl -o phpcpd.phar https://phar.phpunit.de/phpcpd.phar diff --git a/CHANGELOG.md b/CHANGELOG.md index cb408f43..fce2bdac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,15 +15,17 @@ This release marked the change of PHPWord license from LGPL 2.1 to LGPL 3; new r - HTML: Ability to add elements to PHPWord object via html - @basjan GH-231 - ListItemRun: New element that can add a list item with inline formatting like a textrun - @basjan GH-235 - Table: Ability to add table inside a cell (nested table) - @ivanlanin GH-149 -- RTF: UTF8 support for RTF: Internal UTF8 text is converted to Unicode before writing - @ivanlanin GH-158 +- RTF Writer: UTF8 support for RTF: Internal UTF8 text is converted to Unicode before writing - @ivanlanin GH-158 - Table: Ability to define table width (in percent and twip) and position - @ivanlanin GH-237 -- RTF: Ability to add links and page breaks in RTF - @ivanlanin GH-196 +- RTF Writer: Ability to add links and page breaks in RTF - @ivanlanin GH-196 - ListItemRun: Remove fontStyle parameter because ListItemRun is inherited from TextRun and TextRun doesn't have fontStyle - @ivanlanin - Config: Ability to use a config file to store various common settings - @ivanlanin GH-200 -- ODT: Enable inline font style in TextRun - @ivanlanin -- ODT: Enable underline, strike/doublestrike, smallcaps/allcaps, superscript/subscript font style - @ivanlanin -- ODT: Enable section and column - @ivanlanin -- PDF: Add TCPDF and mPDF as optional PDF renderer library - @ivanlanin +- ODT Writer: Enable inline font style in TextRun - @ivanlanin +- ODT Writer: Enable underline, strike/doublestrike, smallcaps/allcaps, superscript/subscript font style - @ivanlanin +- ODT Writer: Enable section and column - @ivanlanin +- PDF Writer: Add TCPDF and mPDF as optional PDF renderer library - @ivanlanin +- ODT Writer: Enable title element and custom document properties - @ivanlanin +- ODT Reader: Ability to read standard and custom document properties - @ivanlanin ### Bugfixes diff --git a/composer.json b/composer.json index a201a033..b9eef98d 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,10 @@ "require-dev": { "phpunit/phpunit": "3.7.*", "phpdocumentor/phpdocumentor":"2.*", - "squizlabs/php_codesniffer": "1.*" + "squizlabs/php_codesniffer": "1.*", + "dompdf/dompdf":"0.6.*", + "tecnick.com/tcpdf": "6.*", + "mpdf/mpdf": "5.*" }, "suggest": { "ext-gd2": "Required to add images", diff --git a/composer.lock b/composer.lock index aab180be..172e564e 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "d46ea4154e935e4be01ffbad0a67bab2", + "hash": "91993ff980d11a416fcf3a7309a4044f", "packages": [ ], @@ -619,6 +619,49 @@ ], "time": "2014-04-24 13:29:03" }, + { + "name": "mpdf/mpdf", + "version": "v5.7.2", + "source": { + "type": "git", + "url": "https://github.com/finwe/mpdf.git", + "reference": "1627f9e7d2ef0f635a886f611079216ed2929717" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/finwe/mpdf/zipball/1627f9e7d2ef0f635a886f611079216ed2929717", + "reference": "1627f9e7d2ef0f635a886f611079216ed2929717", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=4.3.10" + }, + "type": "library", + "autoload": { + "classmap": [ + "mpdf.php", + "classes" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-1.0+" + ], + "authors": [ + { + "name": "Ian Back" + } + ], + "description": "A PHP class to generate PDF files from HTML with Unicode/UTF-8 and CJK support", + "homepage": "http://www.mpdf1.com/mpdf/index.php", + "keywords": [ + "pdf", + "php", + "utf-8" + ], + "time": "2014-05-16 07:18:10" + }, { "name": "nikic/php-parser", "version": "v0.9.4", @@ -833,16 +876,16 @@ }, { "name": "phpdocumentor/phpdocumentor", - "version": "v2.4.0", + "version": "v2.5.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/phpDocumentor2.git", - "reference": "d7503ada7386aa6b2956224d50a8d0226a22a99f" + "reference": "bf9fa40f6d00412410025b2e16eb16c315eb0216" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/d7503ada7386aa6b2956224d50a8d0226a22a99f", - "reference": "d7503ada7386aa6b2956224d50a8d0226a22a99f", + "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/bf9fa40f6d00412410025b2e16eb16c315eb0216", + "reference": "bf9fa40f6d00412410025b2e16eb16c315eb0216", "shasum": "" }, "require": { @@ -895,12 +938,13 @@ "ext-xslcache": "Enabling the XSLCache extension improves the generation of xml based templates." }, "bin": [ - "bin/phpdoc.php" + "bin/phpdoc.php", + "bin/phpdoc" ], "type": "library", "extra": { "branch-alias": { - "dev-develop": "2.4-dev" + "dev-develop": "2.5-dev" } }, "autoload": { @@ -924,7 +968,7 @@ "documentation", "phpdoc" ], - "time": "2014-04-01 18:14:51" + "time": "2014-05-17 12:25:35" }, { "name": "phpdocumentor/reflection", @@ -2528,6 +2572,63 @@ "homepage": "http://symfony.com", "time": "2014-04-18 20:37:09" }, + { + "name": "tecnick.com/tcpdf", + "version": "6.0.078", + "source": { + "type": "git", + "url": "git://git.code.sf.net/p/tcpdf/code", + "reference": "e1cbda79b99f3cdc8fdf26b39eb4870d2cd9fbac" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "fonts", + "config", + "include", + "tcpdf.php", + "tcpdf_parser.php", + "tcpdf_import.php", + "tcpdf_barcodes_1d.php", + "tcpdf_barcodes_2d.php", + "include/tcpdf_colors.php", + "include/tcpdf_filters.php", + "include/tcpdf_font_data.php", + "include/tcpdf_fonts.php", + "include/tcpdf_images.php", + "include/tcpdf_static.php", + "include/barcodes/datamatrix.php", + "include/barcodes/pdf417.php", + "include/barcodes/qrcode.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPLv3" + ], + "authors": [ + { + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "homepage": "http://nicolaasuni.tecnick.com" + } + ], + "description": "TCPDF is a PHP class for generating PDF documents.", + "homepage": "http://www.tcpdf.org/", + "keywords": [ + "PDFD32000-2008", + "TCPDF", + "barcodes", + "datamatrix", + "pdf", + "pdf417", + "qrcode" + ], + "time": "2014-05-12 19:50:13" + }, { "name": "twig/twig", "version": "v1.15.1", diff --git a/docs/intro.rst b/docs/intro.rst index 0b0c1dfe..3c6277f5 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -61,17 +61,15 @@ Writers +---------------------------+----------------------+--------+-------+-------+--------+-------+ | Features | | DOCX | ODT | RTF | HTML | PDF | +===========================+======================+========+=======+=======+========+=======+ -| **Document Properties** | Standard | ✓ | | | | | +| **Document Properties** | Standard | ✓ | ✓ | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ -| | Extended | ✓ | | | | | -+---------------------------+----------------------+--------+-------+-------+--------+-------+ -| | UserDefined | ✓ | | | | | +| | Custom | ✓ | ✓ | | | | +---------------------------+----------------------+--------+-------+-------+--------+-------+ | **Element Type** | Text | ✓ | ✓ | ✓ | ✓ | ✓ | +---------------------------+----------------------+--------+-------+-------+--------+-------+ | | Text Run | ✓ | ✓ | ✓ | ✓ | ✓ | +---------------------------+----------------------+--------+-------+-------+--------+-------+ -| | Title | ✓ | | | ✓ | ✓ | +| | Title | ✓ | ✓ | | ✓ | ✓ | +---------------------------+----------------------+--------+-------+-------+--------+-------+ | | Link | ✓ | ✓ | ✓ | ✓ | ✓ | +---------------------------+----------------------+--------+-------+-------+--------+-------+ @@ -124,9 +122,7 @@ Readers +===========================+======================+========+=======+=======+ | **Document Properties** | Standard | ✓ | | | +---------------------------+----------------------+--------+-------+-------+ -| | Extended | ✓ | | | -+---------------------------+----------------------+--------+-------+-------+ -| | UserDefined | ✓ | | | +| | Custom | ✓ | | | +---------------------------+----------------------+--------+-------+-------+ | **Element Type** | Text | ✓ | ✓ | | +---------------------------+----------------------+--------+-------+-------+ diff --git a/docs/src/documentation.md b/docs/src/documentation.md index 5b969687..ffa3edc3 100644 --- a/docs/src/documentation.md +++ b/docs/src/documentation.md @@ -78,12 +78,11 @@ Below are the supported features for each file formats. | Features | | DOCX | ODT | RTF | HTML | PDF | |-------------------------|--------------------|------|-----|-----|------|-----| -| **Document Properties** | Standard | ✓ | | | | | -| | Extended | ✓ | | | | | -| | UserDefined | ✓ | | | | | +| **Document Properties** | Standard | ✓ | ✓ | | | | +| | Custom | ✓ | ✓ | | | | | **Element Type** | Text | ✓ | ✓ | ✓ | ✓ | ✓ | | | Text Run | ✓ | ✓ | ✓ | ✓ | ✓ | -| | Title | ✓ | | | ✓ | ✓ | +| | Title | ✓ | ✓ | | ✓ | ✓ | | | Link | ✓ | ✓ | ✓ | ✓ | ✓ | | | Preserve Text | ✓ | | | | | | | Text Break | ✓ | ✓ | ✓ | ✓ | ✓ | @@ -111,8 +110,7 @@ Below are the supported features for each file formats. | Features | | DOCX | ODT | RTF | |-------------------------|--------------------|------|-----|-----| | **Document Properties** | Standard | ✓ | | | -| | Extended | ✓ | | | -| | UserDefined | ✓ | | | +| | Custom | ✓ | | | | **Element Type** | Text | ✓ | ✓ | | | | Text Run | ✓ | | | | | Title | ✓ | ✓ | | diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php index fdada151..b752848d 100644 --- a/samples/Sample_Header.php +++ b/samples/Sample_Header.php @@ -6,7 +6,7 @@ use PhpOffice\PhpWord\Autoloader; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\IOFactory; -error_reporting(E_ALL); +error_reporting(E_ALL & ~E_DEPRECATED); define('CLI', (PHP_SAPI == 'cli') ? true : false); define('EOL', CLI ? PHP_EOL : '
'); define('SCRIPT_FILENAME', basename($_SERVER['SCRIPT_FILENAME'], '.php')); diff --git a/samples/resources/Sample_11_ReadWord2007.docx b/samples/resources/Sample_11_ReadWord2007.docx index a457c7a6..c9a50f48 100644 Binary files a/samples/resources/Sample_11_ReadWord2007.docx and b/samples/resources/Sample_11_ReadWord2007.docx differ diff --git a/samples/resources/Sample_24_ReadODText.odt b/samples/resources/Sample_24_ReadODText.odt index 9e18e619..d37c4e66 100644 Binary files a/samples/resources/Sample_24_ReadODText.odt and b/samples/resources/Sample_24_ReadODText.odt differ diff --git a/src/PhpWord/Reader/ODText.php b/src/PhpWord/Reader/ODText.php index 911295d9..19efdc51 100644 --- a/src/PhpWord/Reader/ODText.php +++ b/src/PhpWord/Reader/ODText.php @@ -40,6 +40,7 @@ class ODText extends AbstractReader implements ReaderInterface $readerParts = array( 'content.xml' => 'Content', + 'meta.xml' => 'Meta', ); foreach ($readerParts as $xmlFile => $partName) { diff --git a/src/PhpWord/Reader/ODText/Content.php b/src/PhpWord/Reader/ODText/Content.php index d2195679..12b39f74 100644 --- a/src/PhpWord/Reader/ODText/Content.php +++ b/src/PhpWord/Reader/ODText/Content.php @@ -22,6 +22,8 @@ use PhpOffice\PhpWord\Shared\XMLReader; /** * Content reader + * + * @since 0.10.0 */ class Content extends AbstractPart { diff --git a/src/PhpWord/Reader/ODText/Meta.php b/src/PhpWord/Reader/ODText/Meta.php new file mode 100644 index 00000000..f7f4542d --- /dev/null +++ b/src/PhpWord/Reader/ODText/Meta.php @@ -0,0 +1,79 @@ +getDomFromZip($this->docFile, $this->xmlFile); + $docProps = $phpWord->getDocumentProperties(); + + $metaNode = $xmlReader->getElement('office:meta'); + + // Standard properties + $properties = array( + 'title' => 'dc:title', + 'subject' => 'dc:subject', + 'description' => 'dc:description', + 'keywords' => 'meta:keyword', + 'creator' => 'meta:initial-creator', + 'lastModifiedBy' => 'dc:creator', + // 'created' => 'meta:creation-date', + // 'modified' => 'dc:date', + ); + foreach ($properties as $property => $path) { + $method = "set{$property}"; + $propertyNode = $xmlReader->getElement($path, $metaNode); + if ($propertyNode !== null && method_exists($docProps, $method)) { + $docProps->$method($propertyNode->nodeValue); + } + } + + // Custom properties + $propertyNodes = $xmlReader->getElements('meta:user-defined', $metaNode); + foreach ($propertyNodes as $propertyNode) { + $property = $xmlReader->getAttribute('meta:name', $propertyNode); + + // Set category, company, and manager property + if (in_array($property, array('Category', 'Company', 'Manager'))) { + $method = "set{$property}"; + $docProps->$method($propertyNode->nodeValue); + + // Set other custom properties + } else { + $docProps->setCustomProperty($property, $propertyNode->nodeValue); + } + } + } +} diff --git a/src/PhpWord/Writer/ODText/Element/Title.php b/src/PhpWord/Writer/ODText/Element/Title.php index 9d062a7f..d45f5e12 100644 --- a/src/PhpWord/Writer/ODText/Element/Title.php +++ b/src/PhpWord/Writer/ODText/Element/Title.php @@ -35,8 +35,9 @@ class Title extends AbstractElement return; } - $xmlWriter->startElement('text:p'); + $xmlWriter->startElement('text:h'); + $xmlWriter->writeAttribute('text:outline-level', $element->getDepth()); $xmlWriter->writeRaw($element->getText()); - $xmlWriter->endElement(); // text:p + $xmlWriter->endElement(); // text:h } } diff --git a/src/PhpWord/Writer/ODText/Part/Meta.php b/src/PhpWord/Writer/ODText/Part/Meta.php index 0d2a761a..50a38047 100644 --- a/src/PhpWord/Writer/ODText/Part/Meta.php +++ b/src/PhpWord/Writer/ODText/Part/Meta.php @@ -17,8 +17,12 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; +use PhpOffice\PhpWord\Shared\XMLWriter; + /** * ODText meta part writer: meta.xml + * + * @since 0.11.0 */ class Meta extends AbstractPart { @@ -42,27 +46,61 @@ class Meta extends AbstractPart $xmlWriter->writeAttribute('xmlns:meta', 'urn:oasis:names:tc:opendocument:xmlns:meta:1.0'); $xmlWriter->writeAttribute('xmlns:ooo', 'http://openoffice.org/2004/office'); $xmlWriter->writeAttribute('xmlns:grddl', 'http://www.w3.org/2003/g/data-view#'); - - // office:meta $xmlWriter->startElement('office:meta'); + + // Core properties + $xmlWriter->writeElement('dc:title', $docProps->getTitle()); + $xmlWriter->writeElement('dc:subject', $docProps->getSubject()); + $xmlWriter->writeElement('dc:description', $docProps->getDescription()); $xmlWriter->writeElement('dc:creator', $docProps->getLastModifiedBy()); $xmlWriter->writeElement('dc:date', gmdate('Y-m-d\TH:i:s.000', $docProps->getModified())); - $xmlWriter->writeElement('dc:description', $docProps->getDescription()); - $xmlWriter->writeElement('dc:subject', $docProps->getSubject()); - $xmlWriter->writeElement('dc:title', $docProps->getTitle()); - $xmlWriter->writeElement('meta:creation-date', gmdate('Y-m-d\TH:i:s.000', $docProps->getCreated())); + + // Extended properties + $xmlWriter->writeElement('meta:generator', 'PHPWord'); $xmlWriter->writeElement('meta:initial-creator', $docProps->getCreator()); + $xmlWriter->writeElement('meta:creation-date', gmdate('Y-m-d\TH:i:s.000', $docProps->getCreated())); $xmlWriter->writeElement('meta:keyword', $docProps->getKeywords()); - // @todo : Where these properties are written ? - // $docProps->getCategory() - // $docProps->getCompany() + // Category, company, and manager are put in meta namespace + $properties = array('Category', 'Company', 'Manager'); + foreach ($properties as $property) { + $method = "get{$property}"; + if ($docProps->$method() !== null) { + $this->writeCustomProperty($xmlWriter, $property, $docProps->$method()); + } + } - $xmlWriter->endElement(); + // Other custom properties + // @todo Check type. Currently all assumed as string + foreach ($docProps->getCustomProperties() as $property) { + $value = $docProps->getCustomPropertyValue($property); + $this->writeCustomProperty($xmlWriter, $property, $value); + } - $xmlWriter->endElement(); + $xmlWriter->endElement(); // office:meta + $xmlWriter->endElement(); // office:document-meta - // Return return $xmlWriter->getData(); } + + /** + * Write individual property + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param string $property + * @param string $value + * @param string $type string (default/null) + * + * @todo Handle other `$type`: double|date|dateTime|duration|boolean + */ + private function writeCustomProperty(XMLWriter $xmlWriter, $property, $value, $type = null) + { + $xmlWriter->startElement('meta:user-defined'); + $xmlWriter->writeAttribute('meta:name', $property); + if ($type !== null) { + $xmlWriter->writeAttribute('meta:value-type', $type); + } + $xmlWriter->writeRaw($value); + $xmlWriter->endElement(); // meta:user-defined + } } diff --git a/src/PhpWord/Writer/PDF/MPDF.php b/src/PhpWord/Writer/PDF/MPDF.php index 77bbafd6..e12e53ae 100644 --- a/src/PhpWord/Writer/PDF/MPDF.php +++ b/src/PhpWord/Writer/PDF/MPDF.php @@ -32,7 +32,7 @@ class MPDF extends AbstractRenderer implements WriterInterface * * @var string */ - protected $includeFile = 'mdf.php'; + protected $includeFile = 'mpdf.php'; /** * Save PhpWord to file diff --git a/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php b/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php index a2c8fb31..a22cb530 100644 --- a/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php +++ b/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php @@ -33,7 +33,7 @@ class DomPDFTest extends \PHPUnit_Framework_TestCase public function testConstruct() { define('DOMPDF_ENABLE_AUTOLOAD', false); - $file = __DIR__ . "/../../_files/temp.pdf"; + $file = __DIR__ . "/../../_files/dompdf.pdf"; $phpWord = new PhpWord(); $section = $phpWord->addSection(); diff --git a/tests/PhpWord/Tests/Writer/PDF/MPDFTest.php b/tests/PhpWord/Tests/Writer/PDF/MPDFTest.php new file mode 100644 index 00000000..4728b7f1 --- /dev/null +++ b/tests/PhpWord/Tests/Writer/PDF/MPDFTest.php @@ -0,0 +1,51 @@ +addSection(); + $section->addText('Test 1'); + + $rendererName = Settings::PDF_RENDERER_MPDF; + $rendererLibraryPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/mpdf/mpdf'); + Settings::setPdfRenderer($rendererName, $rendererLibraryPath); + $writer = new PDF($phpWord); + $writer->save($file); + + $this->assertTrue(file_exists($file)); + + unlink($file); + } +} diff --git a/tests/PhpWord/Tests/Writer/PDF/TCPDFTest.php b/tests/PhpWord/Tests/Writer/PDF/TCPDFTest.php new file mode 100644 index 00000000..c73f0043 --- /dev/null +++ b/tests/PhpWord/Tests/Writer/PDF/TCPDFTest.php @@ -0,0 +1,51 @@ +addSection(); + $section->addText('Test 1'); + + $rendererName = Settings::PDF_RENDERER_TCPDF; + $rendererLibraryPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/tecnick.com/tcpdf'); + Settings::setPdfRenderer($rendererName, $rendererLibraryPath); + $writer = new PDF($phpWord); + $writer->save($file); + + $this->assertTrue(file_exists($file)); + + unlink($file); + } +}