diff --git a/.travis.yml b/.travis.yml index f322426d..ad3df613 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,9 @@ before_script: ## Composer # - curl -s http://getcomposer.org/installer | php # - php composer.phar install --prefer-source + - composer self-update + - composer require dompdf/dompdf:0.6.* - composer install --prefer-source - - composer selfupdate --quiet ## PHP_CodeSniffer - pyrus install pear/PHP_CodeSniffer - phpenv rehash diff --git a/CHANGELOG.md b/CHANGELOG.md index 72b93e7b..759f41e8 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ This is the changelog between releases of PHPWord. Releases are listed in reverse chronological order with the latest version listed on top, while additions/changes in each release are listed in chronological order. Changes in each release are divided into three parts: added or change features, bugfixes, and miscellaneous improvements. Each line contains short information about the change made, the person who made it, and the related issue number(s) in GitHub. -## 0.9.2 - 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. +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 @@ -27,12 +27,18 @@ This release marked heavy refactorings on internal code structure with the creat - Object: Ability to add object in header, footer, textrun, and footnote - @ivanlanin GH-187 - Media: Add `Media::resetElements()` to reset all media data - @juzi GH-19 - General: Add `Style::resetStyles()`, `Footnote::resetElements()`, and `TOC::resetTitles()` - @ivanlanin GH-187 -- Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table - @ivanlanin +- DOCX Reader: Ability to read header, footer, footnotes, link, preservetext, textbreak, pagebreak, table, list, image - @ivanlanin - Endnote: Ability to add endnotes - @ivanlanin +- ListItem: Ability to create custom list and reset list number - @ivanlanin GH-10 GH-198 +- ODT Writer: Basic table writing support - @ivanlanin +- Image: Keep image aspect ratio if only 1 dimension styled - @japonicus GH-194 +- HTML Writer: Basic HTML writer: text, textrun, link, title, textbreak, table, image (as Base64), footnote, endnote - @ivanlanin GH-203 GH-67 GH-147 +- PDF Writer: Basic PDF writer using DomPDF: All HTML element except image - @ivanlanin GH-68 ### Bugfixes - Footnote: Footnote content doesn't show footnote reference number - @ivanlanin GH-170 +- Documentation : Error in a fonction - @theBeerNut GH-195 ### Deprecated @@ -61,6 +67,8 @@ This release marked heavy refactorings on internal code structure with the creat - Style: New `Style\AbstractStyle` abstract class - @ivanlanin GH-187 - Writer: New 'ODText\Base` class - @ivanlanin GH-187 - General: Rename `Footnote` to `Footnotes` to reflect the nature of collection - @ivanlanin +- General: Add some unit tests for Shared & Element (100%!) - @Progi1984 +- Test: Add some samples and tests for image wrapping style - @brunocasado GH-59 ## 0.9.1 - 27 Mar 2014 diff --git a/composer.json b/composer.json index 6a2d9573..97d3e003 100644 --- a/composer.json +++ b/composer.json @@ -40,9 +40,10 @@ "phpunit/phpunit": "3.7.*" }, "suggest": { - "ext-gd2": "*", - "ext-xmlwriter": "*", - "ext-xsl": "*" + "ext-gd2": "Required to add images", + "ext-xmlwriter": "Required to write DOCX and ODT", + "ext-xsl": "Required to apply XSL style sheet to template part", + "dompdf/dompdf": "Required to write PDF" }, "autoload": { "psr-4": { diff --git a/docs/elements.rst b/docs/elements.rst index 62ee007f..b6842b65 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -207,10 +207,14 @@ Lists To add a list item use the function ``addListItem``. +Basic usage: + .. code-block:: php $section->addListItem($text, [$depth], [$fontStyle], [$listStyle], [$paragraphStyle]); +Parameters: + - ``$text`` Text that appears in the document. - ``$depth`` Depth of list item. - ``$fontStyle`` See "Font style" section. @@ -219,6 +223,40 @@ To add a list item use the function ``addListItem``. PHPWord\_Style\_ListItem. - ``$paragraphStyle`` See "Paragraph style" section. +Advanced usage: + +You can also create your own numbering style by changing the ``$listStyle`` parameter +with the name of your numbering style. + +.. code-block:: php + + $phpWord->addNumberingStyle( + 'multilevel', + array('type' => 'multilevel', 'levels' => array( + array('format' => 'decimal', 'text' => '%1.', 'left' => 360, 'hanging' => 360, 'tabPos' => 360), + array('format' => 'upperLetter', 'text' => '%2.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720), + ) + ) + ); + $section->addListItem('List Item I', 0, null, 'multilevel'); + $section->addListItem('List Item I.a', 1, null, 'multilevel'); + $section->addListItem('List Item I.b', 1, null, 'multilevel'); + $section->addListItem('List Item II', 0, null, 'multilevel'); + +Level styles: + +- ``start`` Starting value +- ``format`` Numbering format bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter +- ``restart`` Restart numbering level symbol +- ``suffix`` Content between numbering symbol and paragraph text tab|space|nothing +- ``text`` Numbering level text e.g. %1 for nonbullet or bullet character +- ``align`` Numbering symbol align left|center|right|both +- ``left`` See paragraph style +- ``hanging`` See paragraph style +- ``tabPos`` See paragraph style +- ``font`` Font name +- ``hint`` See font style + Tables ------ diff --git a/docs/general.rst b/docs/general.rst index c267d87d..c7e55d5a 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -63,9 +63,9 @@ XML Writer compatibility ~~~~~~~~~~~~~~~~~~~~~~~~ This option sets -```XMLWriter::setIndent`` `__ +`XMLWriter::setIndent `__ and -```XMLWriter::setIndentString`` `__. +`XMLWriter::setIndentString `__. The default value of this option is ``true`` (compatible), which is `required for OpenOffice `__ to render OOXML document correctly. You can set this option to ``false`` @@ -108,7 +108,7 @@ name. Use the following functions: .. code-block:: php - $properties = $phpWord->getProperties(); + $properties = $phpWord->getDocumentProperties(); $properties->setCreator('My name'); $properties->setCompany('My factory'); $properties->setTitle('My title'); diff --git a/docs/intro.rst b/docs/intro.rst index 0f1f3d74..a617777d 100644 --- a/docs/intro.rst +++ b/docs/intro.rst @@ -62,63 +62,63 @@ Below are the supported features for each file formats. Writers ~~~~~~~ -+-------------------------------------------------+--------+-------+-------+ -| Features | DOCX | ODT | RTF | -+=========================+=======================+========+=======+=======+ -| **Document Properties** | Standard | | | | -+ +-----------------------+--------+-------+-------+ -| | Extended | | | | -+ +-----------------------+--------+-------+-------+ -| | UserDefined | | | | -+-------------------------+-----------------------+--------+-------+-------+ -| **Element Type** | Text | ✓ | ✓ | ✓ | -+ +-----------------------+--------+-------+-------+ -| | Text Run | ✓ | ✓ | ✓ | -+ +-----------------------+--------+-------+-------+ -| | Title | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Link | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Preserve Text | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Text Break | ✓ | ✓ | ✓ | -+ +-----------------------+--------+-------+-------+ -| | Page Break | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | List | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Table | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Image | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Object | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Watermark | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Table of Contents | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Header | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Footer | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Footnote | ✓ | | | -+ +-----------------------+--------+-------+-------+ -| | Endnote | ✓ | | | -+-------------------------+-----------------------+--------+-------+-------+ -| **Graphs** | 2D basic graphs | | | | -+ +-----------------------+--------+-------+-------+ -| | 2D advanced graphs | | | | -+ +-----------------------+--------+-------+-------+ -| | 3D graphs | | | | -+-------------------------+-----------------------+--------+-------+-------+ -| **Math** | OMML support | | | | -+ +-----------------------+--------+-------+-------+ -| | MathML support | | | | -+-------------------------+-----------------------+--------+-------+-------+ -| **Bonus** | Encryption | | | | -+ +-----------------------+--------+-------+-------+ -| | Protection | | | | -+-------------------------+-----------------------+--------+-------+-------+ ++-------------------------------------------------+--------+-------+-------+-------+-------+ +| Features | DOCX | ODT | RTF | HTML | PDF | ++=========================+=======================+========+=======+=======+=======+=======+ +| **Document Properties** | Standard | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Extended | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | UserDefined | ✓ | | | | | ++-------------------------+-----------------------+--------+-------+-------+-------+-------+ +| **Element Type** | Text | ✓ | ✓ | ✓ | ✓ | ✓ | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Text Run | ✓ | ✓ | ✓ | ✓ | ✓ | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Title | ✓ | | | ✓ | ✓ | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Link | ✓ | | | ✓ | ✓ | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Preserve Text | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Text Break | ✓ | ✓ | ✓ | ✓ | ✓ | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Page Break | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | List | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Table | ✓ | ✓ | | ✓ | ✓ | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Image | ✓ | | | ✓ | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Object | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Watermark | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Table of Contents | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Header | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Footer | ✓ | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Footnote | ✓ | | | ✓ | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Endnote | ✓ | | | ✓ | | ++-------------------------+-----------------------+--------+-------+-------+-------+-------+ +| **Graphs** | 2D basic graphs | | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | 2D advanced graphs | | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | 3D graphs | | | | | | ++-------------------------+-----------------------+--------+-------+-------+-------+-------+ +| **Math** | OMML support | | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | MathML support | | | | | | ++-------------------------+-----------------------+--------+-------+-------+-------+-------+ +| **Bonus** | Encryption | | | | | | ++ +-----------------------+--------+-------+-------+-------+-------+ +| | Protection | | | | | | ++-------------------------+-----------------------+--------+-------+-------+-------+-------+ Readers diff --git a/samples/Sample_01_SimpleText.php b/samples/Sample_01_SimpleText.php index 02b78bd7..659686d3 100755 --- a/samples/Sample_01_SimpleText.php +++ b/samples/Sample_01_SimpleText.php @@ -45,13 +45,7 @@ $section->addTextBreak(); $section->addImage('resources/_earth.jpg', array('width'=>18, 'height'=>18)); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_02_TabStops.php b/samples/Sample_02_TabStops.php index dbed59a3..d6e4cdbc 100755 --- a/samples/Sample_02_TabStops.php +++ b/samples/Sample_02_TabStops.php @@ -33,13 +33,7 @@ $section->addText("Left Aligned\tRight Aligned", null, 'rightTab'); $section->addText("\tCenter Aligned", null, 'centerTab'); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_03_Sections.php b/samples/Sample_03_Sections.php index 4953b6a6..bfbc84af 100755 --- a/samples/Sample_03_Sections.php +++ b/samples/Sample_03_Sections.php @@ -26,13 +26,7 @@ $section->addHeader()->addText('Header'); $section->addFooter()->addText('Footer'); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_04_Textrun.php b/samples/Sample_04_Textrun.php index 354eadfd..f415ca17 100644 --- a/samples/Sample_04_Textrun.php +++ b/samples/Sample_04_Textrun.php @@ -34,13 +34,7 @@ $textrun->addObject('resources/_sheet.xls'); $textrun->addText(' Here is some more text. '); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_05_Multicolumn.php b/samples/Sample_05_Multicolumn.php index 478a8dd0..a3083824 100644 --- a/samples/Sample_05_Multicolumn.php +++ b/samples/Sample_05_Multicolumn.php @@ -36,13 +36,7 @@ $section = $phpWord->addSection(array('breakType' => 'continuous')); $section->addText('Normal paragraph again.'); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_06_Footnote.php b/samples/Sample_06_Footnote.php index 75120088..1bec44e4 100755 --- a/samples/Sample_06_Footnote.php +++ b/samples/Sample_06_Footnote.php @@ -38,13 +38,7 @@ $footnote = $section->addFootnote(); $footnote->addText('The reference for this is wrapped in its own line'); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_07_TemplateCloneRow.php b/samples/Sample_07_TemplateCloneRow.php index 3049466d..0e79e69f 100755 --- a/samples/Sample_07_TemplateCloneRow.php +++ b/samples/Sample_07_TemplateCloneRow.php @@ -60,4 +60,8 @@ echo date('H:i:s'), " Write to Word2007 format", EOL; $document->saveAs($name); rename($name, "results/{$name}"); -include_once 'Sample_Footer.php'; +$writers = array('Word2007' => 'docx'); +echo getEndingNotes($writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_08_ParagraphPagination.php b/samples/Sample_08_ParagraphPagination.php index bf1400a0..57fbab09 100644 --- a/samples/Sample_08_ParagraphPagination.php +++ b/samples/Sample_08_ParagraphPagination.php @@ -46,13 +46,7 @@ $section->addText('Paragraph with pageBreakBefore = true (default: false). ' . null, array('pageBreakBefore' => true)); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php index 5af73fb6..fd930b2b 100644 --- a/samples/Sample_09_Tables.php +++ b/samples/Sample_09_Tables.php @@ -85,13 +85,7 @@ $table->addCell(2000, $cellVCentered)->addText('D', null, $cellHCentered); $table->addCell(null, $cellRowContinue); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_10_EastAsianFontStyle.php b/samples/Sample_10_EastAsianFontStyle.php index 39f449c3..44bca8a6 100644 --- a/samples/Sample_10_EastAsianFontStyle.php +++ b/samples/Sample_10_EastAsianFontStyle.php @@ -10,13 +10,7 @@ $header = array('size' => 16, 'bold' => true); $section->addText('中文楷体样式测试',array('name' => '楷体', 'size' => 16, 'color' => '1B2232')); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_11_ReadWord2007.php b/samples/Sample_11_ReadWord2007.php index e5112e2e..09d9cab0 100644 --- a/samples/Sample_11_ReadWord2007.php +++ b/samples/Sample_11_ReadWord2007.php @@ -7,13 +7,8 @@ $source = "resources/{$name}.docx"; echo date('H:i:s'), " Reading contents from `{$source}`", EOL; $phpWord = \PhpOffice\PhpWord\IOFactory::load($source); -// (Re)write contents -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_12_HeaderFooter.php b/samples/Sample_12_HeaderFooter.php index 15309041..8e053286 100644 --- a/samples/Sample_12_HeaderFooter.php +++ b/samples/Sample_12_HeaderFooter.php @@ -59,15 +59,8 @@ $sec2Header->addText("All pages in Section 2 will Have this!"); $section2->addTextBreak(); $section2->addText('Some text...'); - // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_13_Images.php b/samples/Sample_13_Images.php index 4ef325b8..65e38bf5 100644 --- a/samples/Sample_13_Images.php +++ b/samples/Sample_13_Images.php @@ -10,24 +10,29 @@ $section = $phpWord->addSection(); $section->addText('Local image without any styles:'); $section->addImage('resources/_mars.jpg'); $section->addTextBreak(2); -// + $section->addText('Local image with styles:'); $section->addImage('resources/_earth.jpg', array('width' => 210, 'height' => 210, 'align' => 'center')); $section->addTextBreak(2); +// Remote image $source = 'http://php.net/images/logos/php-med-trans-light.gif'; $section->addText("Remote image from: {$source}"); $section->addImage($source); -// End code -// Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +//Wrapping style +$text = str_repeat('Hello World! ', 15); +$wrappingStyles = array('inline', 'behind', 'infront', 'square', 'tight'); +foreach ($wrappingStyles as $wrappingStyle) { + $section->addTextBreak(5); + $section->addText('Wrapping style ' . $wrappingStyle); + $section->addImage('resources/_earth.jpg', array('marginTop' => -1, 'marginLeft' => 1, + 'width' => 80, 'height' => 80, 'wrappingStyle' => $wrappingStyle)); + $section->addText($text); } -include_once 'Sample_Footer.php'; +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_14_ListItem.php b/samples/Sample_14_ListItem.php index 4cd9edea..cd22df8e 100644 --- a/samples/Sample_14_ListItem.php +++ b/samples/Sample_14_ListItem.php @@ -8,50 +8,54 @@ $phpWord = new \PhpOffice\PhpWord\PhpWord(); // Begin code $section = $phpWord->addSection(); -// Add listitem elements -$section->addListItem('List Item 1', 0); -$section->addListItem('List Item 2', 0); -$section->addListItem('List Item 3', 0); -$section->addTextBreak(2); +// Style definition -// Add listitem elements -$section->addListItem('List Item 1', 0); -$section->addListItem('List Item 1.1', 1); -$section->addListItem('List Item 1.2', 1); -$section->addListItem('List Item 1.3 (styled)', 1, array('bold'=>true)); -$section->addListItem('List Item 1.3.1', 2); -$section->addListItem('List Item 1.3.2', 2); -$section->addTextBreak(2); - -// Add listitem elements -$listStyle = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER); -$section->addListItem('List Item 1', 0, null, $listStyle); -$section->addListItem('List Item 2', 0, null, $listStyle); -$section->addListItem('List Item 3', 0, null, $listStyle); -$section->addTextBreak(2); - -// Add listitem elements $phpWord->addFontStyle('myOwnStyle', array('color'=>'FF0000')); $phpWord->addParagraphStyle('P-Style', array('spaceAfter'=>95)); -$listStyle = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER_NESTED); -$section->addListItem('List Item 1', 0, 'myOwnStyle', $listStyle, 'P-Style'); -$section->addListItem('List Item 2', 0, 'myOwnStyle', $listStyle, 'P-Style'); -$section->addListItem('List Item 3', 1, 'myOwnStyle', $listStyle, 'P-Style'); -$section->addListItem('List Item 4', 1, 'myOwnStyle', $listStyle, 'P-Style'); -$section->addListItem('List Item 5', 2, 'myOwnStyle', $listStyle, 'P-Style'); -$section->addListItem('List Item 6', 1, 'myOwnStyle', $listStyle, 'P-Style'); -$section->addListItem('List Item 7', 0, 'myOwnStyle', $listStyle, 'P-Style'); +$phpWord->addNumberingStyle( + 'multilevel', + array('type' => 'multilevel', 'levels' => array( + array('format' => 'decimal', 'text' => '%1.', 'left' => 360, 'hanging' => 360, 'tabPos' => 360), + array('format' => 'upperLetter', 'text' => '%2.', 'left' => 720, 'hanging' => 360, 'tabPos' => 720), + ) + ) +); +$predefinedMultilevel = array('listType' => \PhpOffice\PhpWord\Style\ListItem::TYPE_NUMBER_NESTED); -// End code +// Lists + +$section->addText('Multilevel list.'); +$section->addListItem('List Item I', 0, null, 'multilevel'); +$section->addListItem('List Item I.a', 1, null, 'multilevel'); +$section->addListItem('List Item I.b', 1, null, 'multilevel'); +$section->addListItem('List Item II', 0, null, 'multilevel'); +$section->addListItem('List Item II.a', 1, null, 'multilevel'); +$section->addListItem('List Item III', 0, null, 'multilevel'); +$section->addTextBreak(2); + +$section->addText('Basic simple bulleted list.'); +$section->addListItem('List Item 1'); +$section->addListItem('List Item 2'); +$section->addListItem('List Item 3'); +$section->addTextBreak(2); + +$section->addText('Continue from multilevel list above.'); +$section->addListItem('List Item IV', 0, null, 'multilevel'); +$section->addListItem('List Item IV.a', 1, null, 'multilevel'); +$section->addTextBreak(2); + +$section->addText('Multilevel predefined list.'); +$section->addListItem('List Item 1', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addListItem('List Item 2', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addListItem('List Item 3', 1, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addListItem('List Item 4', 1, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addListItem('List Item 5', 2, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addListItem('List Item 6', 1, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addListItem('List Item 7', 0, 'myOwnStyle', $predefinedMultilevel, 'P-Style'); +$section->addTextBreak(2); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_15_Link.php b/samples/Sample_15_Link.php index c4435ac0..1bd61e79 100644 --- a/samples/Sample_15_Link.php +++ b/samples/Sample_15_Link.php @@ -16,16 +16,8 @@ $phpWord->addLinkStyle('myOwnLinkStyle', array('bold'=>true, 'color'=>'808000')) $section->addLink('http://www.bing.com', null, 'myOwnLinkStyle'); $section->addLink('http://www.yahoo.com', null, 'myOwnLinkStyle'); -// End code - // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_16_Object.php b/samples/Sample_16_Object.php index cb8de6db..af23a00f 100644 --- a/samples/Sample_16_Object.php +++ b/samples/Sample_16_Object.php @@ -11,16 +11,8 @@ $section->addText('You can open this OLE object by double clicking on the icon:' $section->addTextBreak(2); $section->addObject('resources/_sheet.xls'); -// End code - // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_17_TitleTOC.php b/samples/Sample_17_TitleTOC.php index 15de531b..b18b1bc9 100644 --- a/samples/Sample_17_TitleTOC.php +++ b/samples/Sample_17_TitleTOC.php @@ -67,16 +67,9 @@ $section->addTitle('Subtitle 3.1.2', 3); $section->addText('Text'); echo date('H:i:s'), " Note: Please refresh TOC manually.", EOL; -// End code // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_18_Watermark.php b/samples/Sample_18_Watermark.php index d0ea786a..313cfbed 100644 --- a/samples/Sample_18_Watermark.php +++ b/samples/Sample_18_Watermark.php @@ -12,16 +12,8 @@ $header = $section->addHeader(); $header->addWatermark('resources/_earth.jpg', array('marginTop' => 200, 'marginLeft' => 55)); $section->addText('The header reference to the current section includes a watermark image.'); -// End code - // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_19_TextBreak.php b/samples/Sample_19_TextBreak.php index d7e11ea4..a209ce39 100644 --- a/samples/Sample_19_TextBreak.php +++ b/samples/Sample_19_TextBreak.php @@ -25,16 +25,8 @@ $section->addText('Text break with inline paragraph style:'); $section->addTextBreak(1, null, $paragraphStyle); $section->addText('Done.'); -// End code - // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_20_BGColor.php b/samples/Sample_20_BGColor.php index fec40859..892cd8b2 100644 --- a/samples/Sample_20_BGColor.php +++ b/samples/Sample_20_BGColor.php @@ -11,13 +11,7 @@ $section->addText("This one uses bgColor and is using hex value (0xfbbb10)", arr $section->addText("Compatible with font colors", array("color"=>"0000ff", "bgColor" => "fbbb10")); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_21_TableRowRules.php b/samples/Sample_21_TableRowRules.php index 10ad156d..b600ec00 100644 --- a/samples/Sample_21_TableRowRules.php +++ b/samples/Sample_21_TableRowRules.php @@ -28,13 +28,7 @@ $section->addText("In this example, image is 250px height. Rows are calculated i $section->addText("So: $"."table2->addRow(3750, array('exactHeight'=>true));"); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_22_CheckBox.php b/samples/Sample_22_CheckBox.php index 89827388..e7aae5ba 100644 --- a/samples/Sample_22_CheckBox.php +++ b/samples/Sample_22_CheckBox.php @@ -15,13 +15,7 @@ $cell = $table->addCell(); $cell->addCheckBox('chkBox2', 'Checkbox 2'); // Save file -$name = basename(__FILE__, '.php'); -$writers = array('Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf'); -foreach ($writers as $writer => $extension) { - echo date('H:i:s'), " Write to {$writer} format", EOL; - $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); - $xmlWriter->save("{$name}.{$extension}"); - rename("{$name}.{$extension}", "results/{$name}.{$extension}"); +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; } - -include_once 'Sample_Footer.php'; diff --git a/samples/Sample_23_TemplateBlock.php b/samples/Sample_23_TemplateBlock.php index bbde5a72..d0856ec2 100644 --- a/samples/Sample_23_TemplateBlock.php +++ b/samples/Sample_23_TemplateBlock.php @@ -18,4 +18,8 @@ echo date('H:i:s'), " Write to Word2007 format", EOL; $document->saveAs($name); rename($name, "results/{$name}"); -include_once 'Sample_Footer.php'; +$writers = array('Word2007' => 'docx'); +echo getEndingNotes($writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/Sample_Footer.php b/samples/Sample_Footer.php index 4d5777c2..f7e2305f 100644 --- a/samples/Sample_Footer.php +++ b/samples/Sample_Footer.php @@ -2,33 +2,10 @@ /** * Footer file */ -// Do not show execution time for index -if (!IS_INDEX) { - echo date('H:i:s'), " Done writing file(s)", EOL; - echo date('H:i:s'), " Peak memory usage: ", (memory_get_peak_usage(true) / 1024 / 1024), " MB", EOL; -} -// Show message when executed with CLI, show links when using browsers -if (CLI) { - echo 'The results are stored in the "results" subdirectory.', EOL; -} else { - if (!IS_INDEX) { - $types = array('docx', 'odt', 'rtf'); - echo '

 

'; - echo '

Results: '; - foreach ($types as $type) { - $result = 'results/' . SCRIPT_FILENAME . '.' . $type; - if (file_exists($result)) { - echo "{$type} "; - } - } - echo '

'; - } ?> - '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 if (CLI) { return; @@ -21,6 +32,7 @@ $pageHeading = str_replace('_', ' ', SCRIPT_FILENAME); $pageTitle = IS_INDEX ? 'Welcome to ' : "{$pageHeading} - "; $pageTitle .= 'PHPWord'; $pageHeading = IS_INDEX ? '' : "

{$pageHeading}

"; + // Populate samples $files = ''; if ($handle = opendir('.')) { @@ -32,6 +44,73 @@ if ($handle = opendir('.')) { } closedir($handle); } + +/** + * Write documents + * + * @param \PhpOffice\PhpWord\PhpWord $phpWord + * @param string $filename + * @param array $writers + */ +function write($phpWord, $filename, $writers) +{ + $result = ''; + + // Write documents + foreach ($writers as $writer => $extension) { + $result .= date('H:i:s') . " Write to {$writer} format"; + if (!is_null($extension)) { + $xmlWriter = \PhpOffice\PhpWord\IOFactory::createWriter($phpWord, $writer); + $xmlWriter->save("{$filename}.{$extension}"); + rename("{$filename}.{$extension}", "results/{$filename}.{$extension}"); + } else { + $result .= ' ... NOT DONE!'; + } + $result .= EOL; + } + + $result .= getEndingNotes($writers); + + return $result; +} + +/** + * Get ending notes + * + * @param array $writers + */ +function getEndingNotes($writers) +{ + $result = ''; + + // Do not show execution time for index + if (!IS_INDEX) { + $result .= date('H:i:s') . " Done writing file(s)" . EOL; + $result .= date('H:i:s') . " Peak memory usage: " . (memory_get_peak_usage(true) / 1024 / 1024) . " MB" . EOL; + } + + // Return + if (CLI) { + $result .= 'The results are stored in the "results" subdirectory.' . EOL; + } else { + if (!IS_INDEX) { + $types = array_values($writers); + $result .= '

 

'; + $result .= '

Results: '; + foreach ($types as $type) { + if (!is_null($type)) { + $resultFile = 'results/' . SCRIPT_FILENAME . '.' . $type; + if (file_exists($resultFile)) { + $result .= "{$type} "; + } + } + } + $result .= '

'; + } + } + + return $result; +} ?> <?php echo $pageTitle; ?> diff --git a/samples/index.php b/samples/index.php index 451cf381..8c742729 100644 --- a/samples/index.php +++ b/samples/index.php @@ -24,5 +24,7 @@ foreach ($requirements as $key => $value) { echo "
  • {$value[0]} ... {$status}
  • "; } echo ""; -} // if (!CLI) -include_once 'Sample_Footer.php'; +} +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/src/PhpWord/Element/AbstractElement.php b/src/PhpWord/Element/AbstractElement.php index 17313754..3fa36c23 100644 --- a/src/PhpWord/Element/AbstractElement.php +++ b/src/PhpWord/Element/AbstractElement.php @@ -22,7 +22,7 @@ use PhpOffice\PhpWord\TOC; /** * Container abstract class * - * @since 0.9.2 + * @since 0.10.0 */ abstract class AbstractElement { @@ -69,6 +69,20 @@ abstract class AbstractElement */ protected $elements = array(); + /** + * Index of element in the elements collection (start with 1) + * + * @var integer + */ + protected $elementIndex = 1; + + /** + * Unique Id for element + * + * @var integer + */ + protected $elementId; + /** * Relation Id * @@ -96,7 +110,7 @@ abstract class AbstractElement $text = String::toUTF8($text); $textObject = new Text($text, $fontStyle, $paragraphStyle); $textObject->setDocPart($this->getDocPart(), $this->getDocPartId()); - $this->elements[] = $textObject; + $this->addElement($textObject); return $textObject; } @@ -113,7 +127,7 @@ abstract class AbstractElement $textRun = new TextRun($paragraphStyle); $textRun->setDocPart($this->getDocPart(), $this->getDocPartId()); - $this->elements[] = $textRun; + $this->addElement($textRun); return $textRun; } @@ -136,7 +150,7 @@ abstract class AbstractElement $link->setDocPart($this->getDocPart(), $this->getDocPartId()); $rId = Media::addElement($elementDocPart, 'link', $linkSrc); $link->setRelationId($rId); - $this->elements[] = $link; + $this->addElement($link); return $link; } @@ -167,7 +181,7 @@ abstract class AbstractElement $bookmarkId = $data[1]; $title->setAnchor($anchor); $title->setBookmarkId($bookmarkId); - $this->elements[] = $title; + $this->addElement($title); return $title; } @@ -186,7 +200,7 @@ abstract class AbstractElement $preserveText = new PreserveText(String::toUTF8($text), $fontStyle, $paragraphStyle); $preserveText->setDocPart($this->getDocPart(), $this->getDocPartId()); - $this->elements[] = $preserveText; + $this->addElement($preserveText); return $preserveText; } @@ -205,7 +219,7 @@ abstract class AbstractElement for ($i = 1; $i <= $count; $i++) { $textBreak = new TextBreak($fontStyle, $paragraphStyle); $textBreak->setDocPart($this->getDocPart(), $this->getDocPartId()); - $this->elements[] = $textBreak; + $this->addElement($textBreak); } } @@ -225,7 +239,7 @@ abstract class AbstractElement $listItem = new ListItem(String::toUTF8($text), $depth, $fontStyle, $styleList, $paragraphStyle); $listItem->setDocPart($this->getDocPart(), $this->getDocPartId()); - $this->elements[] = $listItem; + $this->addElement($listItem); return $listItem; } @@ -241,7 +255,7 @@ abstract class AbstractElement $this->checkValidity('table'); $table = new Table($this->getDocPart(), $this->getDocPartId(), $style); - $this->elements[] = $table; + $this->addElement($table); return $table; } @@ -263,7 +277,8 @@ abstract class AbstractElement $image->setDocPart($this->getDocPart(), $this->getDocPartId()); $rId = Media::addElement($elementDocPart, 'image', $src, $image); $image->setRelationId($rId); - $this->elements[] = $image; + $this->addElement($image); + return $image; } @@ -296,7 +311,8 @@ abstract class AbstractElement $object->setRelationId($rId); $rIdimg = Media::addElement($elementDocPart, 'image', $icon, new Image($icon)); $object->setImageRelationId($rIdimg); - $this->elements[] = $object; + $this->addElement($object); + return $object; } else { throw new InvalidObjectException(); @@ -318,7 +334,7 @@ abstract class AbstractElement $footnote->setDocPart('footnote', $this->getDocPartId()); $footnote->setRelationId($rId); - $this->elements[] = $footnote; + $this->addElement($footnote); return $footnote; } @@ -338,7 +354,7 @@ abstract class AbstractElement $endnote->setDocPart('endnote', $this->getDocPartId()); $endnote->setRelationId($rId); - $this->elements[] = $endnote; + $this->addElement($endnote); return $endnote; } @@ -358,7 +374,7 @@ abstract class AbstractElement $checkBox = new CheckBox(String::toUTF8($name), String::toUTF8($text), $fontStyle, $paragraphStyle); $checkBox->setDocPart($this->getDocPart(), $this->getDocPartId()); - $this->elements[] = $checkBox; + $this->addElement($checkBox); return $checkBox; } @@ -405,6 +421,16 @@ abstract class AbstractElement return $this->docPartId; } + /** + * Set element index and unique id, and add element into elements collection + */ + protected function addElement(AbstractElement $element) + { + $element->setElementIndex($this->countElements() + 1); + $element->setElementId(); + $this->elements[] = $element; + } + /** * Get all elements * @@ -415,6 +441,54 @@ abstract class AbstractElement return $this->elements; } + /** + * Count elements + * + * @return integer + */ + public function countElements() + { + return count($this->elements); + } + + /** + * Get element index + * + * @return int + */ + public function getElementIndex() + { + return $this->elementIndex; + } + + /** + * Set element index + * + * @param int $value + */ + public function setElementIndex($value) + { + $this->elementIndex = $value; + } + + /** + * Get element unique ID + * + * @return string + */ + public function getElementId() + { + return $this->elementId; + } + + /** + * Set element unique ID from 6 first digit of md5 + */ + public function setElementId() + { + $this->elementId = substr(md5(rand()), 0, 6); + } + /** * Get relation Id * @@ -458,9 +532,6 @@ abstract class AbstractElement { if (!is_null($styleValue) && is_array($styleValue)) { foreach ($styleValue as $key => $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } $styleObject->setStyleValue($key, $value); } $style = $styleObject; @@ -555,7 +626,7 @@ abstract class AbstractElement * Create textrun element * * @param mixed $paragraphStyle - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function createTextRun($paragraphStyle = null) @@ -567,7 +638,7 @@ abstract class AbstractElement * Create footnote element * * @param mixed $paragraphStyle - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function createFootnote($paragraphStyle = null) diff --git a/src/PhpWord/Element/Endnote.php b/src/PhpWord/Element/Endnote.php index 33148ee6..07d21969 100644 --- a/src/PhpWord/Element/Endnote.php +++ b/src/PhpWord/Element/Endnote.php @@ -14,7 +14,7 @@ use PhpOffice\PhpWord\Style\Paragraph; /** * Endnote element * - * @since 0.9.2 + * @since 0.10.0 */ class Endnote extends Footnote { diff --git a/src/PhpWord/Element/Footer.php b/src/PhpWord/Element/Footer.php index 6d441126..8db9c6eb 100755 --- a/src/PhpWord/Element/Footer.php +++ b/src/PhpWord/Element/Footer.php @@ -44,7 +44,7 @@ class Footer extends AbstractElement * Set type * * @param string $value - * @since 0.9.2 + * @since 0.10.0 */ public function setType($value = self::AUTO) { @@ -55,7 +55,7 @@ class Footer extends AbstractElement * Get type * * @return string - * @since 0.9.2 + * @since 0.10.0 */ public function getType() { diff --git a/src/PhpWord/Element/Footnote.php b/src/PhpWord/Element/Footnote.php index cbd5afb5..de303330 100644 --- a/src/PhpWord/Element/Footnote.php +++ b/src/PhpWord/Element/Footnote.php @@ -48,7 +48,7 @@ class Footnote extends AbstractElement * Get Footnote Reference ID * * @return int - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function getReferenceId() @@ -60,7 +60,7 @@ class Footnote extends AbstractElement * Set Footnote Reference ID * * @param int $rId - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function setReferenceId($rId) diff --git a/src/PhpWord/Element/Header.php b/src/PhpWord/Element/Header.php index c14ed0e7..e4211d29 100755 --- a/src/PhpWord/Element/Header.php +++ b/src/PhpWord/Element/Header.php @@ -64,7 +64,7 @@ class Header extends AbstractElement * Set header type * * @param string $value - * @since 0.9.2 + * @since 0.10.0 */ public function setType($value = self::AUTO) { diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index 7ea28819..b9eeb23e 100755 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -18,6 +18,13 @@ use PhpOffice\PhpWord\Style\Image as ImageStyle; */ class Image extends AbstractElement { + /** + * Image source type constants + */ + const SOURCE_LOCAL = 'local'; // Local images + const SOURCE_GD = 'gd'; // Generated using GD + const SOURCE_ARCHIVE = 'archive'; // Image in archives zip://$archive#$image + /** * Image source * @@ -25,6 +32,13 @@ class Image extends AbstractElement */ private $source; + /** + * Source type: local|gd|archive + * + * @var string + */ + private $sourceType; + /** * Image style * @@ -85,57 +99,11 @@ class Image extends AbstractElement */ public function __construct($source, $style = null, $isWatermark = false) { - // Detect if it's a memory image, by .php ext or by URL - if (stripos(strrev($source), strrev('.php')) === 0) { - $this->isMemImage = true; - } else { - $this->isMemImage = (filter_var($source, FILTER_VALIDATE_URL) !== false); - } - - // Check supported types - if ($this->isMemImage) { - $supportedTypes = array('image/jpeg', 'image/gif', 'image/png'); - $imgData = @getimagesize($source); - if (!is_array($imgData)) { - throw new InvalidImageException(); - } - $this->imageType = $imgData['mime']; // string - if (!in_array($this->imageType, $supportedTypes)) { - throw new UnsupportedImageTypeException(); - } - } else { - $supportedTypes = array( - IMAGETYPE_JPEG, IMAGETYPE_GIF, - IMAGETYPE_PNG, IMAGETYPE_BMP, - IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM - ); - if (!file_exists($source)) { - throw new InvalidImageException(); - } - $imgData = getimagesize($source); - if (function_exists('exif_imagetype')) { - $this->imageType = exif_imagetype($source); - } else { - // @codeCoverageIgnoreStart - $tmp = getimagesize($source); - $this->imageType = $tmp[2]; - // @codeCoverageIgnoreEnd - } - if (!in_array($this->imageType, $supportedTypes)) { - throw new UnsupportedImageTypeException(); - } - $this->imageType = image_type_to_mime_type($this->imageType); - } - - // Set private properties $this->source = $source; - $this->isWatermark = $isWatermark; + $this->setIsWatermark($isWatermark); $this->style = $this->setStyle(new ImageStyle(), $style, true); - if ($this->style->getWidth() == null && $this->style->getHeight() == null) { - $this->style->setWidth($imgData[0]); - $this->style->setHeight($imgData[1]); - } - $this->setImageFunctions(); + + $this->checkImage($source); } /** @@ -158,6 +126,16 @@ class Image extends AbstractElement return $this->source; } + /** + * Get image source type + * + * @return string + */ + public function getSourceType() + { + return $this->sourceType; + } + /** * Get image media ID * @@ -239,9 +217,92 @@ class Image extends AbstractElement } /** - * Set image functions + * Check memory image, supported type, image functions, and proportional width/height + * + * @param string $source */ - private function setImageFunctions() + private function checkImage($source) + { + $this->setSourceType($source); + + // Check image data + if ($this->sourceType == self::SOURCE_ARCHIVE) { + $imageData = $this->getArchiveImageSize($source); + } else { + $imageData = @getimagesize($source); + } + if (!is_array($imageData)) { + throw new InvalidImageException(); + } + list($actualWidth, $actualHeight, $imageType) = $imageData; + + // Check image type support + $supportedTypes = array(IMAGETYPE_JPEG, IMAGETYPE_GIF, IMAGETYPE_PNG); + if ($this->sourceType != self::SOURCE_GD) { + $supportedTypes = array_merge($supportedTypes, array(IMAGETYPE_BMP, IMAGETYPE_TIFF_II, IMAGETYPE_TIFF_MM)); + } + if (!in_array($imageType, $supportedTypes)) { + throw new UnsupportedImageTypeException(); + } + + // Define image functions + $this->imageType = image_type_to_mime_type($imageType); + $this->setFunctions(); + $this->setProportionalSize($actualWidth, $actualHeight); + } + + /** + * Set source type + * + * @param string $source + */ + private function setSourceType($source) + { + if (stripos(strrev($source), strrev('.php')) === 0) { + $this->isMemImage = true; + $this->sourceType = self::SOURCE_GD; + } elseif (strpos($source, 'zip://') !== false) { + $this->isMemImage = false; + $this->sourceType = self::SOURCE_ARCHIVE; + } else { + $this->isMemImage = (filter_var($source, FILTER_VALIDATE_URL) !== false); + $this->sourceType = $this->isMemImage ? self::SOURCE_GD : self::SOURCE_LOCAL; + } + } + + /** + * Get image size from archive + * + * @param string $source + * @return array|null + */ + private function getArchiveImageSize($source) + { + $imageData = null; + $source = substr($source, 6); + list($zipFilename, $imageFilename) = explode('#', $source); + $tempFilename = tempnam(sys_get_temp_dir(), 'PHPWordImage'); + + $zip = new \ZipArchive(); + if ($zip->open($zipFilename) !== false) { + if ($zip->locateName($imageFilename)) { + $imageContent = $zip->getFromName($imageFilename); + if ($imageContent !== false) { + file_put_contents($tempFilename, $imageContent); + $imageData = @getimagesize($tempFilename); + unlink($tempFilename); + } + } + $zip->close(); + } + + return $imageData; + } + + /** + * Set image functions and extensions + */ + private function setFunctions() { switch ($this->imageType) { case 'image/png': @@ -260,8 +321,8 @@ class Image extends AbstractElement $this->imageFunc = 'imagejpeg'; $this->imageExtension = 'jpg'; break; - case 'image/x-ms-bmp': case 'image/bmp': + case 'image/x-ms-bmp': $this->imageType = 'image/bmp'; $this->imageExtension = 'bmp'; break; @@ -270,4 +331,26 @@ class Image extends AbstractElement break; } } + + /** + * Set proportional width/height if one dimension not available + * + * @param integer $actualWidth + * @param integer $actualHeight + */ + private function setProportionalSize($actualWidth, $actualHeight) + { + $styleWidth = $this->style->getWidth(); + $styleHeight = $this->style->getHeight(); + if (!($styleWidth && $styleHeight)) { + if ($styleWidth == null && $styleHeight == null) { + $this->style->setWidth($actualWidth); + $this->style->setHeight($actualHeight); + } elseif ($styleWidth) { + $this->style->setHeight($actualHeight * ($styleWidth / $actualWidth)); + } else { + $this->style->setWidth($actualWidth * ($styleHeight / $actualHeight)); + } + } + } } diff --git a/src/PhpWord/Element/ListItem.php b/src/PhpWord/Element/ListItem.php index e73183bd..66061537 100644 --- a/src/PhpWord/Element/ListItem.php +++ b/src/PhpWord/Element/ListItem.php @@ -43,15 +43,21 @@ class ListItem extends AbstractElement * * @param string $text * @param int $depth - * @param mixed $styleFont - * @param mixed $styleList - * @param mixed $styleParagraph + * @param mixed $fontStyle + * @param array|string|null $listStyle + * @param mixed $paragraphStyle */ - public function __construct($text, $depth = 0, $styleFont = null, $styleList = null, $styleParagraph = null) + public function __construct($text, $depth = 0, $fontStyle = null, $listStyle = null, $paragraphStyle = null) { - $this->textObject = new Text($text, $styleFont, $styleParagraph); + $this->textObject = new Text($text, $fontStyle, $paragraphStyle); $this->depth = $depth; - $this->style = $this->setStyle(new ListItemStyle(), $styleList, true); + + // Version >= 0.10.0 will pass numbering style name. Older version will use old method + if (!is_null($listStyle) && is_string($listStyle)) { + $this->style = new ListItemStyle($listStyle); + } else { + $this->style = $this->setStyle(new ListItemStyle(), $listStyle, true); + } } /** diff --git a/src/PhpWord/Element/Object.php b/src/PhpWord/Element/Object.php index 6375cb70..7407688a 100644 --- a/src/PhpWord/Element/Object.php +++ b/src/PhpWord/Element/Object.php @@ -101,7 +101,7 @@ class Object extends AbstractElement * Get Object ID * * @return int - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function getObjectId() @@ -113,7 +113,7 @@ class Object extends AbstractElement * Set Object ID * * @param int $objId - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function setObjectId($objId) diff --git a/src/PhpWord/Element/Section.php b/src/PhpWord/Element/Section.php index fc6c1787..d3c4447b 100644 --- a/src/PhpWord/Element/Section.php +++ b/src/PhpWord/Element/Section.php @@ -66,9 +66,6 @@ class Section extends AbstractElement if (is_null($value)) { continue; } - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } $this->settings->setSettingValue($key, $value); } } @@ -113,7 +110,7 @@ class Section extends AbstractElement * * @param string $type * @return Header - * @since 0.9.2 + * @since 0.10.0 */ public function addHeader($type = Header::AUTO) { @@ -125,7 +122,7 @@ class Section extends AbstractElement * * @param string $type * @return Footer - * @since 0.9.2 + * @since 0.10.0 */ public function addFooter($type = Header::AUTO) { @@ -177,7 +174,7 @@ class Section extends AbstractElement * @param boolean $header * @return Header|Footer * @throws \PhpOffice\PhpWord\Exception\Exception - * @since 0.9.2 + * @since 0.10.0 */ private function addHeaderFooter($type = Header::AUTO, $header = true) { @@ -201,7 +198,7 @@ class Section extends AbstractElement * Create header * * @return Header - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function createHeader() @@ -213,7 +210,7 @@ class Section extends AbstractElement * Create footer * * @return Footer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function createFooter() @@ -225,7 +222,7 @@ class Section extends AbstractElement * Get footer * * @return Footer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function getFooter() diff --git a/src/PhpWord/Element/Table.php b/src/PhpWord/Element/Table.php index 484dc9d1..56984754 100644 --- a/src/PhpWord/Element/Table.php +++ b/src/PhpWord/Element/Table.php @@ -33,7 +33,7 @@ class Table extends AbstractElement /** * Table width * - * @var int + * @var integer */ private $width = null; @@ -42,7 +42,7 @@ class Table extends AbstractElement * Create a new table * * @param string $docPart - * @param int $docPartId + * @param integer $docPartId * @param mixed $style */ public function __construct($docPart, $docPartId, $style = null) @@ -54,7 +54,7 @@ class Table extends AbstractElement /** * Add a row * - * @param int $height + * @param integer $height * @param mixed $style */ public function addRow($height = null, $style = null) @@ -67,7 +67,7 @@ class Table extends AbstractElement /** * Add a cell * - * @param int $width + * @param integer $width * @param mixed $style * @return Cell */ @@ -101,7 +101,7 @@ class Table extends AbstractElement /** * Set table width * - * @param int $width + * @param integer $width */ public function setWidth($width) { @@ -111,10 +111,31 @@ class Table extends AbstractElement /** * Get table width * - * @return int + * @return integer */ public function getWidth() { return $this->width; } + + /** + * Get column count + * + * @return integer + */ + public function countColumns() + { + $columnCount = 0; + if (is_array($this->rows)) { + $rowCount = count($this->rows); + for ($i = 0; $i < $rowCount; $i++) { + $cellCount = count($this->rows[$i]->getCells()); + if ($columnCount < $cellCount) { + $columnCount = $cellCount; + } + } + } + + return $columnCount; + } } diff --git a/src/PhpWord/Element/Title.php b/src/PhpWord/Element/Title.php index 5ed1bfbc..93b48d62 100644 --- a/src/PhpWord/Element/Title.php +++ b/src/PhpWord/Element/Title.php @@ -119,6 +119,16 @@ class Title extends AbstractElement return $this->text; } + /** + * Get depth + * + * @return integer + */ + public function getDepth() + { + return $this->depth; + } + /** * Get Title style * diff --git a/src/PhpWord/Endnotes.php b/src/PhpWord/Endnotes.php index cdc7d18d..6587dfe9 100644 --- a/src/PhpWord/Endnotes.php +++ b/src/PhpWord/Endnotes.php @@ -12,7 +12,7 @@ namespace PhpOffice\PhpWord; /** * Endnote collection * - * @since 0.9.2 + * @since 0.10.0 */ class Endnotes { diff --git a/src/PhpWord/Footnotes.php b/src/PhpWord/Footnotes.php index 7f4435b7..a0a5c42f 100644 --- a/src/PhpWord/Footnotes.php +++ b/src/PhpWord/Footnotes.php @@ -26,7 +26,7 @@ class Footnotes * * @param \PhpOffice\PhpWord\Element\Footnote $element * @return integer Reference ID - * @since 0.9.2 + * @since 0.10.0 */ public static function addElement($element) { @@ -41,7 +41,7 @@ class Footnotes * * @param integer $index * @param \PhpOffice\PhpWord\Element\Footnote $element - * @since 0.9.2 + * @since 0.10.0 */ public static function setElement($index, $element) { @@ -55,7 +55,7 @@ class Footnotes * * @param integer $index * @return \PhpOffice\PhpWord\Element\Footnote - * @since 0.9.2 + * @since 0.10.0 */ public static function getElement($index) { @@ -70,7 +70,7 @@ class Footnotes * Get elements * * @return array - * @since 0.9.2 + * @since 0.10.0 */ public static function getElements() { @@ -81,7 +81,7 @@ class Footnotes * Get element count * * @return integer - * @since 0.9.2 + * @since 0.10.0 */ public static function countElements() { @@ -91,7 +91,7 @@ class Footnotes /** * Reset elements * - * @since 0.9.2 + * @since 0.10.0 */ public static function resetElements() { @@ -103,7 +103,7 @@ class Footnotes * * @param \PhpOffice\PhpWord\Element\Footnote $element * @return integer Reference ID - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function addFootnoteElement($element) @@ -115,7 +115,7 @@ class Footnotes * Get Footnote Elements * * @return array - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function getFootnoteElements() @@ -127,7 +127,7 @@ class Footnotes * Get Footnote Elements Count * * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function countFootnoteElements() @@ -140,7 +140,7 @@ class Footnotes * * @param string $linkSrc * @return integer Reference ID - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function addFootnoteLinkElement($linkSrc) @@ -152,7 +152,7 @@ class Footnotes * Get Footnote Link Elements * * @return array - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function getFootnoteLinkElements() diff --git a/src/PhpWord/IOFactory.php b/src/PhpWord/IOFactory.php index 720a948b..350e4651 100644 --- a/src/PhpWord/IOFactory.php +++ b/src/PhpWord/IOFactory.php @@ -26,7 +26,7 @@ abstract class IOFactory */ public static function createWriter(PhpWord $phpWord, $name = 'Word2007') { - if ($name !== 'WriterInterface' && $name !== 'ODText' && $name !== 'RTF' && $name !== 'Word2007') { + if (!in_array($name, array('WriterInterface', 'Word2007', 'ODText', 'RTF', 'HTML', 'PDF'))) { throw new Exception("\"{$name}\" is not a valid writer."); } @@ -43,7 +43,7 @@ abstract class IOFactory */ public static function createReader($name = 'Word2007') { - if ($name !== 'ReaderInterface' && $name !== 'Word2007') { + if (!in_array($name, array('ReaderInterface', 'Word2007'))) { throw new Exception("\"{$name}\" is not a valid reader."); } diff --git a/src/PhpWord/Media.php b/src/PhpWord/Media.php index d938558d..8c06fe81 100755 --- a/src/PhpWord/Media.php +++ b/src/PhpWord/Media.php @@ -34,6 +34,7 @@ class Media * @return integer * @throws \PhpOffice\PhpWord\Exception\Exception * @since 0.9.2 + * @since 0.10.0 */ public static function addElement($container, $mediaType, $source, Image $image = null) { @@ -98,7 +99,7 @@ class Media * @param string $container section|headerx|footerx|footnote|endnote * @param string $mediaType image|object|link * @return integer - * @since 0.9.2 + * @since 0.10.0 */ public static function countElements($container, $mediaType = null) { @@ -125,7 +126,7 @@ class Media * @param string $container section|headerx|footerx|footnote|endnote * @param string $mediaType image|object|link * @return array - * @since 0.9.2 + * @since 0.10.0 */ public static function getElements($container, $mediaType = null) { @@ -171,7 +172,7 @@ class Media * @param string $type * @param \PhpOffice\PhpWord\Element\Image $image * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function addSectionMediaElement($src, $type, Image $image = null) @@ -184,7 +185,7 @@ class Media * * @param string $linkSrc * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function addSectionLinkElement($linkSrc) @@ -197,7 +198,7 @@ class Media * * @param string $key * @return array - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function getSectionMediaElements($key = null) @@ -210,7 +211,7 @@ class Media * * @param string $key * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function countSectionMediaElements($key = null) @@ -225,7 +226,7 @@ class Media * @param string $src * @param \PhpOffice\PhpWord\Element\Image $image * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function addHeaderMediaElement($headerCount, $src, Image $image = null) @@ -238,7 +239,7 @@ class Media * * @param string $key * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function countHeaderMediaElements($key) @@ -250,7 +251,7 @@ class Media * Get Header Media Elements * * @return array - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function getHeaderMediaElements() @@ -265,7 +266,7 @@ class Media * @param string $src * @param \PhpOffice\PhpWord\Element\Image $image * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function addFooterMediaElement($footerCount, $src, Image $image = null) @@ -278,7 +279,7 @@ class Media * * @param string $key * @return integer - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function countFooterMediaElements($key) @@ -290,7 +291,7 @@ class Media * Get Footer Media Elements * * @return array - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public static function getFooterMediaElements() diff --git a/src/PhpWord/PhpWord.php b/src/PhpWord/PhpWord.php index d9844b90..92427805 100644 --- a/src/PhpWord/PhpWord.php +++ b/src/PhpWord/PhpWord.php @@ -9,9 +9,11 @@ namespace PhpOffice\PhpWord; -use PhpOffice\PhpWord\Element\Section; +use PhpOffice\PhpWord\DocumentProperties; use PhpOffice\PhpWord\Exception\Exception; +use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Template; /** * PHPWord main class @@ -212,6 +214,17 @@ class PhpWord Style::addLinkStyle($styleName, $styles); } + /** + * Adds a numbering style + * + * @param string $styleName + * @param mixed $styles + */ + public function addNumberingStyle($styleName, $styles) + { + Style::addNumberingStyle($styleName, $styles); + } + /** * Get all sections * @@ -243,7 +256,7 @@ class PhpWord * * @param array $settings * @return \PhpOffice\PhpWord\Element\Section - * @deprecated 0.9.2 + * @deprecated 0.10.0 * @codeCoverageIgnore */ public function createSection($settings = null) diff --git a/src/PhpWord/Reader/Word2007.php b/src/PhpWord/Reader/Word2007.php index f5d8a178..34b2122d 100644 --- a/src/PhpWord/Reader/Word2007.php +++ b/src/PhpWord/Reader/Word2007.php @@ -9,16 +9,18 @@ namespace PhpOffice\PhpWord\Reader; -use PhpOffice\PhpWord\DocumentProperties; -use PhpOffice\PhpWord\Footnote; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Footnote; +use PhpOffice\PhpWord\Endnotes; +use PhpOffice\PhpWord\DocumentProperties; use PhpOffice\PhpWord\Shared\XMLReader; +use PhpOffice\PhpWord\Element\Section; /** * Reader for Word2007 * - * @since 0.9.2 + * @since 0.10.0 * @todo title, list, watermark, checkbox, toc * @todo Partly done: image, object */ @@ -38,6 +40,13 @@ class Word2007 extends AbstractReader implements ReaderInterface */ private $rels = array('main' => array(), 'document' => array()); + /** + * Filename + * + * @var string + */ + private $filename; + /** * Loads PhpWord from file * @@ -48,14 +57,27 @@ class Word2007 extends AbstractReader implements ReaderInterface { $this->phpWord = new PhpWord(); - $this->readRelationships($filename); + $this->filename = $filename; + $this->readRelationships(); + + // Read styles and numbering first + foreach ($this->rels['document'] as $rId => $rel) { + switch ($rel['type']) { + case 'styles': + $this->readStyles($rel['target']); + break; + case 'numbering': + $this->readNumbering($rel['target']); + break; + } + } // Read main relationship foreach ($this->rels['main'] as $rId => $rel) { switch ($rel['type']) { case 'officeDocument': - $this->readDocument($filename, $rel['target']); + $this->readDocument($rel['target']); break; case 'core-properties': @@ -71,31 +93,26 @@ class Word2007 extends AbstractReader implements ReaderInterface 'dcterms:modified' => 'setModified', ); $callbacks = array('dcterms:created' => 'strtotime', 'dcterms:modified' => 'strtotime'); - $this->readDocProps($filename, $rel['target'], $mapping, $callbacks); + $this->readDocProps($rel['target'], $mapping, $callbacks); break; case 'extended-properties': $mapping = array('Company' => 'setCompany', 'Manager' => 'setManager'); - $this->readDocProps($filename, $rel['target'], $mapping); + $this->readDocProps($rel['target'], $mapping); break; case 'custom-properties': - $this->readDocPropsCustom($filename, $rel['target']); + $this->readDocPropsCustom($rel['target']); break; } } - // Read document relationships + // Read footnotes and endnotes foreach ($this->rels['document'] as $rId => $rel) { switch ($rel['type']) { - - case 'styles': - $this->readStyles($filename, $rel['target']); - break; - case 'footnotes': case 'endnotes': - $this->readNotes($filename, $rel['target'], $rel['type']); + $this->readNotes($rel['target'], $rel['type']); break; } } @@ -105,24 +122,22 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read all relationship files - * - * @param string $filename */ - private function readRelationships($filename) + private function readRelationships() { // _rels/.rels - $this->rels['main'] = $this->getRels($filename, '_rels/.rels'); + $this->rels['main'] = $this->getRels('_rels/.rels'); // word/_rels/*.xml.rels $wordRelsPath = 'word/_rels/'; $zipClass = Settings::getZipClass(); $zip = new $zipClass(); - if ($zip->open($filename) === true) { + if ($zip->open($this->filename) === true) { for ($i = 0; $i < $zip->numFiles; $i++) { $xmlFile = $zip->getNameIndex($i); - if ((substr($xmlFile, 0, strlen($wordRelsPath))) == $wordRelsPath) { + if ((substr($xmlFile, 0, strlen($wordRelsPath))) == $wordRelsPath && (substr($xmlFile, -1)) != '/') { $docPart = str_replace('.xml.rels', '', str_replace($wordRelsPath, '', $xmlFile)); - $this->rels[$docPart] = $this->getRels($filename, $xmlFile, 'word/'); + $this->rels[$docPart] = $this->getRels($xmlFile, 'word/'); } } $zip->close(); @@ -132,15 +147,14 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read core and extended document properties * - * @param string $filename * @param string $xmlFile * @param array $mapping * @param array $callbacks */ - private function readDocProps($filename, $xmlFile, $mapping, $callbacks = array()) + private function readDocProps($xmlFile, $mapping, $callbacks = array()) { $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $docProps = $this->phpWord->getDocumentProperties(); $nodes = $xmlReader->getElements('*'); @@ -164,19 +178,17 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read custom document properties * - * @param string $filename * @param string $xmlFile */ - private function readDocPropsCustom($filename, $xmlFile) + private function readDocPropsCustom($xmlFile) { $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $docProps = $this->phpWord->getDocumentProperties(); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { - $nodeName = $node->nodeName; $propertyName = $xmlReader->getAttribute('name', $node); $attributeNode = $xmlReader->getElement('*', $node); $attributeType = $attributeNode->nodeName; @@ -191,19 +203,19 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read document.xml * - * @param string $filename * @param string $xmlFile */ - private function readDocument($filename, $xmlFile) + private function readDocument($xmlFile) { $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $nodes = $xmlReader->getElements('w:body/*'); if ($nodes->length > 0) { $section = $this->phpWord->addSection(); foreach ($nodes as $node) { switch ($node->nodeName) { + case 'w:p': // Paragraph if ($xmlReader->getAttribute('w:type', $node, 'w:r/w:br') == 'page') { $section->addPageBreak(); // PageBreak @@ -213,19 +225,27 @@ class Word2007 extends AbstractReader implements ReaderInterface // Section properties if ($xmlReader->elementExists('w:pPr/w:sectPr', $node)) { $settingsNode = $xmlReader->getElement('w:pPr/w:sectPr', $node); - $settings = $this->readSectionStyle($xmlReader, $settingsNode); - $section->setSettings($settings); - $this->readHeaderFooter($filename, $settings, $section); + if (!is_null($settingsNode)) { + $settings = $this->readSectionStyle($xmlReader, $settingsNode); + $section->setSettings($settings); + if (!is_null($settings)) { + $this->readHeaderFooter($settings, $section); + } + } $section = $this->phpWord->addSection(); } break; + case 'w:tbl': // Table $this->readTable($xmlReader, $node, $section, 'document'); break; + case 'w:sectPr': // Last section $settings = $this->readSectionStyle($xmlReader, $node); $section->setSettings($settings); - $this->readHeaderFooter($filename, $settings, $section); + if (!is_null($settings)) { + $this->readHeaderFooter($settings, $section); + } break; } } @@ -235,13 +255,12 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read styles.xml * - * @param string $filename * @param string $xmlFile */ - private function readStyles($filename, $xmlFile) + private function readStyles($xmlFile) { $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $nodes = $xmlReader->getElements('w:style'); if ($nodes->length > 0) { @@ -253,21 +272,26 @@ class Word2007 extends AbstractReader implements ReaderInterface } // $default = ($xmlReader->getAttribute('w:default', $node) == 1); switch ($type) { + case 'paragraph': $pStyle = $this->readParagraphStyle($xmlReader, $node); $fStyle = $this->readFontStyle($xmlReader, $node); if (empty($fStyle)) { - $this->phpWord->addParagraphStyle($name, $pStyle); + if (is_array($pStyle)) { + $this->phpWord->addParagraphStyle($name, $pStyle); + } } else { $this->phpWord->addFontStyle($name, $fStyle, $pStyle); } break; + case 'character': $fStyle = $this->readFontStyle($xmlReader, $node); if (!empty($fStyle)) { $this->phpWord->addFontStyle($name, $fStyle); } break; + case 'table': $tStyle = $this->readTableStyle($xmlReader, $node); if (!empty($tStyle)) { @@ -279,14 +303,104 @@ class Word2007 extends AbstractReader implements ReaderInterface } } + /** + * Read numbering.xml + * + * @param string $xmlFile + */ + private function readNumbering($xmlFile) + { + $abstracts = array(); + $numberings = array(); + $xmlReader = new XMLReader(); + $xmlReader->getDomFromZip($this->filename, $xmlFile); + + // Abstract numbering definition + $nodes = $xmlReader->getElements('w:abstractNum'); + if ($nodes->length > 0) { + foreach ($nodes as $node) { + $abstractId = $xmlReader->getAttribute('w:abstractNumId', $node); + $abstracts[$abstractId] = array('levels' => array()); + $abstract = &$abstracts[$abstractId]; + $subnodes = $xmlReader->getElements('*', $node); + foreach ($subnodes as $subnode) { + switch ($subnode->nodeName) { + case 'w:multiLevelType': + $abstract['type'] = $xmlReader->getAttribute('w:val', $subnode); + break; + case 'w:lvl': + $levelId = $xmlReader->getAttribute('w:ilvl', $subnode); + $abstract['levels'][$levelId] = $this->readNumberingLevel($xmlReader, $subnode, $levelId); + break; + } + } + } + } + + // Numbering instance definition + $nodes = $xmlReader->getElements('w:num'); + if ($nodes->length > 0) { + foreach ($nodes as $node) { + $numId = $xmlReader->getAttribute('w:numId', $node); + $abstractId = $xmlReader->getAttribute('w:val', $node, 'w:abstractNumId'); + $numberings[$numId] = $abstracts[$abstractId]; + $numberings[$numId]['numId'] = $numId; + $subnodes = $xmlReader->getElements('w:lvlOverride/w:lvl', $node); + foreach ($subnodes as $subnode) { + $levelId = $xmlReader->getAttribute('w:ilvl', $subnode); + $overrides = $this->readNumberingLevel($xmlReader, $subnode, $levelId); + foreach ($overrides as $key => $value) { + $numberings[$numId]['levels'][$levelId][$key] = $value; + } + } + } + } + + // Push to Style collection + foreach ($numberings as $numId => $numbering) { + $this->phpWord->addNumberingStyle("PHPWordList{$numId}", $numbering); + } + } + + /** + * Read numbering level definition from w:abstractNum and w:num + * + * @param integer $levelId + * @return array + */ + private function readNumberingLevel(XMLReader $xmlReader, \DOMElement $subnode, $levelId) + { + $level = array(); + + $level['level'] = $levelId; + $level['start'] = $xmlReader->getAttribute('w:val', $subnode, 'w:start'); + $level['format'] = $xmlReader->getAttribute('w:val', $subnode, 'w:numFmt'); + $level['restart'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlRestart'); + $level['suffix'] = $xmlReader->getAttribute('w:val', $subnode, 'w:suff'); + $level['text'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlText'); + $level['align'] = $xmlReader->getAttribute('w:val', $subnode, 'w:lvlJc'); + $level['tab'] = $xmlReader->getAttribute('w:pos', $subnode, 'w:pPr/w:tabs/w:tab'); + $level['left'] = $xmlReader->getAttribute('w:left', $subnode, 'w:pPr/w:ind'); + $level['hanging'] = $xmlReader->getAttribute('w:hanging', $subnode, 'w:pPr/w:ind'); + $level['font'] = $xmlReader->getAttribute('w:ascii', $subnode, 'w:rPr/w:rFonts'); + $level['hint'] = $xmlReader->getAttribute('w:hint', $subnode, 'w:rPr/w:rFonts'); + + foreach ($level as $key => $value) { + if (is_null($value)) { + unset($level[$key]); + } + } + + return $level; + } + /** * Read header footer * - * @param string $filename * @param array $settings * @param \PhpOffice\PhpWord\Element\Section $section */ - private function readHeaderFooter($filename, $settings, &$section) + private function readHeaderFooter($settings, &$section) { if (is_array($settings) && array_key_exists('hf', $settings)) { foreach ($settings['hf'] as $rId => $hfSetting) { @@ -297,7 +411,7 @@ class Word2007 extends AbstractReader implements ReaderInterface // Read header/footer content $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { @@ -321,17 +435,17 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read (footnotes|endnotes).xml * - * @param string $filename * @param string $xmlFile + * @param string $notesType */ - private function readNotes($filename, $xmlFile, $notesType = 'footnotes') + private function readNotes($xmlFile, $notesType = 'footnotes') { $notesType = ($notesType == 'endnotes') ? 'endnotes' : 'footnotes'; $collectionClass = 'PhpOffice\\PhpWord\\' . ucfirst($notesType); $collection = $collectionClass::getElements(); $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $nodes = $xmlReader->getElements('*'); if ($nodes->length > 0) { foreach ($nodes as $node) { @@ -360,7 +474,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @todo Get font style for preserve text */ - private function readParagraph(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart) + private function readParagraph(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart) { // Paragraph style $pStyle = null; @@ -394,6 +508,17 @@ class Word2007 extends AbstractReader implements ReaderInterface } $parent->addPreserveText($textContent, $fStyle, $pStyle); + // List item + } elseif ($xmlReader->elementExists('w:pPr/w:numPr', $domNode)) { + $textContent = ''; + $numId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:numId'); + $levelId = $xmlReader->getAttribute('w:val', $domNode, 'w:pPr/w:numPr/w:ilvl'); + $nodes = $xmlReader->getElements('w:r', $domNode); + foreach ($nodes as $node) { + $textContent .= $xmlReader->getValue('w:t', $node); + } + $parent->addListItem($textContent, $levelId, null, "PHPWordList{$numId}", $pStyle); + // Text and TextRun } else { $runCount = $xmlReader->countElements('w:r', $domNode); @@ -419,13 +544,15 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Read w:r * + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader + * @param \DOMElement $domNode * @param mixed $parent * @param string $docPart * @param mixed $pStyle * * @todo Footnote paragraph style */ - private function readRun(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart, $pStyle = null) + private function readRun(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart, $pStyle = null) { if (!in_array($domNode->nodeName, array('w:r', 'w:hyperlink'))) { return; @@ -454,8 +581,8 @@ class Word2007 extends AbstractReader implements ReaderInterface $rId = $xmlReader->getAttribute('r:id', $domNode, 'w:pict/v:shape/v:imagedata'); $target = $this->getMediaTarget($docPart, $rId); if (!is_null($target)) { - $textContent = ""; - $parent->addText($textContent, $fStyle, $pStyle); + $imageSource = "zip://{$this->filename}#{$target}"; + $parent->addImage($imageSource); } // Object @@ -482,7 +609,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * @param mixed $parent * @param string $docPart */ - private function readTable(XMLReader $xmlReader, \DOMNode $domNode, &$parent, $docPart) + private function readTable(XMLReader $xmlReader, \DOMElement $domNode, &$parent, $docPart) { // Table style $tblStyle = null; @@ -515,8 +642,9 @@ class Word2007 extends AbstractReader implements ReaderInterface } elseif ($rowNode->nodeName == 'w:tc') { // Cell $cellWidth = $xmlReader->getAttribute('w:w', $rowNode, 'w:tcPr/w:tcW'); $cellStyle = null; - if ($xmlReader->elementExists('w:tcPr', $rowNode)) { - $cellStyle = $this->readCellStyle($xmlReader, $xmlReader->getElement('w:tcPr', $rowNode)); + $cellStyleNode = $xmlReader->getElement('w:tcPr', $rowNode); + if (!is_null($cellStyleNode)) { + $cellStyle = $this->readCellStyle($xmlReader, $cellStyleNode); } $cell = $row->addCell($cellWidth, $cellStyle); @@ -537,7 +665,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return array|null */ - private function readSectionStyle(XMLReader $xmlReader, \DOMNode $domNode) + private function readSectionStyle(XMLReader $xmlReader, \DOMElement $domNode) { $ret = null; $mapping = array( @@ -552,6 +680,7 @@ class Word2007 extends AbstractReader implements ReaderInterface } $property = $mapping[$node->nodeName]; switch ($node->nodeName) { + case 'w:type': $ret['breakType'] = $xmlReader->getAttribute('w:val', $node); break; @@ -596,7 +725,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return string|array|null */ - private function readParagraphStyle(XMLReader $xmlReader, \DOMNode $domNode) + private function readParagraphStyle(XMLReader $xmlReader, \DOMElement $domNode) { $style = null; if ($xmlReader->elementExists('w:pPr', $domNode)) { @@ -618,6 +747,7 @@ class Word2007 extends AbstractReader implements ReaderInterface } $property = $mapping[$node->nodeName]; switch ($node->nodeName) { + case 'w:ind': $style['indent'] = $xmlReader->getAttribute('w:left', $node); $style['hanging'] = $xmlReader->getAttribute('w:hanging', $node); @@ -658,13 +788,16 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return string|array|null */ - private function readFontStyle(XMLReader $xmlReader, \DOMNode $domNode) + private function readFontStyle(XMLReader $xmlReader, \DOMElement $domNode) { $style = null; // Hyperlink has an extra w:r child if ($domNode->nodeName == 'w:hyperlink') { $domNode = $xmlReader->getElement('w:r', $domNode); } + if (is_null($domNode)) { + return $style; + } if ($xmlReader->elementExists('w:rPr', $domNode)) { if ($xmlReader->elementExists('w:rPr/w:rStyle', $domNode)) { $style = $xmlReader->getAttribute('w:val', $domNode, 'w:rPr/w:rStyle'); @@ -684,6 +817,7 @@ class Word2007 extends AbstractReader implements ReaderInterface } $property = $mapping[$node->nodeName]; switch ($node->nodeName) { + case 'w:rFonts': $style['name'] = $xmlReader->getAttribute('w:ascii', $node); $style['hint'] = $xmlReader->getAttribute('w:hint', $node); @@ -728,7 +862,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * @return string|array|null * @todo Capture w:tblStylePr w:type="firstRow" */ - private function readTableStyle(XMLReader $xmlReader, \DOMNode $domNode) + private function readTableStyle(XMLReader $xmlReader, \DOMElement $domNode) { $style = null; $margins = array('top', 'left', 'bottom', 'right'); @@ -751,6 +885,7 @@ class Word2007 extends AbstractReader implements ReaderInterface } // $property = $mapping[$node->nodeName]; switch ($node->nodeName) { + case 'w:tblCellMar': foreach ($margins as $side) { $ucfSide = ucfirst($side); @@ -778,7 +913,7 @@ class Word2007 extends AbstractReader implements ReaderInterface * * @return array|null */ - private function readCellStyle(XMLReader $xmlReader, \DOMNode $domNode) + private function readCellStyle(XMLReader $xmlReader, \DOMElement $domNode) { $style = null; $mapping = array( @@ -809,12 +944,11 @@ class Word2007 extends AbstractReader implements ReaderInterface /** * Get relationship array * - * @param string $filename * @param string $xmlFile * @param string $targetPrefix * @return array */ - private function getRels($filename, $xmlFile, $targetPrefix = '') + private function getRels($xmlFile, $targetPrefix = '') { $metaPrefix = 'http://schemas.openxmlformats.org/package/2006/relationships/metadata/'; $officePrefix = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/'; @@ -822,7 +956,7 @@ class Word2007 extends AbstractReader implements ReaderInterface $rels = array(); $xmlReader = new XMLReader(); - $xmlReader->getDomFromZip($filename, $xmlFile); + $xmlReader->getDomFromZip($this->filename, $xmlFile); $nodes = $xmlReader->getElements('*'); foreach ($nodes as $node) { $rId = $xmlReader->getAttribute('Id', $node); diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php index 0af6cf72..4558736b 100644 --- a/src/PhpWord/Settings.php +++ b/src/PhpWord/Settings.php @@ -14,10 +14,13 @@ namespace PhpOffice\PhpWord; */ class Settings { - /** Available Zip library classes */ + /** Available Zip library classes */ const PCLZIP = 'PhpOffice\\PhpWord\\Shared\\ZipArchive'; const ZIPARCHIVE = 'ZipArchive'; + /** Optional PDF Rendering libraries */ + const PDF_RENDERER_DOMPDF = 'DomPDF'; + /** * Compatibility option for XMLWriter * @@ -27,13 +30,32 @@ class Settings /** * Name of the class used for Zip file management - * e.g. - * ZipArchive * * @var string */ 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 * @@ -74,7 +96,7 @@ class Settings return true; } return false; - } // function setZipClass() + } /** * 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() { 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; + } } diff --git a/src/PhpWord/Shared/String.php b/src/PhpWord/Shared/String.php index e603f034..95f75f13 100644 --- a/src/PhpWord/Shared/String.php +++ b/src/PhpWord/Shared/String.php @@ -77,6 +77,23 @@ class String return $value; } + /** + * Return name without underscore for < 0.10.0 variable name compatibility + * + * @param string $value + * @return string + */ + public static function removeUnderscorePrefix($value) + { + if (!is_null($value)) { + if (substr($value, 0, 1) == '_') { + $value = substr($value, 1); + } + } + + return $value; + } + /** * Build control characters array */ diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php index e5b5facc..b7f5549e 100644 --- a/src/PhpWord/Shared/XMLReader.php +++ b/src/PhpWord/Shared/XMLReader.php @@ -15,7 +15,7 @@ use PhpOffice\PhpWord\Settings; /** * XML Reader wrapper * - * @since 0.9.2 + * @since 0.10.0 */ class XMLReader { @@ -70,7 +70,7 @@ class XMLReader * @param string $path * @return \DOMNodeList */ - public function getElements($path, \DOMNode $contextNode = null) + public function getElements($path, \DOMElement $contextNode = null) { if ($this->dom === null) { return array(); @@ -86,9 +86,9 @@ class XMLReader * Get element * * @param string $path - * @return \DOMNode|null + * @return \DOMElement|null */ - public function getElement($path, \DOMNode $contextNode) + public function getElement($path, \DOMElement $contextNode) { $elements = $this->getElements($path, $contextNode); if ($elements->length > 0) { @@ -105,7 +105,7 @@ class XMLReader * @param string $path * @return string|null */ - public function getAttribute($attribute, \DOMNode $contextNode, $path = null) + public function getAttribute($attribute, \DOMElement $contextNode, $path = null) { if (is_null($path)) { $return = $contextNode->getAttribute($attribute); @@ -127,7 +127,7 @@ class XMLReader * @param string $path * @return string|null */ - public function getValue($path, \DOMNode $contextNode) + public function getValue($path, \DOMElement $contextNode) { $elements = $this->getElements($path, $contextNode); if ($elements->length > 0) { @@ -143,7 +143,7 @@ class XMLReader * @param string $path * @return integer */ - public function countElements($path, \DOMNode $contextNode) + public function countElements($path, \DOMElement $contextNode) { $elements = $this->getElements($path, $contextNode); @@ -156,7 +156,7 @@ class XMLReader * @param string $path * @return boolean */ - public function elementExists($path, \DOMNode $contextNode) + public function elementExists($path, \DOMElement $contextNode) { return $this->getElements($path, $contextNode)->length > 0; } diff --git a/src/PhpWord/Shared/ZipArchive.php b/src/PhpWord/Shared/ZipArchive.php index 74f742b6..5485ea14 100644 --- a/src/PhpWord/Shared/ZipArchive.php +++ b/src/PhpWord/Shared/ZipArchive.php @@ -22,7 +22,7 @@ require_once 'PCLZip/pclzip.lib.php'; /** * PCLZip wrapper * - * @since 0.9.2 + * @since 0.10.0 */ class ZipArchive { @@ -217,8 +217,7 @@ class ZipArchive public function getNameIndex($index) { $list = $this->zip->listContent(); - $listCount = count($list); - if ($index <= $listCount) { + if (isset($list[$index])) { return $list[$index]['filename']; } else { return false; diff --git a/src/PhpWord/Style.php b/src/PhpWord/Style.php index 5afbb91b..e0b5d439 100755 --- a/src/PhpWord/Style.php +++ b/src/PhpWord/Style.php @@ -12,6 +12,7 @@ namespace PhpOffice\PhpWord; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\Style\Table; +use PhpOffice\PhpWord\Style\Numbering; /** * Style collection @@ -33,7 +34,7 @@ class Style */ public static function addParagraphStyle($styleName, $styles) { - self::setStyleValues($styleName, $styles, new Paragraph()); + self::setStyleValues($styleName, new Paragraph(), $styles); } /** @@ -45,7 +46,7 @@ class Style */ public static function addFontStyle($styleName, $styleFont, $styleParagraph = null) { - self::setStyleValues($styleName, $styleFont, new Font('text', $styleParagraph)); + self::setStyleValues($styleName, new Font('text', $styleParagraph), $styleFont); } /** @@ -56,7 +57,7 @@ class Style */ public static function addLinkStyle($styleName, $styles) { - self::setStyleValues($styleName, $styles, new Font('link')); + self::setStyleValues($styleName, new Font('link'), $styles); } /** @@ -64,15 +65,11 @@ class Style * * @param string $styleName * @param array $styleTable - * @param array $styleFirstRow + * @param array|null $styleFirstRow */ public static function addTableStyle($styleName, $styleTable, $styleFirstRow = null) { - if (!array_key_exists($styleName, self::$styles)) { - $style = new Table($styleTable, $styleFirstRow); - - self::$styles[$styleName] = $style; - } + self::setStyleValues($styleName, new Table($styleTable, $styleFirstRow), null); } /** @@ -84,12 +81,36 @@ class Style */ public static function addTitleStyle($titleCount, $styleFont, $styleParagraph = null) { - $styleName = 'Heading_' . $titleCount; - self::setStyleValues("Heading_{$titleCount}", $styleFont, new Font('title', $styleParagraph)); + self::setStyleValues("Heading_{$titleCount}", new Font('title', $styleParagraph), $styleFont); + } + + /** + * Add numbering style + * + * @param string $styleName + * @param array $styleValues + * @return Numbering + * @since 0.10.0 + */ + public static function addNumberingStyle($styleName, $styleValues) + { + self::setStyleValues($styleName, new Numbering(), $styleValues); + } + + /** + * Count styles + * + * @return integer + * @since 0.10.0 + */ + public static function countStyles() + { + return count(self::$styles); } /** * Reset styles + * @since 0.10.0 */ public static function resetStyles() { @@ -120,6 +141,7 @@ class Style * Get style by name * * @param string $styleName + * @return Paragraph|Font|Table|Numbering|null */ public static function getStyle($styleName) { @@ -131,24 +153,21 @@ class Style } /** - * Set style values + * Set style values and put it to static style collection * * @param string $styleName - * @param array $styleValues - * @param mixed $styleObject + * @param Paragraph|Font|Table|Numbering $styleObject + * @param array|null $styleValues */ - private static function setStyleValues($styleName, $styleValues, $styleObject) + private static function setStyleValues($styleName, $styleObject, $styleValues = null) { if (!array_key_exists($styleName, self::$styles)) { - if (is_array($styleValues)) { + if (!is_null($styleValues) && is_array($styleValues)) { foreach ($styleValues as $key => $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } $styleObject->setStyleValue($key, $value); } } - + $styleObject->setIndex(self::countStyles() + 1); // One based index self::$styles[$styleName] = $styleObject; } } diff --git a/src/PhpWord/Style/AbstractStyle.php b/src/PhpWord/Style/AbstractStyle.php index c17ea6e2..ea77dc70 100644 --- a/src/PhpWord/Style/AbstractStyle.php +++ b/src/PhpWord/Style/AbstractStyle.php @@ -9,35 +9,144 @@ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Shared\String; + /** * Abstract style class * - * @since 0.9.2 + * @since 0.10.0 */ abstract class AbstractStyle { + /** + * Index number in Style collection for named style + * + * This number starts from one and defined in Style::setStyleValues() + * + * @var integer|null + */ + protected $index; + + /** + * Get index number + * + * @return integer|null + */ + public function getIndex() + { + return $this->index; + } + + /** + * Set index number + * + * @param integer|null $value + */ + public function setIndex($value = null) + { + $this->index = $this->setIntVal($value, $this->index); + + return $this; + } + /** * Set style value template method * - * Some child classes have their own specific overrides + * Some child classes have their own specific overrides. + * Backward compability check for versions < 0.10.0 which use underscore + * prefix for their private properties. + * Check if the set method is exists. Throws an exception? * * @param string $key * @param string $value - * - * @todo Implement type check mechanism, e.g. boolean, integer, enum, defaults + * @return self */ public function setStyleValue($key, $value) { - // Backward compability check for versions < 0.9.2 which use underscore - // prefix for their private properties - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } - - // Check if the set method is exists. Throws an exception? - $method = 'set' . $key; + $method = 'set' . String::removeUnderscorePrefix($key); if (method_exists($this, $method)) { $this->$method($value); } + + return $this; + } + + /** + * Set style by using associative array + * + * @param array $styles + * @return self + */ + public function setStyleByArray($styles = array()) + { + foreach ($styles as $key => $value) { + $this->setStyleValue($key, $value); + } + + return $this; + } + + /** + * Set boolean value + * + * @param mixed $value + * @param boolean|null $default + * @return boolean|null + */ + protected function setBoolVal($value, $default = null) + { + if (!is_bool($value)) { + $value = $default; + } + + return $value; + } + + /** + * Set integer value + * + * @param mixed $value + * @param integer|null $default + * @return integer|null + */ + protected function setIntVal($value, $default = null) + { + if (!is_int($value)) { + $value = $default; + } + + return $value; + } + + /** + * Set float value + * + * @param mixed $value + * @param float|null $default + * @return float|null + */ + protected function setFloatVal($value, $default = null) + { + if (!is_float($value)) { + $value = $default; + } + + return $value; + } + + /** + * Set enum value + * + * @param mixed $value + * @param array $enum + * @param mixed $default + */ + protected function setEnumVal($value, $enum, $default = null) + { + if (!in_array($value, $enum)) { + $value = $default; + } + + return $value; } } diff --git a/src/PhpWord/Style/Cell.php b/src/PhpWord/Style/Cell.php index 2e1f9d79..64a7f35a 100644 --- a/src/PhpWord/Style/Cell.php +++ b/src/PhpWord/Style/Cell.php @@ -9,6 +9,8 @@ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Shared\String; + /** * Table cell style */ @@ -145,9 +147,7 @@ class Cell extends AbstractStyle */ public function setStyleValue($key, $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } + $key = String::removeUnderscorePrefix($key); if ($key == 'borderSize') { $this->setBorderSize($value); } elseif ($key == 'borderColor') { diff --git a/src/PhpWord/Style/Font.php b/src/PhpWord/Style/Font.php index 2fef3a66..dd8ff02a 100644 --- a/src/PhpWord/Style/Font.php +++ b/src/PhpWord/Style/Font.php @@ -193,8 +193,6 @@ class Font extends AbstractStyle if ($key === 'line-height') { $this->setLineHeight($value); null; - } elseif (substr($key, 0, 1) == '_') { - $key = substr($key, 1); } $this->setStyleValue($key, $value); } diff --git a/src/PhpWord/Style/ListItem.php b/src/PhpWord/Style/ListItem.php index ecc4274c..a4f4933d 100644 --- a/src/PhpWord/Style/ListItem.php +++ b/src/PhpWord/Style/ListItem.php @@ -9,46 +9,239 @@ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Style; + /** * List item style + * + * Before version 0.10.0, numbering style is defined statically with $listType. + * After version 0.10.0, numbering style is defined by using Numbering and + * recorded by $numStyle. $listStyle is maintained for backward compatility */ class ListItem extends AbstractStyle { + const TYPE_SQUARE_FILLED = 1; + const TYPE_BULLET_FILLED = 3; // default + const TYPE_BULLET_EMPTY = 5; const TYPE_NUMBER = 7; const TYPE_NUMBER_NESTED = 8; const TYPE_ALPHANUM = 9; - const TYPE_BULLET_FILLED = 3; - const TYPE_BULLET_EMPTY = 5; - const TYPE_SQUARE_FILLED = 1; /** - * List Type + * Legacy list type + * + * @var integer */ private $listType; /** - * Create a new ListItem Style + * Numbering style name + * + * @var string + * @since 0.10.0 */ - public function __construct() - { - $this->listType = self::TYPE_BULLET_FILLED; - } + private $numStyle; /** - * Set List Type + * Numbering definition instance ID * - * @param int $pValue + * @var integer + * @since 0.10.0 */ - public function setListType($pValue = self::TYPE_BULLET_FILLED) + private $numId; + + /** + * Create new instance + * + * @param string $numStyle + */ + public function __construct($numStyle = null) { - $this->listType = $pValue; + if (!is_null($numStyle)) { + $this->setNumStyle($numStyle); + } else { + $this->setListType(); + } } /** * Get List Type + * + * @return integer */ public function getListType() { return $this->listType; } + + /** + * Set legacy list type for version < 0.10.0 + * + * @param integer $value + */ + public function setListType($value = self::TYPE_BULLET_FILLED) + { + $enum = array(self::TYPE_SQUARE_FILLED, self::TYPE_BULLET_FILLED, + self::TYPE_BULLET_EMPTY, self::TYPE_NUMBER, + self::TYPE_NUMBER_NESTED, self::TYPE_ALPHANUM); + $this->listType = $this->setEnumVal($value, $enum, $this->listType); + $this->getListTypeStyle(); + } + + /** + * Get numbering style name + * + * @return string + */ + public function getNumStyle() + { + return $this->numStyle; + } + + /** + * Set numbering style name + * + * @param string $value + */ + public function setNumStyle($value) + { + $this->numStyle = $value; + $numStyleObject = Style::getStyle($this->numStyle); + if ($numStyleObject instanceof Numbering) { + $this->numId = $numStyleObject->getIndex(); + $numStyleObject->setNumId($this->numId); + } + } + + /** + * Get numbering Id + * + * @return integer + */ + public function getNumId() + { + return $this->numId; + } + + /** + * Get legacy numbering definition + * + * @return array + * @since 0.10.0 + */ + private function getListTypeStyle() + { + // Check if legacy style already registered in global Style collection + $numStyle = "PHPWordList{$this->listType}"; + if (!is_null(Style::getStyle($numStyle))) { + $this->setNumStyle($numStyle); + return; + } + + // Property mapping for numbering level information + $properties = array('start', 'format', 'text', 'align', 'tabPos', 'left', 'hanging', 'font', 'hint'); + + // Legacy level information + $listTypeStyles = array( + self::TYPE_SQUARE_FILLED => array( + 'type' => 'hybridMultilevel', + 'levels' => array( + 0 => '1, bullet, , left, 720, 720, 360, Wingdings, default', + 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', + 2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default', + 3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default', + 4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default', + 5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default', + 6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default', + 7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default', + 8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default', + ), + ), + self::TYPE_BULLET_FILLED => array( + 'type' => 'hybridMultilevel', + 'levels' => array( + 0 => '1, bullet, , left, 720, 720, 360, Symbol, default', + 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', + 2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default', + 3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default', + 4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default', + 5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default', + 6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default', + 7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default', + 8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default', + ), + ), + self::TYPE_BULLET_EMPTY => array( + 'type' => 'hybridMultilevel', + 'levels' => array( + 0 => '1, bullet, o, left, 720, 720, 360, Courier New, default', + 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', + 2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default', + 3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default', + 4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default', + 5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default', + 6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default', + 7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default', + 8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default', + ), + ), + self::TYPE_NUMBER => array( + 'type' => 'hybridMultilevel', + 'levels' => array( + 0 => '1, decimal, %1., left, 720, 720, 360, , default', + 1 => '1, bullet, o, left, 1440, 1440, 360, Courier New, default', + 2 => '1, bullet, , left, 2160, 2160, 360, Wingdings, default', + 3 => '1, bullet, , left, 2880, 2880, 360, Symbol, default', + 4 => '1, bullet, o, left, 3600, 3600, 360, Courier New, default', + 5 => '1, bullet, , left, 4320, 4320, 360, Wingdings, default', + 6 => '1, bullet, , left, 5040, 5040, 360, Symbol, default', + 7 => '1, bullet, o, left, 5760, 5760, 360, Courier New, default', + 8 => '1, bullet, , left, 6480, 6480, 360, Wingdings, default', + ), + ), + self::TYPE_NUMBER_NESTED => array( + 'type' => 'multilevel', + 'levels' => array( + 0 => '1, decimal, %1., left, 360, 360, 360, , ', + 1 => '1, decimal, %1.%2., left, 792, 792, 432, , ', + 2 => '1, decimal, %1.%2.%3., left, 1224, 1224, 504, , ', + 3 => '1, decimal, %1.%2.%3.%4., left, 1800, 1728, 648, , ', + 4 => '1, decimal, %1.%2.%3.%4.%5., left, 2520, 2232, 792, , ', + 5 => '1, decimal, %1.%2.%3.%4.%5.%6., left, 2880, 2736, 936, , ', + 6 => '1, decimal, %1.%2.%3.%4.%5.%6.%7., left, 3600, 3240, 1080, , ', + 7 => '1, decimal, %1.%2.%3.%4.%5.%6.%7.%8., left, 3960, 3744, 1224, , ', + 8 => '1, decimal, %1.%2.%3.%4.%5.%6.%7.%8.%9., left, 4680, 4320, 1440, , ', + ), + ), + self::TYPE_ALPHANUM => array( + 'type' => 'multilevel', + 'levels' => array( + 0 => '1, decimal, %1., left, 720, 720, 360, , ', + 1 => '1, lowerLetter, %2., left, 1440, 1440, 360, , ', + 2 => '1, lowerRoman, %3., right, 2160, 2160, 180, , ', + 3 => '1, decimal, %4., left, 2880, 2880, 360, , ', + 4 => '1, lowerLetter, %5., left, 3600, 3600, 360, , ', + 5 => '1, lowerRoman, %6., right, 4320, 4320, 180, , ', + 6 => '1, decimal, %7., left, 5040, 5040, 360, , ', + 7 => '1, lowerLetter, %8., left, 5760, 5760, 360, , ', + 8 => '1, lowerRoman, %9., right, 6480, 6480, 180, , ', + ), + ), + ); + + // Populate style and register to global Style register + $style = $listTypeStyles[$this->listType]; + foreach ($style['levels'] as $key => $value) { + $level = array(); + $levelProperties = explode(', ', $value); + $level['level'] = $key; + for ($i = 0; $i < count($properties); $i++) { + $property = $properties[$i]; + $level[$property] = $levelProperties[$i]; + } + $style['levels'][$key] = $level; + } + Style::addNumberingStyle($numStyle, $style); + $this->setNumStyle($numStyle); + } } diff --git a/src/PhpWord/Style/Numbering.php b/src/PhpWord/Style/Numbering.php new file mode 100644 index 00000000..8c2e4a69 --- /dev/null +++ b/src/PhpWord/Style/Numbering.php @@ -0,0 +1,123 @@ +numId; + } + + /** + * Set Id + * + * @param integer $value + * @return self + */ + public function setNumId($value) + { + $this->numId = $this->setIntVal($value, $this->numId); + return $this; + } + + /** + * Get multilevel type + * + * @return string + */ + public function getType() + { + return $this->type; + } + + /** + * Set multilevel type + * + * @param string $value + * @return self + */ + public function setType($value) + { + $enum = array('singleLevel', 'multilevel', 'hybridMultilevel'); + $this->type = $this->setEnumVal($value, $enum, $this->type); + return $this; + } + + /** + * Get levels + * + * @return NumberingLevel[] + */ + public function getLevels() + { + return $this->levels; + } + + /** + * Set multilevel type + * + * @param array $values + * @return self + */ + public function setLevels($values) + { + if (is_array($values)) { + foreach ($values as $key => $value) { + $numberingLevel = new NumberingLevel(); + if (is_array($value)) { + $numberingLevel->setStyleByArray($value); + $numberingLevel->setLevel($key); + } + $this->levels[$key] = $numberingLevel; + } + } + + return $this; + } +} diff --git a/src/PhpWord/Style/NumberingLevel.php b/src/PhpWord/Style/NumberingLevel.php new file mode 100644 index 00000000..dff7de22 --- /dev/null +++ b/src/PhpWord/Style/NumberingLevel.php @@ -0,0 +1,378 @@ +level; + } + + /** + * Set level + * + * @param integer $value + * @return self + */ + public function setLevel($value) + { + $this->level = $this->setIntVal($value, $this->level); + return $this; + } + + /** + * Get start + * + * @return integer + */ + public function getStart() + { + return $this->start; + } + + /** + * Set start + * + * @param integer $value + * @return self + */ + public function setStart($value) + { + $this->start = $this->setIntVal($value, $this->start); + return $this; + } + + /** + * Get format + * + * @return string + */ + public function getFormat() + { + return $this->format; + } + + /** + * Set format + * + * @param string $value + * @return self + */ + public function setFormat($value) + { + $enum = array('bullet', 'decimal', 'upperRoman', 'lowerRoman', 'upperLetter', 'lowerLetter'); + $this->format = $this->setEnumVal($value, $enum, $this->format); + return $this; + } + + /** + * Get start + * + * @return integer + */ + public function getRestart() + { + return $this->restart; + } + + /** + * Set start + * + * @param integer $value + * @return self + */ + public function setRestart($value) + { + $this->restart = $this->setIntVal($value, $this->restart); + return $this; + } + + /** + * Get suffix + * + * @return string + */ + public function getSuffix() + { + return $this->suffix; + } + + /** + * Set suffix + * + * @param string $value + * @return self + */ + public function setSuffix($value) + { + $enum = array('tab', 'space', 'nothing'); + $this->suffix = $this->setEnumVal($value, $enum, $this->suffix); + return $this; + } + + /** + * Get text + * + * @return string + */ + public function getText() + { + return $this->text; + } + + /** + * Set text + * + * @param string $value + * @return self + */ + public function setText($value) + { + $this->text = $value; + return $this; + } + + /** + * Get align + * + * @return string + */ + public function getAlign() + { + return $this->align; + } + + /** + * Set align + * + * @param string $value + * @return self + */ + public function setAlign($value) + { + $enum = array('left', 'center', 'right', 'both'); + $this->align = $this->setEnumVal($value, $enum, $this->align); + return $this; + } + + /** + * Get left + * + * @return integer + */ + public function getLeft() + { + return $this->left; + } + + /** + * Set left + * + * @param integer $value + * @return self + */ + public function setLeft($value) + { + $this->left = $this->setIntVal($value, $this->left); + return $this; + } + + /** + * Get hanging + * + * @return integer + */ + public function getHanging() + { + return $this->hanging; + } + + /** + * Set hanging + * + * @param integer $value + * @return self + */ + public function setHanging($value) + { + $this->hanging = $this->setIntVal($value, $this->hanging); + return $this; + } + + /** + * Get tab + * + * @return integer + */ + public function getTabPos() + { + return $this->tabPos; + } + + /** + * Set tab + * + * @param integer $value + * @return self + */ + public function setTabPos($value) + { + $this->tabPos = $this->setIntVal($value, $this->tabPos); + return $this; + } + + /** + * Get font + * + * @return string + */ + public function getFont() + { + return $this->font; + } + + /** + * Set font + * + * @param string $value + * @return self + */ + public function setFont($value) + { + $this->font = $value; + return $this; + } + + /** + * Get hint + * + * @return string + */ + public function getHint() + { + return $this->hint; + } + + /** + * Set hint + * + * @param string $value + * @return self + */ + public function setHint($value) + { + $enum = array('default', 'eastAsia', 'cs'); + $this->hint = $this->setEnumVal($value, $enum, $this->hint); + return $this; + } +} diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index 8feb7924..57ad4405 100755 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -10,6 +10,7 @@ namespace PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Exception\InvalidStyleException; +use PhpOffice\PhpWord\Shared\String; /** * Paragraph style @@ -127,8 +128,6 @@ class Paragraph extends AbstractStyle foreach ($style as $key => $value) { if ($key === 'line-height') { null; - } elseif (substr($key, 0, 1) == '_') { - $key = substr($key, 1); } $this->setStyleValue($key, $value); } @@ -144,9 +143,7 @@ class Paragraph extends AbstractStyle */ public function setStyleValue($key, $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } + $key = String::removeUnderscorePrefix($key); if ($key == 'indent' || $key == 'hanging') { $value = $value * 720; } elseif ($key == 'spacing') { diff --git a/src/PhpWord/Style/Row.php b/src/PhpWord/Style/Row.php index d4dc642f..698adea4 100644 --- a/src/PhpWord/Style/Row.php +++ b/src/PhpWord/Style/Row.php @@ -45,16 +45,12 @@ class Row extends AbstractStyle /** * Set tblHeader * - * @param boolean $pValue - * @return $this + * @param boolean $value + * @return self */ - public function setTblHeader($pValue = false) + public function setTblHeader($value = false) { - if (!is_bool($pValue)) { - $pValue = false; - } - $this->tblHeader = $pValue; - return $this; + $this->tblHeader = $this->setBoolVal($value, $this->tblHeader); } /** @@ -70,16 +66,12 @@ class Row extends AbstractStyle /** * Set cantSplit * - * @param boolean $pValue - * @return $this + * @param boolean $value + * @return self */ - public function setCantSplit($pValue = false) + public function setCantSplit($value = false) { - if (!is_bool($pValue)) { - $pValue = false; - } - $this->cantSplit = $pValue; - return $this; + $this->cantSplit = $this->setBoolVal($value, $this->cantSplit); } /** @@ -95,15 +87,12 @@ class Row extends AbstractStyle /** * Set exactHeight * - * @param bool $pValue - * @return $this + * @param bool $value + * @return self */ - public function setExactHeight($pValue = false) + public function setExactHeight($value = false) { - if (!is_bool($pValue)) { - $pValue = false; - } - $this->exactHeight = $pValue; + $this->exactHeight = $this->setBoolVal($value, $this->exactHeight); return $this; } diff --git a/src/PhpWord/Style/Section.php b/src/PhpWord/Style/Section.php index 6005e3ca..717fdaf7 100644 --- a/src/PhpWord/Style/Section.php +++ b/src/PhpWord/Style/Section.php @@ -9,6 +9,8 @@ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Shared\String; + /** * Section settings */ @@ -217,9 +219,7 @@ class Section extends AbstractStyle */ public function setSettingValue($key, $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } + $key = String::removeUnderscorePrefix($key); if ($key == 'orientation' && $value == 'landscape') { $this->setLandscape(); } elseif ($key == 'orientation' && is_null($value)) { diff --git a/src/PhpWord/Style/TOC.php b/src/PhpWord/Style/TOC.php index f7a752a9..e8a781b0 100644 --- a/src/PhpWord/Style/TOC.php +++ b/src/PhpWord/Style/TOC.php @@ -110,15 +110,4 @@ class TOC extends AbstractStyle { $this->indent = $pValue; } - - /** - * Set style value - * - * @param string $key - * @param string $value - */ - public function setStyleValue($key, $value) - { - $this->$key = $value; - } } diff --git a/src/PhpWord/Style/Table.php b/src/PhpWord/Style/Table.php index f7c98c36..000ecff7 100755 --- a/src/PhpWord/Style/Table.php +++ b/src/PhpWord/Style/Table.php @@ -9,6 +9,8 @@ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Shared\String; + /** * Table style */ @@ -161,18 +163,12 @@ class Table extends AbstractStyle unset($this->firstRow->borderInsideHColor); unset($this->firstRow->borderInsideHSize); foreach ($styleFirstRow as $key => $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } $this->firstRow->setStyleValue($key, $value); } } if (!is_null($styleTable) && is_array($styleTable)) { foreach ($styleTable as $key => $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } $this->setStyleValue($key, $value); } } @@ -186,9 +182,7 @@ class Table extends AbstractStyle */ public function setStyleValue($key, $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } + $key = String::removeUnderscorePrefix($key); if ($key == 'borderSize') { $this->setBorderSize($value); } elseif ($key == 'borderColor') { diff --git a/src/PhpWord/TOC.php b/src/PhpWord/TOC.php index 708592c6..2513d921 100644 --- a/src/PhpWord/TOC.php +++ b/src/PhpWord/TOC.php @@ -82,9 +82,6 @@ class TOC if (!is_null($styleTOC) && is_array($styleTOC)) { foreach ($styleTOC as $key => $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } self::$TOCStyle->setStyleValue($key, $value); } } @@ -93,9 +90,6 @@ class TOC if (is_array($styleFont)) { self::$fontStyle = new Font(); foreach ($styleFont as $key => $value) { - if (substr($key, 0, 1) == '_') { - $key = substr($key, 1); - } self::$fontStyle->setStyleValue($key, $value); } } else { diff --git a/src/PhpWord/Template.php b/src/PhpWord/Template.php index 856c6546..bcc6edf3 100644 --- a/src/PhpWord/Template.php +++ b/src/PhpWord/Template.php @@ -420,17 +420,4 @@ class Template } return substr($this->documentXML, $startPosition, ($endPosition - $startPosition)); } - - /** - * Delete a block of text - * - * @param string $blockname - * @param string $replacement - * @deprecated - * @codeCoverageIgnore - */ - public function deleteTemplateBlock($blockname, $replacement = '') - { - $this->deleteBlock($blockname, $replacement); - } } diff --git a/src/PhpWord/Writer/AbstractWriter.php b/src/PhpWord/Writer/AbstractWriter.php index 553ab671..8e5a23d1 100644 --- a/src/PhpWord/Writer/AbstractWriter.php +++ b/src/PhpWord/Writer/AbstractWriter.php @@ -16,7 +16,7 @@ use PhpOffice\PhpWord\Settings; /** * Abstract writer class * - * @since 0.9.2 + * @since 0.10.0 */ abstract class AbstractWriter implements WriterInterface { @@ -48,6 +48,13 @@ abstract class AbstractWriter implements WriterInterface */ private $diskCachingDirectory = './'; + /** + * Temporary directory + * + * @var string + */ + private $tempDir = ''; + /** * Original file name * @@ -81,7 +88,7 @@ abstract class AbstractWriter implements WriterInterface * Set PhpWord object * * @param \PhpOffice\PhpWord\PhpWord - * @return $this + * @return self */ public function setPhpWord(PhpWord $phpWord = null) { @@ -119,7 +126,7 @@ abstract class AbstractWriter implements WriterInterface * * @param boolean $pValue * @param string $pDirectory - * @return $this + * @return self */ public function setUseDiskCaching($pValue = false, $pDirectory = null) { @@ -146,6 +153,32 @@ abstract class AbstractWriter implements WriterInterface return $this->diskCachingDirectory; } + /** + * Get temporary directory + * + * @return string + */ + public function getTempDir() + { + return $this->tempDir; + } + + /** + * Set temporary directory + * + * @param string $value + * @return self + */ + public function setTempDir($value) + { + if (!is_dir($value)) { + mkdir($value); + } + $this->tempDir = $value; + + return $this; + } + /** * Get temporary file name * @@ -156,6 +189,10 @@ abstract class AbstractWriter implements WriterInterface */ protected function getTempFile($filename) { + // Temporary directory + $this->setTempDir(sys_get_temp_dir() . '/PHPWordWriter/'); + + // Temporary file $this->originalFilename = $filename; if (strtolower($filename) == 'php://output' || strtolower($filename) == 'php://stdout') { $filename = @tempnam(sys_get_temp_dir(), 'phpword_'); @@ -170,8 +207,6 @@ abstract class AbstractWriter implements WriterInterface /** * Cleanup temporary file - * - * If a temporary file was used, copy it to the correct file stream */ protected function cleanupTempFile() { @@ -181,6 +216,18 @@ abstract class AbstractWriter implements WriterInterface } @unlink($this->tempFilename); } + + $this->clearTempDir(); + } + + /** + * Clear temporary directory + */ + protected function clearTempDir() + { + if (is_dir($this->tempDir)) { + $this->deleteDir($this->tempDir); + } } /** @@ -215,4 +262,24 @@ abstract class AbstractWriter implements WriterInterface return $objZip; } + + /** + * Delete directory + * + * @param string $dir + */ + private function deleteDir($dir) + { + foreach (scandir($dir) as $file) { + if ($file === '.' || $file === '..') { + continue; + } elseif (is_file($dir . "/" . $file)) { + unlink($dir . "/" . $file); + } elseif (is_dir($dir . "/" . $file)) { + $this->deleteDir($dir . "/" . $file); + } + } + + rmdir($dir); + } } diff --git a/src/PhpWord/Writer/HTML.php b/src/PhpWord/Writer/HTML.php new file mode 100644 index 00000000..bd98e893 --- /dev/null +++ b/src/PhpWord/Writer/HTML.php @@ -0,0 +1,746 @@ +setPhpWord($phpWord); + } + + /** + * Save PhpWord to file + * + * @param string $filename + * @throws Exception + */ + public function save($filename = null) + { + if (!is_null($this->getPhpWord())) { + $this->setTempDir(sys_get_temp_dir() . '/PHPWordWriter/'); + $hFile = fopen($filename, 'w') or die("can't open file"); + fwrite($hFile, $this->writeDocument()); + fclose($hFile); + $this->clearTempDir(); + } else { + throw new Exception("No PHPWord assigned."); + } + } + + /** + * Get phpWord data + * + * @return string + */ + public function writeDocument() + { + $html = ''; + $html .= '' . PHP_EOL; + $html .= '' . PHP_EOL; + $html .= '' . PHP_EOL; + $html .= '' . PHP_EOL; + $html .= $this->writeHTMLHead(); + $html .= '' . PHP_EOL; + $html .= '' . PHP_EOL; + $html .= $this->writeHTMLBody(); + $html .= $this->writeNotes(); + $html .= '' . PHP_EOL; + $html .= '' . PHP_EOL; + + return $html; + } + + /** + * Generate HTML header + * + * @return string + */ + private function writeHTMLHead() + { + $properties = $this->getPhpWord()->getDocumentProperties(); + $propertiesMapping = array( + 'creator' => 'author', + 'title' => '', + 'description' => '', + 'subject' => '', + 'keywords' => '', + 'category' => '', + 'company' => '', + 'manager' => '' + ); + $title = $properties->getTitle(); + $title = ($title != '') ? $title : 'PHPWord'; + + $html = ''; + $html .= '' . PHP_EOL; + $html .= '' . htmlspecialchars($title) . '' . PHP_EOL; + foreach ($propertiesMapping as $key => $value) { + $value = ($value == '') ? $key : $value; + $method = "get" . $key; + if ($properties->$method() != '') { + $html .= '' . PHP_EOL; + } + } + $html .= $this->writeStyles(); + + return $html; + } + + /** + * Get content + * + * @return string + */ + private function writeHTMLBody() + { + $phpWord = $this->getPhpWord(); + $html = ''; + + $sections = $phpWord->getSections(); + $countSections = count($sections); + $pSection = 0; + + if ($countSections > 0) { + foreach ($sections as $section) { + $pSection++; + $contents = $section->getElements(); + foreach ($contents as $element) { + 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) { + $html .= $this->writeEndnote($element); + } elseif ($element instanceof Footnote) { + $html .= $this->writeFootnote($element); + } + } + } + } + + return $html; + } + + /** + * Write footnote/endnote contents + */ + private function writeNotes() + { + $footnote = Footnotes::getElements(); + $endnote = Endnotes::getElements(); + $html = ''; + + if (count($this->notes) > 0) { + $html .= "
    "; + 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 = "{$noteId}" . $this->writeTextRun($element, true); + $html .= "

    {$content}

    " . PHP_EOL; + } + } + } + + return $html; + } + + /** + * Get text + * + * @param Text $text + * @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 .= 'writeParagraphStyle($paragraphStyle) . '"'; + } + $html .= '>'; + } + if ($fontStyle) { + $html .= 'writeFontStyle($fontStyle) . '"'; + } + $html .= '>'; + } + $html .= htmlspecialchars($text->getText()); + if ($fontStyle) { + $html .= ''; + } + if ($paragraphStyle && !$withoutP) { + $html .= '

    ' . PHP_EOL; + } + + return $html; + } + + /** + * Write text run content + * + * @param TextRun|Footnote|Endnote $textrun + * @return string + */ + private function writeTextRun($textrun, $withoutP = false) + { + $html = ''; + $elements = $textrun->getElements(); + if (count($elements) > 0) { + $paragraphStyle = $textrun->getParagraphStyle(); + $spIsObject = ($paragraphStyle instanceof Paragraph); + + $html .= $withoutP ? '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) { + $html .= $this->writeEndnote($element); + } elseif ($element instanceof Footnote) { + $html .= $this->writeFootnote($element); + } + } + $html .= $withoutP ? '' : '

    '; + $html .= PHP_EOL; + } + + return $html; + } + + /** + * Write link + * + * @param Link $element + * @param boolean $withoutP + * @return string + */ + private function writeLink($element, $withoutP = false) + { + $url = $element->getLinkSrc(); + $text = $element->getLinkName(); + if ($text == '') { + $text = $url; + } + $html = ''; + if (!$withoutP) { + $html .= "

    " . PHP_EOL; + } + $html .= "{$text}" . PHP_EOL; + if (!$withoutP) { + $html .= "

    " . PHP_EOL; + } + + return $html; + } + + /** + * Write heading + * + * @param Title $element + * @return string + */ + private function writeTitle($element) + { + $tag = 'h' . $element->getDepth(); + $text = htmlspecialchars($element->getText()); + $html = "<{$tag}>{$text}" . PHP_EOL; + + return $html; + } + + /** + * Write preserve text + * + * @param PreserveText $element + * @param boolean $withoutP + * @return string + */ + private function writePreserveText($element, $withoutP = false) + { + return $this->writeUnsupportedElement($element, $withoutP); + } + + /** + * Get text break + * + * @param TextBreak $element + * @param boolean $withoutP + * @return string + */ + private function writeTextBreak($element, $withoutP = false) + { + if ($withoutP) { + $html = '
    ' . PHP_EOL; + } else { + $html = '

     

    ' . PHP_EOL; + } + + return $html; + } + + /** + * Write page break + * + * @param PageBreak $element + * @return string + */ + private function writePageBreak($element) + { + return $this->writeUnsupportedElement($element, false); + } + + /** + * Write list item + * + * @param ListItem $element + * @return string + */ + private function writeListItem($element) + { + $text = htmlspecialchars($element->getTextObject()->getText()); + $html = '

    ' . $text . '' . PHP_EOL; + + return $html; + } + + /** + * Write table + * + * @param Table $element + * @return string + */ + private function writeTable($element) + { + $html = ''; + $rows = $element->getRows(); + $cRows = count($rows); + if ($cRows > 0) { + $html .= "" . PHP_EOL; + foreach ($rows as $row) { + // $height = $row->getHeight(); + $rowStyle = $row->getStyle(); + $tblHeader = $rowStyle->getTblHeader(); + $html .= "" . 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) { + $html .= $this->writeEndnote($element); + } elseif ($element instanceof Footnote) { + $html .= $this->writeFootnote($element); + } + } + } else { + $html .= $this->writeTextBreak(new TextBreak()); + } + $html .= "" . PHP_EOL; + } + $html .= "" . PHP_EOL; + } + $html .= "
    " . PHP_EOL; + } + + return $html; + } + + /** + * Write image + * + * @param Image $element + * @param boolean $withoutP + * @return string + */ + private function writeImage($element, $withoutP = false) + { + $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 = ""; + if (!$withoutP) { + $html = "

    {$html}

    " . PHP_EOL; + } + } + } + + return $html; + } + + /** + * Write object + * + * @param Object $element + * @param boolean $withoutP + * @return string + */ + private function writeObject($element, $withoutP = false) + { + return $this->writeUnsupportedElement($element, $withoutP); + } + + /** + * Write footnote + * + * @param Footnote $element + * @return string + */ + private function writeFootnote($element) + { + return $this->writeNote($element); + } + + /** + * Write endnote + * + * @param Endnote $element + * @return string + */ + private function writeEndnote($element) + { + return $this->writeNote($element); + } + + /** + * Write footnote/endnote marks + * + * @param Footnote|Endnote $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 = "{$index}"; + + return $html; + } + + /** + * 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 = "{$elementMark}" . PHP_EOL; + } else { + $html = "

    {$elementMark}

    " . PHP_EOL; + } + + return $html; + } + + /** + * Get styles + * + * @return string + */ + private function writeStyles() + { + $css = '' . PHP_EOL; + + return $css; + } + + /** + * Get font style + * + * @param Font $style + * @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 Paragraph $style + * @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; + } + + /** + * Get Base64 image data + * + * @return string|null + */ + private function getBase64ImageData(Image $element) + { + $imageData = null; + $imageBinary = null; + $source = $element->getSource(); + $imageType = $element->getImageType(); + + // Get actual source from archive image + if ($element->getSourceType() == Image::SOURCE_ARCHIVE) { + $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)); + $imageData = 'data:' . $imageType . ';base64,' . $base64; + } + + return $imageData; + } +} diff --git a/src/PhpWord/Writer/ODText/Base.php b/src/PhpWord/Writer/ODText/Base.php index a1f0d315..24eab60c 100644 --- a/src/PhpWord/Writer/ODText/Base.php +++ b/src/PhpWord/Writer/ODText/Base.php @@ -17,7 +17,7 @@ use PhpOffice\PhpWord\Shared\XMLWriter; /** * ODT base part writer * - * @since 0.9.2 + * @since 0.10.0 */ class Base extends AbstractWriterPart { diff --git a/src/PhpWord/Writer/ODText/Content.php b/src/PhpWord/Writer/ODText/Content.php index 8823c28e..c618b042 100644 --- a/src/PhpWord/Writer/ODText/Content.php +++ b/src/PhpWord/Writer/ODText/Content.php @@ -24,7 +24,6 @@ use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; use PhpOffice\PhpWord\Style; -use PhpOffice\PhpWord\TOC; /** * ODText content part writer @@ -95,10 +94,311 @@ class Content extends Base } } - // office:font-face-decls - $this->writeFontFaces($xmlWriter); - // office:automatic-styles + $this->writeFontFaces($xmlWriter); // office:font-face-decls + + $this->writeAutomaticStyles($xmlWriter); // office:automatic-styles + + // Tables + $sections = $phpWord->getSections(); + $countSections = count($sections); + if ($countSections > 0) { + $sectionId = 0; + foreach ($sections as $section) { + $sectionId++; + $elements = $section->getElements(); + foreach ($elements as $element) { + if ($elements instanceof Table) { + $xmlWriter->startElement('style:style'); + $xmlWriter->writeAttribute('style:name', $element->getElementId()); + $xmlWriter->writeAttribute('style:family', 'table'); + $xmlWriter->startElement('style:table-properties'); + //$xmlWriter->writeAttribute('style:width', 'table'); + $xmlWriter->writeAttribute('style:rel-width', 100); + $xmlWriter->writeAttribute('table:align', 'center'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + } + } + } + } + + $xmlWriter->endElement(); + + // office:body + $xmlWriter->startElement('office:body'); + // office:text + $xmlWriter->startElement('office:text'); + // text:sequence-decls + $xmlWriter->startElement('text:sequence-decls'); + // text:sequence-decl + $xmlWriter->startElement('text:sequence-decl'); + $xmlWriter->writeAttribute('text:display-outline-level', 0); + $xmlWriter->writeAttribute('text:name', 'Illustration'); + $xmlWriter->endElement(); + // text:sequence-decl + $xmlWriter->startElement('text:sequence-decl'); + $xmlWriter->writeAttribute('text:display-outline-level', 0); + $xmlWriter->writeAttribute('text:name', 'Table'); + $xmlWriter->endElement(); + // text:sequence-decl + $xmlWriter->startElement('text:sequence-decl'); + $xmlWriter->writeAttribute('text:display-outline-level', 0); + $xmlWriter->writeAttribute('text:name', 'Text'); + $xmlWriter->endElement(); + // text:sequence-decl + $xmlWriter->startElement('text:sequence-decl'); + $xmlWriter->writeAttribute('text:display-outline-level', 0); + $xmlWriter->writeAttribute('text:name', 'Drawing'); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + $sections = $phpWord->getSections(); + $countSections = count($sections); + if ($countSections > 0) { + foreach ($sections as $section) { + $elements = $section->getElements(); + + foreach ($elements as $element) { + if ($element instanceof Text) { + $this->writeText($xmlWriter, $element); + } elseif ($element instanceof TextRun) { + $this->writeTextRun($xmlWriter, $element); + } elseif ($element instanceof Link) { + $this->writeLink($xmlWriter, $element); + } elseif ($element instanceof Title) { + $this->writeTitle($xmlWriter, $element); + } elseif ($element instanceof ListItem) { + $this->writeListItem($xmlWriter, $element); + } elseif ($element instanceof TextBreak) { + $this->writeTextBreak($xmlWriter); + } elseif ($element instanceof PageBreak) { + $this->writePageBreak($xmlWriter); + } elseif ($element instanceof Table) { + $this->writeTable($xmlWriter, $element); + } elseif ($element instanceof Image) { + $this->writeImage($xmlWriter, $element); + } elseif ($element instanceof Object) { + $this->writeObject($xmlWriter, $element); + } + } + } + } + $xmlWriter->endElement(); + $xmlWriter->endElement(); + $xmlWriter->endElement(); + + // Return + return $xmlWriter->getData(); + } + + /** + * Write text + * + * @param XMLWriter $xmlWriter + * @param Text $text + * @param bool $withoutP + */ + protected function writeText(XMLWriter $xmlWriter, Text $text, $withoutP = false) + { + $styleFont = $text->getFontStyle(); + $styleParagraph = $text->getParagraphStyle(); + + // @todo Commented for TextRun. Should really checkout this value + // $SfIsObject = ($styleFont instanceof Font) ? true : false; + $SfIsObject = false; + + if ($SfIsObject) { + // Don't never be the case, because I browse all sections for cleaning all styles not declared + die('PhpWord : $SfIsObject wouldn\'t be an object'); + } else { + if (!$withoutP) { + $xmlWriter->startElement('text:p'); // text:p + } + if (empty($styleFont)) { + if (empty($styleParagraph)) { + $xmlWriter->writeAttribute('text:style-name', 'P1'); + } elseif (is_string($styleParagraph)) { + $xmlWriter->writeAttribute('text:style-name', $styleParagraph); + } + $xmlWriter->writeRaw($text->getText()); + } else { + if (empty($styleParagraph)) { + $xmlWriter->writeAttribute('text:style-name', 'Standard'); + } elseif (is_string($styleParagraph)) { + $xmlWriter->writeAttribute('text:style-name', $styleParagraph); + } + // text:span + $xmlWriter->startElement('text:span'); + if (is_string($styleFont)) { + $xmlWriter->writeAttribute('text:style-name', $styleFont); + } + $xmlWriter->writeRaw($text->getText()); + $xmlWriter->endElement(); + } + if (!$withoutP) { + $xmlWriter->endElement(); // text:p + } + } + } + + /** + * Write TextRun section + * + * @param XMLWriter $xmlWriter + * @param TextRun $textrun + * @todo Enable all other section types + */ + protected function writeTextRun(XMLWriter $xmlWriter, TextRun $textrun) + { + $elements = $textrun->getElements(); + $xmlWriter->startElement('text:p'); + if (count($elements) > 0) { + foreach ($elements as $element) { + if ($element instanceof Text) { + $this->writeText($xmlWriter, $element, true); + } + } + } + $xmlWriter->endElement(); + } + + /** + * Write link element + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + */ + protected function writeLink(XMLWriter $xmlWriter, Link $link) + { + $this->writeUnsupportedElement($xmlWriter, 'Link'); + } + + /** + * Write title element + */ + protected function writeTitle(XMLWriter $xmlWriter, Title $title) + { + $this->writeUnsupportedElement($xmlWriter, 'Title'); + } + + /** + * Write preserve text + */ + protected function writePreserveText(XMLWriter $xmlWriter, PreserveText $preservetext) + { + $this->writeUnsupportedElement($xmlWriter, 'PreserveText'); + } + + /** + * Write list item + */ + protected function writeListItem(XMLWriter $xmlWriter, ListItem $listItem) + { + $this->writeUnsupportedElement($xmlWriter, 'ListItem'); + } + + /** + * Write text break + */ + protected function writeTextBreak(XMLWriter $xmlWriter) + { + $xmlWriter->startElement('text:p'); + $xmlWriter->writeAttribute('text:style-name', 'Standard'); + $xmlWriter->endElement(); + } + + /** + * Write page break + */ + protected function writePageBreak(XMLWriter $xmlWriter) + { + $this->writeUnsupportedElement($xmlWriter, 'PageBreak'); + } + + /** + * Write table + */ + protected function writeTable(XMLWriter $xmlWriter, Table $table) + { + $rows = $table->getRows(); + $rowCount = count($rows); + $colCount = $table->countColumns(); + if ($rowCount > 0) { + $xmlWriter->startElement('table:table'); + $xmlWriter->writeAttribute('table:name', $table->getElementId()); + $xmlWriter->writeAttribute('table:style', $table->getElementId()); + + $xmlWriter->startElement('table:table-column'); + $xmlWriter->writeAttribute('table:number-columns-repeated', $colCount); + $xmlWriter->endElement(); // table:table-column + + foreach ($rows as $row) { + $xmlWriter->startElement('table:table-row'); + foreach ($row->getCells() as $cell) { + $xmlWriter->startElement('table:table-cell'); + $xmlWriter->writeAttribute('office:value-type', 'string'); + $elements = $cell->getElements(); + if (count($elements) > 0) { + foreach ($elements as $element) { + if ($element instanceof Text) { + $this->writeText($xmlWriter, $element); + } elseif ($element instanceof TextRun) { + $this->writeTextRun($xmlWriter, $element); + } elseif ($element instanceof ListItem) { + $this->writeListItem($xmlWriter, $element); + } elseif ($element instanceof TextBreak) { + $this->writeTextBreak($xmlWriter); + } elseif ($element instanceof Image) { + $this->writeImage($xmlWriter, $element); + } elseif ($element instanceof Object) { + $this->writeObject($xmlWriter, $element); + } + } + } else { + $this->writeTextBreak($xmlWriter); + } + $xmlWriter->endElement(); // table:table-cell + } + $xmlWriter->endElement(); // table:table-row + } + $xmlWriter->endElement(); // table:table + } + } + + /** + * Write image + */ + protected function writeImage(XMLWriter $xmlWriter, Image $element) + { + $this->writeUnsupportedElement($xmlWriter, 'Image'); + } + + /** + * Write object + */ + protected function writeObject(XMLWriter $xmlWriter, Object $element) + { + $this->writeUnsupportedElement($xmlWriter, 'Object'); + } + + /** + * Write unsupported element + * + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param string $element + */ + private function writeUnsupportedElement(XMLWriter $xmlWriter, $element) + { + $xmlWriter->startElement('text:p'); + $xmlWriter->writeRaw($element); + $xmlWriter->endElement(); + } + + /** + * Write automatic styles + */ + private function writeAutomaticStyles(XMLWriter $xmlWriter) + { $xmlWriter->startElement('office:automatic-styles'); $styles = Style::getStyles(); $numPStyles = 0; @@ -151,172 +451,5 @@ class Content extends Base $xmlWriter->endElement(); } } - $xmlWriter->endElement(); - - // office:body - $xmlWriter->startElement('office:body'); - // office:text - $xmlWriter->startElement('office:text'); - // text:sequence-decls - $xmlWriter->startElement('text:sequence-decls'); - // text:sequence-decl - $xmlWriter->startElement('text:sequence-decl'); - $xmlWriter->writeAttribute('text:display-outline-level', 0); - $xmlWriter->writeAttribute('text:name', 'Illustration'); - $xmlWriter->endElement(); - // text:sequence-decl - $xmlWriter->startElement('text:sequence-decl'); - $xmlWriter->writeAttribute('text:display-outline-level', 0); - $xmlWriter->writeAttribute('text:name', 'Table'); - $xmlWriter->endElement(); - // text:sequence-decl - $xmlWriter->startElement('text:sequence-decl'); - $xmlWriter->writeAttribute('text:display-outline-level', 0); - $xmlWriter->writeAttribute('text:name', 'Text'); - $xmlWriter->endElement(); - // text:sequence-decl - $xmlWriter->startElement('text:sequence-decl'); - $xmlWriter->writeAttribute('text:display-outline-level', 0); - $xmlWriter->writeAttribute('text:name', 'Drawing'); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - $sections = $phpWord->getSections(); - $countSections = count($sections); - if ($countSections > 0) { - foreach ($sections as $section) { - $elements = $section->getElements(); - - foreach ($elements as $element) { - if ($element instanceof Text) { - $this->writeText($xmlWriter, $element); - } elseif ($element instanceof TextRun) { - $this->writeTextRun($xmlWriter, $element); - } elseif ($element instanceof TextBreak) { - $this->writeTextBreak($xmlWriter); - } elseif ($element instanceof Link) { - $this->writeUnsupportedElement($xmlWriter, 'Link'); - } elseif ($element instanceof Title) { - $this->writeUnsupportedElement($xmlWriter, 'Title'); - } elseif ($element instanceof PageBreak) { - $this->writeUnsupportedElement($xmlWriter, 'Page Break'); - } elseif ($element instanceof Table) { - $this->writeUnsupportedElement($xmlWriter, 'Table'); - } elseif ($element instanceof ListItem) { - $this->writeUnsupportedElement($xmlWriter, 'List Item'); - } elseif ($element instanceof Image) { - $this->writeUnsupportedElement($xmlWriter, 'Image'); - } elseif ($element instanceof Object) { - $this->writeUnsupportedElement($xmlWriter, 'Object'); - } elseif ($element instanceof TOC) { - $this->writeUnsupportedElement($xmlWriter, 'TOC'); - } else { - $this->writeUnsupportedElement($xmlWriter, 'Element'); - } - } - } - } - $xmlWriter->endElement(); - $xmlWriter->endElement(); - $xmlWriter->endElement(); - - // Return - return $xmlWriter->getData(); - } - - /** - * Write text - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param \PhpOffice\PhpWord\Element\Text $text - * @param bool $withoutP - */ - protected function writeText(XMLWriter $xmlWriter, Text $text, $withoutP = false) - { - $styleFont = $text->getFontStyle(); - $styleParagraph = $text->getParagraphStyle(); - - // @todo Commented for TextRun. Should really checkout this value - // $SfIsObject = ($styleFont instanceof Font) ? true : false; - $SfIsObject = false; - - if ($SfIsObject) { - // Don't never be the case, because I browse all sections for cleaning all styles not declared - die('PhpWord : $SfIsObject wouldn\'t be an object'); - } else { - if (!$withoutP) { - $xmlWriter->startElement('text:p'); // text:p - } - if (empty($styleFont)) { - if (empty($styleParagraph)) { - $xmlWriter->writeAttribute('text:style-name', 'P1'); - } elseif (is_string($styleParagraph)) { - $xmlWriter->writeAttribute('text:style-name', $styleParagraph); - } - $xmlWriter->writeRaw($text->getText()); - } else { - if (empty($styleParagraph)) { - $xmlWriter->writeAttribute('text:style-name', 'Standard'); - } elseif (is_string($styleParagraph)) { - $xmlWriter->writeAttribute('text:style-name', $styleParagraph); - } - // text:span - $xmlWriter->startElement('text:span'); - if (is_string($styleFont)) { - $xmlWriter->writeAttribute('text:style-name', $styleFont); - } - $xmlWriter->writeRaw($text->getText()); - $xmlWriter->endElement(); - } - if (!$withoutP) { - $xmlWriter->endElement(); // text:p - } - } - } - - /** - * Write TextRun section - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param \PhpOffice\PhpWord\Element\TextRun $textrun - * @todo Enable all other section types - */ - protected function writeTextRun(XMLWriter $xmlWriter, TextRun $textrun) - { - $elements = $textrun->getElements(); - $xmlWriter->startElement('text:p'); - if (count($elements) > 0) { - foreach ($elements as $element) { - if ($element instanceof Text) { - $this->writeText($xmlWriter, $element, true); - } - } - } - $xmlWriter->endElement(); - } - - /** - * Write TextBreak - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - */ - protected function writeTextBreak(XMLWriter $xmlWriter) - { - $xmlWriter->startElement('text:p'); - $xmlWriter->writeAttribute('text:style-name', 'Standard'); - $xmlWriter->endElement(); - } - - /** - * Write unsupported element - * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter - * @param string $element - */ - private function writeUnsupportedElement($xmlWriter, $element) - { - $xmlWriter->startElement('text:p'); - $xmlWriter->writeRaw($element); - $xmlWriter->endElement(); } } diff --git a/src/PhpWord/Writer/PDF.php b/src/PhpWord/Writer/PDF.php new file mode 100644 index 00000000..7421e3e4 --- /dev/null +++ b/src/PhpWord/Writer/PDF.php @@ -0,0 +1,66 @@ +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); + } +} diff --git a/src/PhpWord/Writer/PDF/AbstractRenderer.php b/src/PhpWord/Writer/PDF/AbstractRenderer.php new file mode 100644 index 00000000..64f196e9 --- /dev/null +++ b/src/PhpWord/Writer/PDF/AbstractRenderer.php @@ -0,0 +1,170 @@ + 'A4', // (210 mm by 297 mm) + ); + + /** + * Create new instance + * + * @param PhpWord $phpWord PhpWord object + */ + public function __construct(PhpWord $phpWord) + { + parent::__construct($phpWord); + } + + /** + * 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; + } + + /** + * Save PhpWord to PDF file, pre-save + * + * @param string $pFilename Name of the file to save as + * @return resource + */ + protected function prepareForSave($pFilename = null) + { + $fileHandle = fopen($pFilename, 'w'); + if ($fileHandle === false) { + throw new Exception("Could not open file $pFilename for writing."); + } + $this->isPdf = true; + + return $fileHandle; + } + + /** + * Save PhpWord to PDF file, post-save + * + * @param resource $fileHandle + * @throws Exception + */ + protected function restoreStateAfterSave($fileHandle) + { + fclose($fileHandle); + } +} diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php new file mode 100644 index 00000000..d4c7b5f8 --- /dev/null +++ b/src/PhpWord/Writer/PDF/DomPDF.php @@ -0,0 +1,70 @@ +set_paper(strtolower($paperSize), $orientation); + + $pdf->load_html($this->writeDocument()); + $pdf->render(); + + // Write to file + fwrite($fileHandle, $pdf->output()); + + parent::restoreStateAfterSave($fileHandle); + } +} diff --git a/src/PhpWord/Writer/Word2007.php b/src/PhpWord/Writer/Word2007.php index 0b13c53d..424bb305 100755 --- a/src/PhpWord/Writer/Word2007.php +++ b/src/PhpWord/Writer/Word2007.php @@ -9,17 +9,18 @@ namespace PhpOffice\PhpWord\Writer; -use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Exception\Exception; -use PhpOffice\PhpWord\Media; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Media; +use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Writer\Word2007\ContentTypes; +use PhpOffice\PhpWord\Writer\Word2007\Rels; use PhpOffice\PhpWord\Writer\Word2007\DocProps; use PhpOffice\PhpWord\Writer\Word2007\Document; use PhpOffice\PhpWord\Writer\Word2007\Footer; -use PhpOffice\PhpWord\Writer\Word2007\Header; use PhpOffice\PhpWord\Writer\Word2007\Notes; -use PhpOffice\PhpWord\Writer\Word2007\Rels; +use PhpOffice\PhpWord\Writer\Word2007\Numbering; +use PhpOffice\PhpWord\Writer\Word2007\Header; use PhpOffice\PhpWord\Writer\Word2007\Styles; /** @@ -57,6 +58,7 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->writerParts['docprops'] = new DocProps(); $this->writerParts['document'] = new Document(); $this->writerParts['styles'] = new Styles(); + $this->writerParts['numbering'] = new Numbering(); $this->writerParts['header'] = new Header(); $this->writerParts['footer'] = new Footer(); $this->writerParts['footnotes'] = new Notes(); @@ -97,7 +99,6 @@ class Word2007 extends AbstractWriter implements WriterInterface $this->addHeaderFooterMedia($objZip, 'footer'); // Add header/footer contents - $overrides = array(); $rId = Media::countElements('section') + 6; // @see Rels::writeDocRels for 6 first elements $sections = $this->phpWord->getSections(); foreach ($sections as $section) { @@ -116,9 +117,9 @@ class Word2007 extends AbstractWriter implements WriterInterface $objZip->addFromString('word/_rels/document.xml.rels', $this->getWriterPart('rels')->writeDocRels($this->docRels)); $objZip->addFromString('word/document.xml', $this->getWriterPart('document')->writeDocument($this->phpWord)); $objZip->addFromString('word/styles.xml', $this->getWriterPart('styles')->writeStyles($this->phpWord)); + $objZip->addFromString('word/numbering.xml', $this->getWriterPart('numbering')->writeNumbering()); // Write static files - $objZip->addFile(__DIR__ . '/../_staticDocParts/numbering.xml', 'word/numbering.xml'); $objZip->addFile(__DIR__ . '/../_staticDocParts/settings.xml', 'word/settings.xml'); $objZip->addFile(__DIR__ . '/../_staticDocParts/theme1.xml', 'word/theme/theme1.xml'); $objZip->addFile(__DIR__ . '/../_staticDocParts/webSettings.xml', 'word/webSettings.xml'); diff --git a/src/PhpWord/Writer/Word2007/Base.php b/src/PhpWord/Writer/Word2007/Base.php index bc157461..897830ff 100644 --- a/src/PhpWord/Writer/Word2007/Base.php +++ b/src/PhpWord/Writer/Word2007/Base.php @@ -279,7 +279,7 @@ class Base extends AbstractWriterPart { $textObject = $listItem->getTextObject(); $depth = $listItem->getDepth(); - $listType = $listItem->getStyle()->getListType(); + $numId = $listItem->getStyle()->getNumId(); $styleParagraph = $textObject->getParagraphStyle(); $xmlWriter->startElement('w:p'); @@ -290,7 +290,7 @@ class Base extends AbstractWriterPart $xmlWriter->writeAttribute('w:val', $depth); $xmlWriter->endElement(); // w:ilvl $xmlWriter->startElement('w:numId'); - $xmlWriter->writeAttribute('w:val', $listType); + $xmlWriter->writeAttribute('w:val', $numId); $xmlWriter->endElement(); // w:numId $xmlWriter->endElement(); // w:numPr $xmlWriter->endElement(); // w:pPr @@ -427,25 +427,22 @@ class Base extends AbstractWriterPart $marginTop = $style->getMarginTop(); $marginLeft = $style->getMarginLeft(); $wrappingStyle = $style->getWrappingStyle(); + $w10wrapType = null; if (!$withoutP) { $xmlWriter->startElement('w:p'); - if (!is_null($align)) { $xmlWriter->startElement('w:pPr'); $xmlWriter->startElement('w:jc'); $xmlWriter->writeAttribute('w:val', $align); - $xmlWriter->endElement(); - $xmlWriter->endElement(); + $xmlWriter->endElement(); // w:jc + $xmlWriter->endElement(); // w:pPr } } $xmlWriter->startElement('w:r'); - $xmlWriter->startElement('w:pict'); - $xmlWriter->startElement('v:shape'); $xmlWriter->writeAttribute('type', '#_x0000_t75'); - $imgStyle = ''; if (null !== $width) { $imgStyle .= 'width:' . $width . 'px;'; @@ -459,33 +456,38 @@ class Base extends AbstractWriterPart if (null !== $marginLeft) { $imgStyle .= 'margin-left:' . $marginLeft . 'in;'; } - switch ($wrappingStyle) { case ImageStyle::WRAPPING_STYLE_BEHIND: $imgStyle .= 'position:absolute;z-index:-251658752;'; break; - case ImageStyle::WRAPPING_STYLE_SQUARE: + case ImageStyle::WRAPPING_STYLE_INFRONT: $imgStyle .= 'position:absolute;z-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; break; - case ImageStyle::WRAPPING_STYLE_TIGHT: - $imgStyle .= 'position:absolute;z-index:251659264;mso-wrap-edited:f;mso-position-horizontal:absolute;mso-position-vertical:absolute'; + case ImageStyle::WRAPPING_STYLE_SQUARE: + $imgStyle .= 'position:absolute;z-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; + $w10wrapType = 'square'; break; - case ImageStyle::WRAPPING_STYLE_INFRONT: - $imgStyle .= 'position:absolute;zz-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; + case ImageStyle::WRAPPING_STYLE_TIGHT: + $imgStyle .= 'position:absolute;z-index:251659264;mso-position-horizontal:absolute;mso-position-vertical:absolute;'; + $w10wrapType = 'tight'; break; } - $xmlWriter->writeAttribute('style', $imgStyle); $xmlWriter->startElement('v:imagedata'); $xmlWriter->writeAttribute('r:id', 'rId' . $rId); $xmlWriter->writeAttribute('o:title', ''); - $xmlWriter->endElement(); - $xmlWriter->endElement(); + $xmlWriter->endElement(); // v:imagedata - $xmlWriter->endElement(); + if (!is_null($w10wrapType)) { + $xmlWriter->startElement('w10:wrap'); + $xmlWriter->writeAttribute('type', $w10wrapType); + $xmlWriter->endElement(); // w10:wrap + } - $xmlWriter->endElement(); + $xmlWriter->endElement(); // v:shape + $xmlWriter->endElement(); // w:pict + $xmlWriter->endElement(); // w:r if (!$withoutP) { $xmlWriter->endElement(); // w:p diff --git a/src/PhpWord/Writer/Word2007/Notes.php b/src/PhpWord/Writer/Word2007/Notes.php index 9b36d302..a6077a78 100644 --- a/src/PhpWord/Writer/Word2007/Notes.php +++ b/src/PhpWord/Writer/Word2007/Notes.php @@ -9,8 +9,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007; -use PhpOffice\PhpWord\Element\Endnote; use PhpOffice\PhpWord\Element\Footnote; +use PhpOffice\PhpWord\Element\Endnote; use PhpOffice\PhpWord\Shared\XMLWriter; /** @@ -69,7 +69,7 @@ class Notes extends Base // Content foreach ($elements as $element) { if ($element instanceof Footnote || $element instanceof Endnote) { - $this->writeNote($xmlWriter, $element, null, $notesTypes); + $this->writeNote($xmlWriter, $element, $notesTypes); } } @@ -83,10 +83,9 @@ class Notes extends Base * * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Footnote|\PhpOffice\PhpWord\Element\Endnote $element - * @param boolean $withoutP - * @param string $notesTypes + * @param string $notesTypes */ - protected function writeNote(XMLWriter $xmlWriter, $element, $withoutP = false, $notesTypes = 'footnotes') + protected function writeNote(XMLWriter $xmlWriter, $element, $notesTypes = 'footnotes') { $isFootnote = ($notesTypes == 'footnotes'); $elementNode = $isFootnote ? 'w:footnote' : 'w:endnote'; diff --git a/src/PhpWord/Writer/Word2007/Numbering.php b/src/PhpWord/Writer/Word2007/Numbering.php new file mode 100644 index 00000000..da127891 --- /dev/null +++ b/src/PhpWord/Writer/Word2007/Numbering.php @@ -0,0 +1,177 @@ +getXmlWriter(); + + $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); + $xmlWriter->startElement('w:numbering'); + $xmlWriter->writeAttribute('xmlns:ve', 'http://schemas.openxmlformats.org/markup-compatibility/2006'); + $xmlWriter->writeAttribute('xmlns:o', 'urn:schemas-microsoft-com:office:office'); + $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $xmlWriter->writeAttribute('xmlns:m', 'http://schemas.openxmlformats.org/officeDocument/2006/math'); + $xmlWriter->writeAttribute('xmlns:v', 'urn:schemas-microsoft-com:vml'); + $xmlWriter->writeAttribute('xmlns:wp', 'http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing'); + $xmlWriter->writeAttribute('xmlns:w10', 'urn:schemas-microsoft-com:office:word'); + $xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'); + $xmlWriter->writeAttribute('xmlns:wne', 'http://schemas.microsoft.com/office/word/2006/wordml'); + + // Abstract numbering definitions + foreach ($styles as $style) { + if ($style instanceof NumberingStyle) { + $levels = $style->getLevels(); + + $xmlWriter->startElement('w:abstractNum'); + $xmlWriter->writeAttribute('w:abstractNumId', $style->getNumId()); + + $xmlWriter->startElement('w:nsid'); + $xmlWriter->writeAttribute('w:val', $this->getRandomHexNumber()); + $xmlWriter->endElement(); // w:nsid + + $xmlWriter->startElement('w:multiLevelType'); + $xmlWriter->writeAttribute('w:val', $style->getType()); + $xmlWriter->endElement(); // w:multiLevelType + + if (is_array($levels)) { + foreach ($levels as $levelNum => $levelObject) { + if ($levelObject instanceof NumberingLevel) { + $start = $levelObject->getStart(); + $format = $levelObject->getFormat(); + $restart = $levelObject->getRestart(); + $suffix = $levelObject->getSuffix(); + $text = $levelObject->getText(); + $align = $levelObject->getAlign(); + $tabPos = $levelObject->getTabPos(); + $left = $levelObject->getLeft(); + $hanging = $levelObject->getHanging(); + $font = $levelObject->getFont(); + $hint = $levelObject->getHint(); + + $xmlWriter->startElement('w:lvl'); + $xmlWriter->writeAttribute('w:ilvl', $levelNum); + + if (!is_null($start)) { + $xmlWriter->startElement('w:start'); + $xmlWriter->writeAttribute('w:val', $start); + $xmlWriter->endElement(); // w:start + } + if (!is_null($format)) { + $xmlWriter->startElement('w:numFmt'); + $xmlWriter->writeAttribute('w:val', $format); + $xmlWriter->endElement(); // w:numFmt + } + if (!is_null($restart)) { + $xmlWriter->startElement('w:lvlRestart'); + $xmlWriter->writeAttribute('w:val', $restart); + $xmlWriter->endElement(); // w:lvlRestart + } + if (!is_null($suffix)) { + $xmlWriter->startElement('w:suff'); + $xmlWriter->writeAttribute('w:val', $suffix); + $xmlWriter->endElement(); // w:suff + } + if (!is_null($text)) { + $xmlWriter->startElement('w:lvlText'); + $xmlWriter->writeAttribute('w:val', $text); + $xmlWriter->endElement(); // w:start + } + if (!is_null($align)) { + $xmlWriter->startElement('w:lvlJc'); + $xmlWriter->writeAttribute('w:val', $align); + $xmlWriter->endElement(); // w:lvlJc + } + if (!is_null($tabPos) || !is_null($left) || !is_null($hanging)) { + $xmlWriter->startElement('w:pPr'); + if (!is_null($tabPos)) { + $xmlWriter->startElement('w:tabs'); + $xmlWriter->startElement('w:tab'); + $xmlWriter->writeAttribute('w:val', 'num'); + $xmlWriter->writeAttribute('w:pos', $tabPos); + $xmlWriter->endElement(); // w:tab + $xmlWriter->endElement(); // w:tabs + } + if (!is_null($left) || !is_null($hanging)) { + $xmlWriter->startElement('w:ind'); + if (!is_null($left)) { + $xmlWriter->writeAttribute('w:left', $left); + } + if (!is_null($hanging)) { + $xmlWriter->writeAttribute('w:hanging', $hanging); + } + $xmlWriter->endElement(); // w:ind + } + $xmlWriter->endElement(); // w:pPr + } + if (!is_null($font) || !is_null($hint)) { + $xmlWriter->startElement('w:rPr'); + $xmlWriter->startElement('w:rFonts'); + if (!is_null($font)) { + $xmlWriter->writeAttribute('w:ascii', $font); + $xmlWriter->writeAttribute('w:hAnsi', $font); + $xmlWriter->writeAttribute('w:cs', $font); + } + if (!is_null($hint)) { + $xmlWriter->writeAttribute('w:hint', $hint); + } + $xmlWriter->endElement(); // w:rFonts + $xmlWriter->endElement(); // w:rPr + } + $xmlWriter->endElement(); // w:lvl + } + } + } + $xmlWriter->endElement(); // w:abstractNum + } + } + + // Numbering definition instances + foreach ($styles as $style) { + if ($style instanceof NumberingStyle) { + $xmlWriter->startElement('w:num'); + $xmlWriter->writeAttribute('w:numId', $style->getNumId()); + $xmlWriter->startElement('w:abstractNumId'); + $xmlWriter->writeAttribute('w:val', $style->getNumId()); + $xmlWriter->endElement(); // w:abstractNumId + $xmlWriter->endElement(); // w:num + } + } + + $xmlWriter->endElement(); + + return $xmlWriter->getData(); + } + + /** + * Get random hexadecimal number value + * + * @param int $length + * @return string + */ + private function getRandomHexNumber($length = 8) + { + return strtoupper(substr(md5(rand()), 0, $length)); + } +} diff --git a/src/PhpWord/Writer/Word2007/Rels.php b/src/PhpWord/Writer/Word2007/Rels.php index 168d02aa..dfd842fa 100755 --- a/src/PhpWord/Writer/Word2007/Rels.php +++ b/src/PhpWord/Writer/Word2007/Rels.php @@ -15,7 +15,7 @@ use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 relationship writer * - * @since 0.9.2 + * @since 0.10.0 */ class Rels extends AbstractWriterPart { diff --git a/src/PhpWord/Writer/Word2007/Styles.php b/src/PhpWord/Writer/Word2007/Styles.php index ec00fc18..73b115bf 100644 --- a/src/PhpWord/Writer/Word2007/Styles.php +++ b/src/PhpWord/Writer/Word2007/Styles.php @@ -18,6 +18,8 @@ use PhpOffice\PhpWord\Style; /** * Word2007 styles part writer + * + * @todo Do something with the numbering style introduced in 0.10.0 */ class Styles extends Base { @@ -38,37 +40,31 @@ class Styles extends Base // XML header $xmlWriter->startDocument('1.0', 'UTF-8', 'yes'); $xmlWriter->startElement('w:styles'); - $xmlWriter->writeAttribute( - 'xmlns:r', - 'http://schemas.openxmlformats.org/officeDocument/2006/relationships' - ); - $xmlWriter->writeAttribute( - 'xmlns:w', - 'http://schemas.openxmlformats.org/wordprocessingml/2006/main' - ); + $xmlWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'); + $xmlWriter->writeAttribute('xmlns:w', 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'); + // Write default styles $styles = Style::getStyles(); $this->writeDefaultStyles($xmlWriter, $phpWord, $styles); - // Write other styles + + // Write styles if (count($styles) > 0) { foreach ($styles as $styleName => $style) { if ($styleName == 'Normal') { continue; } - if ($style instanceof Font) { + // Font style + if ($style instanceof Font) { $paragraphStyle = $style->getParagraphStyle(); $styleType = $style->getStyleType(); - $type = ($styleType == 'title') ? 'paragraph' : 'character'; - if (!is_null($paragraphStyle)) { $type = 'paragraph'; } $xmlWriter->startElement('w:style'); $xmlWriter->writeAttribute('w:type', $type); - if ($styleType == 'title') { $arrStyle = explode('_', $styleName); $styleId = 'Heading' . $arrStyle[1]; @@ -80,11 +76,9 @@ class Styles extends Base $xmlWriter->writeAttribute('w:val', $styleLink); $xmlWriter->endElement(); } - $xmlWriter->startElement('w:name'); $xmlWriter->writeAttribute('w:val', $styleName); $xmlWriter->endElement(); - if (!is_null($paragraphStyle)) { // Point parent style to Normal $xmlWriter->startElement('w:basedOn'); @@ -94,19 +88,17 @@ class Styles extends Base } $this->writeFontStyle($xmlWriter, $style); - $xmlWriter->endElement(); + // Paragraph style } elseif ($style instanceof Paragraph) { $xmlWriter->startElement('w:style'); $xmlWriter->writeAttribute('w:type', 'paragraph'); $xmlWriter->writeAttribute('w:customStyle', '1'); $xmlWriter->writeAttribute('w:styleId', $styleName); - $xmlWriter->startElement('w:name'); $xmlWriter->writeAttribute('w:val', $styleName); $xmlWriter->endElement(); - // Parent style $basedOn = $style->getBasedOn(); if (!is_null($basedOn)) { @@ -114,7 +106,6 @@ class Styles extends Base $xmlWriter->writeAttribute('w:val', $basedOn); $xmlWriter->endElement(); } - // Next paragraph style $next = $style->getNext(); if (!is_null($next)) { @@ -126,22 +117,20 @@ class Styles extends Base $this->writeParagraphStyle($xmlWriter, $style); $xmlWriter->endElement(); + // Table style } elseif ($style instanceof Table) { $xmlWriter->startElement('w:style'); $xmlWriter->writeAttribute('w:type', 'table'); $xmlWriter->writeAttribute('w:customStyle', '1'); $xmlWriter->writeAttribute('w:styleId', $styleName); - $xmlWriter->startElement('w:name'); $xmlWriter->writeAttribute('w:val', $styleName); $xmlWriter->endElement(); - $xmlWriter->startElement('w:uiPriority'); $xmlWriter->writeAttribute('w:val', '99'); $xmlWriter->endElement(); $this->writeTableStyle($xmlWriter, $style); - $xmlWriter->endElement(); // w:style } } @@ -149,7 +138,6 @@ class Styles extends Base $xmlWriter->endElement(); // w:styles - // Return return $xmlWriter->getData(); } diff --git a/src/PhpWord/_staticDocParts/numbering.xml b/src/PhpWord/_staticDocParts/numbering.xml deleted file mode 100644 index ac229f40..00000000 --- a/src/PhpWord/_staticDocParts/numbering.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/tests/PhpWord/Tests/Element/AbstractElementTest.php b/tests/PhpWord/Tests/Element/AbstractElementTest.php new file mode 100644 index 00000000..8dbfb5b3 --- /dev/null +++ b/tests/PhpWord/Tests/Element/AbstractElementTest.php @@ -0,0 +1,39 @@ +getMockForAbstractClass('\PhpOffice\PhpWord\Element\AbstractElement'); + $ival = rand(0, 100); + $stub->setElementIndex($ival); + $this->assertEquals($stub->getElementIndex(), $ival); + } + + /** + * Test set/get element unique Id + */ + public function testElementId() + { + $stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Element\AbstractElement'); + $stub->setElementId(); + $this->assertEquals(strlen($stub->getElementId()), 6); + } +} diff --git a/tests/PhpWord/Tests/Element/ImageTest.php b/tests/PhpWord/Tests/Element/ImageTest.php index 5b9aaa45..eb482398 100644 --- a/tests/PhpWord/Tests/Element/ImageTest.php +++ b/tests/PhpWord/Tests/Element/ImageTest.php @@ -51,34 +51,29 @@ class ImageTest extends \PHPUnit_Framework_TestCase /** * Valid image types */ - public function testValidImageTypes() + public function testImages() { - new Image(__DIR__ . "/../_files/images/mars_noext_jpg"); - new Image(__DIR__ . "/../_files/images/mars.jpg"); - new Image(__DIR__ . "/../_files/images/mario.gif"); - new Image(__DIR__ . "/../_files/images/firefox.png"); - new Image(__DIR__ . "/../_files/images/duke_nukem.bmp"); - new Image(__DIR__ . "/../_files/images/angela_merkel.tif"); - } + $images = array( + array('mars.jpg', 'image/jpeg', 'jpg', 'imagecreatefromjpeg', 'imagejpeg'), + array('mario.gif', 'image/gif', 'gif', 'imagecreatefromgif', 'imagegif'), + array('firefox.png', 'image/png', 'png', 'imagecreatefrompng', 'imagepng'), + array('duke_nukem.bmp', 'image/bmp', 'bmp', null, null), + array('angela_merkel.tif', 'image/tiff', 'tif', null, null), + ); - /** - * Image not found - * - * @expectedException \PhpOffice\PhpWord\Exception\InvalidImageException - */ - public function testImageNotFound() - { - new Image(__DIR__ . "/../_files/images/thisisnotarealimage"); - } - - /** - * Invalid image types - * - * @expectedException \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException - */ - public function testInvalidImageTypes() - { - new Image(__DIR__ . "/../_files/images/alexz-johnson.pcx"); + foreach ($images as $imageData) { + list($source, $type, $extension, $createFunction, $imageFunction) = $imageData; + $source = __DIR__ . "/../_files/images/" . $source; + $image = new Image($source); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $image); + $this->assertEquals($image->getSource(), $source); + $this->assertEquals($image->getMediaId(), md5($source)); + $this->assertEquals($image->getImageType(), $type); + $this->assertEquals($image->getImageExtension(), $extension); + $this->assertEquals($image->getImageCreateFunction(), $createFunction); + $this->assertEquals($image->getImageFunction(), $imageFunction); + $this->assertFalse($image->getIsMemImage()); + } } /** @@ -88,118 +83,61 @@ class ImageTest extends \PHPUnit_Framework_TestCase { $oImage = new Image( __DIR__ . "/../_files/images/earth.jpg", - array('width' => 210, 'height' => 210, 'align' => 'center') + array('height' => 210, 'align' => 'center') ); $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Image', $oImage->getStyle()); } + /** + * Test invalid local image + * + * @expectedException \PhpOffice\PhpWord\Exception\InvalidImageException + */ + public function testInvalidImageLocal() + { + new Image(__DIR__ . "/../_files/images/thisisnotarealimage"); + } + + /** + * Test invalid PHP Image + * + * @expectedException \PhpOffice\PhpWord\Exception\InvalidImageException + */ + public function testInvalidImagePhp() + { + $object = new Image('test.php'); + } + + /** + * Test unsupported image + * + * @expectedException \PhpOffice\PhpWord\Exception\UnsupportedImageTypeException + */ + public function testUnsupportedImage() + { + $object = new Image('http://samples.libav.org/image-samples/RACECAR.BMP'); + } + /** * Get relation Id */ public function testRelationID() { - $oImage = new Image(__DIR__ . "/../_files/images/earth.jpg"); + $oImage = new Image(__DIR__ . "/../_files/images/earth.jpg", array('width' => 100)); $iVal = rand(1, 1000); $oImage->setRelationId($iVal); $this->assertEquals($oImage->getRelationId(), $iVal); } /** - * Get is watermark + * Test archived image */ - public function testWatermark() + public function testArchivedImage() { - $oImage = new Image(__DIR__ . "/../_files/images/earth.jpg"); - $oImage->setIsWatermark(true); - $this->assertEquals($oImage->getIsWatermark(), true); - } - - /** - * Test PNG - */ - public function testPNG() - { - $src = __DIR__ . "/../_files/images/firefox.png"; - $oImage = new Image($src); - - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $oImage); - $this->assertEquals($oImage->getSource(), $src); - $this->assertEquals($oImage->getMediaId(), md5($src)); - $this->assertEquals($oImage->getImageCreateFunction(), 'imagecreatefrompng'); - $this->assertEquals($oImage->getImageFunction(), 'imagepng'); - $this->assertEquals($oImage->getImageExtension(), 'png'); - $this->assertEquals($oImage->getImageType(), 'image/png'); - } - - /** - * Test GIF - */ - public function testGIF() - { - $src = __DIR__ . "/../_files/images/mario.gif"; - $oImage = new Image($src); - - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $oImage); - $this->assertEquals($oImage->getSource(), $src); - $this->assertEquals($oImage->getMediaId(), md5($src)); - $this->assertEquals($oImage->getImageCreateFunction(), 'imagecreatefromgif'); - $this->assertEquals($oImage->getImageFunction(), 'imagegif'); - $this->assertEquals($oImage->getImageExtension(), 'gif'); - $this->assertEquals($oImage->getImageType(), 'image/gif'); - } - - /** - * Test JPG - */ - public function testJPG() - { - $src = __DIR__ . "/../_files/images/earth.jpg"; - $oImage = new Image($src); - - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $oImage); - $this->assertEquals($oImage->getSource(), $src); - $this->assertEquals($oImage->getMediaId(), md5($src)); - $this->assertEquals($oImage->getImageCreateFunction(), 'imagecreatefromjpeg'); - $this->assertEquals($oImage->getImageFunction(), 'imagejpeg'); - $this->assertEquals($oImage->getImageExtension(), 'jpg'); - $this->assertEquals($oImage->getImageType(), 'image/jpeg'); - } - - /** - * Test BMP - */ - public function testBMP() - { - $oImage = new Image(__DIR__ . "/../_files/images/duke_nukem.bmp"); - - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $oImage); - $this->assertEquals($oImage->getImageCreateFunction(), null); - $this->assertEquals($oImage->getImageFunction(), null); - $this->assertEquals($oImage->getImageExtension(), 'bmp'); - $this->assertEquals($oImage->getImageType(), 'image/bmp'); - } - - /** - * Test TIFF - */ - public function testTIFF() - { - $oImage = new Image(__DIR__ . "/../_files/images/angela_merkel.tif"); - - $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $oImage); - $this->assertEquals($oImage->getImageCreateFunction(), null); - $this->assertEquals($oImage->getImageFunction(), null); - $this->assertEquals($oImage->getImageType(), 'image/tiff'); - } - - /** - * Test PHP Image - * - * @expectedException \PhpOffice\PhpWord\Exception\InvalidImageException - */ - public function testPhpImage() - { - $object = new Image('test.php'); + $archiveFile = __DIR__ . "/../_files/documents/reader.docx"; + $imageFile = 'word/media/image1.jpeg'; + $image = new Image("zip://{$archiveFile}#{$imageFile}"); + $this->assertEquals('image/jpeg', $image->getImageType()); } } diff --git a/tests/PhpWord/Tests/Element/TableTest.php b/tests/PhpWord/Tests/Element/TableTest.php index 0d3a74b9..3fc51f98 100644 --- a/tests/PhpWord/Tests/Element/TableTest.php +++ b/tests/PhpWord/Tests/Element/TableTest.php @@ -48,11 +48,11 @@ class TableTest extends \PHPUnit_Framework_TestCase */ public function testStyleArray() { - $oTable = new Table( - 'section', - 1, - array('borderSize' => 6, 'borderColor' => '006699', 'cellMargin' => 80) - ); + $oTable = new Table('section', 1, array( + 'borderSize' => 6, + 'borderColor' => '006699', + 'cellMargin' => 80 + )); $this->assertInstanceOf('PhpOffice\\PhpWord\\Style\\Table', $oTable->getStyle()); } @@ -63,7 +63,7 @@ class TableTest extends \PHPUnit_Framework_TestCase public function testWidth() { $oTable = new Table('section', 1); - $iVal = rand(1, 1000); + $iVal = rand(1, 1000); $oTable->setWidth($iVal); $this->assertEquals($oTable->getWidth(), $iVal); } @@ -73,7 +73,7 @@ class TableTest extends \PHPUnit_Framework_TestCase */ public function testRow() { - $oTable = new Table('section', 1); + $oTable = new Table('section', 1); $element = $oTable->addRow(); $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Row', $element); $this->assertCount(1, $oTable->getRows()); @@ -89,4 +89,18 @@ class TableTest extends \PHPUnit_Framework_TestCase $element = $oTable->addCell(); $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Cell', $element); } + + /** + * Add cell + */ + public function testCountColumns() + { + $oTable = new Table('section', 1); + $oTable->addRow(); + $element = $oTable->addCell(); + $this->assertEquals($oTable->countColumns(), 1); + $element = $oTable->addCell(); + $element = $oTable->addCell(); + $this->assertEquals($oTable->countColumns(), 3); + } } diff --git a/tests/PhpWord/Tests/SettingsTest.php b/tests/PhpWord/Tests/SettingsTest.php index fa78489e..e7968355 100644 --- a/tests/PhpWord/Tests/SettingsTest.php +++ b/tests/PhpWord/Tests/SettingsTest.php @@ -19,9 +19,9 @@ use PhpOffice\PhpWord\Settings; class SettingsTest extends \PHPUnit_Framework_TestCase { /** - * Get/set compatibity option + * Test set/get compatibity option */ - public function testGetSetCompatibility() + public function testSetGetCompatibility() { $this->assertTrue(Settings::getCompatibility()); $this->assertTrue(Settings::setCompatibility(false)); @@ -30,12 +30,26 @@ class SettingsTest extends \PHPUnit_Framework_TestCase } /** - * Get/set zip class + * Test set/get zip class */ - public function testGetSetZipClass() + public function testSetGetZipClass() { $this->assertEquals(Settings::ZIPARCHIVE, Settings::getZipClass()); $this->assertTrue(Settings::setZipClass(Settings::PCLZIP)); $this->assertFalse(Settings::setZipClass('foo')); } + + /** + * Test set/get PDF renderer + */ + public function testSetGetPdfRenderer() + { + $domPdfPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/dompdf/dompdf'); + + $this->assertFalse(Settings::setPdfRenderer('FOO', 'dummy/path')); + $this->assertTrue(Settings::setPdfRenderer(Settings::PDF_RENDERER_DOMPDF, $domPdfPath)); + $this->assertEquals(Settings::PDF_RENDERER_DOMPDF, Settings::getPdfRendererName()); + $this->assertEquals($domPdfPath, Settings::getPdfRendererPath()); + $this->assertFalse(Settings::setPdfRendererPath('dummy/path')); + } } diff --git a/tests/PhpWord/Tests/Shared/XMLReaderTest.php b/tests/PhpWord/Tests/Shared/XMLReaderTest.php new file mode 100644 index 00000000..759bf580 --- /dev/null +++ b/tests/PhpWord/Tests/Shared/XMLReaderTest.php @@ -0,0 +1,54 @@ +assertFalse($object->getDomFromZip($filename, 'yadayadaya')); + } + + /** + * Test get elements returns empty + */ + public function testGetElementsReturnsEmpty() + { + $object = new XMLReader(); + $this->assertEquals(array(), $object->getElements('w:document')); + } + + /** + * Test get element returns null + */ + public function testGetElementReturnsNull() + { + $filename = __DIR__ . "/../_files/documents/reader.docx.zip"; + + $object = new XMLReader(); + $object->getDomFromZip($filename, '[Content_Types].xml'); + $element = $object->getElements('*')->item(0); + + $this->assertNull($object->getElement('yadayadaya', $element)); + } +} diff --git a/tests/PhpWord/Tests/Shared/ZipArchiveTest.php b/tests/PhpWord/Tests/Shared/ZipArchiveTest.php index ba47ade1..40bdde68 100644 --- a/tests/PhpWord/Tests/Shared/ZipArchiveTest.php +++ b/tests/PhpWord/Tests/Shared/ZipArchiveTest.php @@ -24,14 +24,51 @@ class ZipArchiveTest extends \PHPUnit_Framework_TestCase public function testAdd() { $existingFile = __DIR__ . "/../_files/documents/sheet.xls"; - $zipFile = __DIR__ . "/../_files/documents/ziptest.zip"; - $object = new ZipArchive(); + $zipFile = __DIR__ . "/../_files/documents/ziptest.zip"; + $object = new ZipArchive(); $object->open($zipFile); $object->addFile($existingFile, 'xls/new.xls'); $object->addFromString('content/string.txt', 'Test'); $this->assertTrue($object->locateName('xls/new.xls')); $this->assertEquals('Test', $object->getFromName('content/string.txt')); + $this->assertEquals('Test', $object->getFromName('/content/string.txt')); + + unlink($zipFile); + } + + /** + * Test find if a given name exists in the archive + */ + public function testLocate() + { + $existingFile = __DIR__ . "/../_files/documents/sheet.xls"; + $zipFile = __DIR__ . "/../_files/documents/ziptest.zip"; + $object = new ZipArchive(); + $object->open($zipFile); + $object->addFile($existingFile, 'xls/new.xls'); + $object->addFromString('content/string.txt', 'Test'); + + $this->assertEquals(1, $object->locateName('content/string.txt')); + $this->assertFalse($object->locateName('blablabla')); + + unlink($zipFile); + } + + /** + * Test returns the name of an entry using its index + */ + public function testNameIndex() + { + $existingFile = __DIR__ . "/../_files/documents/sheet.xls"; + $zipFile = __DIR__ . "/../_files/documents/ziptest.zip"; + $object = new ZipArchive(); + $object->open($zipFile); + $object->addFile($existingFile, 'xls/new.xls'); + $object->addFromString('content/string.txt', 'Test'); + + $this->assertFalse($object->getNameIndex(-1)); + $this->assertEquals('content/string.txt', $object->getNameIndex(1)); unlink($zipFile); } diff --git a/tests/PhpWord/Tests/Style/AbstractStyleTest.php b/tests/PhpWord/Tests/Style/AbstractStyleTest.php new file mode 100644 index 00000000..ae11c27c --- /dev/null +++ b/tests/PhpWord/Tests/Style/AbstractStyleTest.php @@ -0,0 +1,70 @@ +getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle'); + $stub->setStyleByArray(array('index' => 1)); + + $this->assertEquals(1, $stub->getIndex()); + } + + /** + * Test setBoolVal, setIntVal, setFloatVal, setEnumVal with normal value + */ + public function testSetValNormal() + { + $stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle'); + + $this->assertEquals(true, self::callProtectedMethod($stub, 'setBoolVal', array(true, false))); + $this->assertEquals(12, self::callProtectedMethod($stub, 'setIntVal', array(12, 200))); + $this->assertEquals(871.1, self::callProtectedMethod($stub, 'setFloatVal', array(871.1, 2.1))); + $this->assertEquals('a', self::callProtectedMethod($stub, 'setEnumVal', array('a', array('a', 'b'), 'b'))); + } + + /** + * Test setBoolVal, setIntVal, setFloatVal, setEnumVal with default value + */ + public function testSetValDefault() + { + $stub = $this->getMockForAbstractClass('\PhpOffice\PhpWord\Style\AbstractStyle'); + + $this->assertEquals(false, self::callProtectedMethod($stub, 'setBoolVal', array('a', false))); + $this->assertEquals(200, self::callProtectedMethod($stub, 'setIntVal', array('foo', 200))); + $this->assertEquals(2.1, self::callProtectedMethod($stub, 'setFloatVal', array('foo', 2.1))); + $this->assertEquals('b', self::callProtectedMethod($stub, 'setEnumVal', array('z', array('a', 'b'), 'b'))); + } + + /** + * Helper function to call protected method + * + * @param mixed $object + * @param string $method + * @param array $args + */ + public static function callProtectedMethod($object, $method, array $args = array()) + { + $class = new \ReflectionClass(get_class($object)); + $method = $class->getMethod($method); + $method->setAccessible(true); + return $method->invokeArgs($object, $args); + } +} diff --git a/tests/PhpWord/Tests/Style/ImageTest.php b/tests/PhpWord/Tests/Style/ImageTest.php index b35c8cb2..fd74d73c 100644 --- a/tests/PhpWord/Tests/Style/ImageTest.php +++ b/tests/PhpWord/Tests/Style/ImageTest.php @@ -32,7 +32,7 @@ class ImageTest extends \PHPUnit_Framework_TestCase 'align' => 'left', 'marginTop' => 240, 'marginLeft' => 240, - 'wrappingStyle' => 'inline', + 'wrappingStyle' => 'inline' ); foreach ($properties as $key => $value) { $set = "set{$key}"; @@ -54,7 +54,7 @@ class ImageTest extends \PHPUnit_Framework_TestCase 'height' => 200, 'align' => 'left', 'marginTop' => 240, - 'marginLeft' => 240, + 'marginLeft' => 240 ); foreach ($properties as $key => $value) { $get = "get{$key}"; diff --git a/tests/PhpWord/Tests/Style/ListItemTest.php b/tests/PhpWord/Tests/Style/ListItemTest.php index 0fb67da3..6eef720c 100644 --- a/tests/PhpWord/Tests/Style/ListItemTest.php +++ b/tests/PhpWord/Tests/Style/ListItemTest.php @@ -53,4 +53,16 @@ class ListItemTest extends \PHPUnit_Framework_TestCase $object->setListType($value); $this->assertEquals($value, $object->getListType()); } + + /** + * Test set/get numbering style name + */ + public function testSetGetNumStyle() + { + $expected = 'List Name'; + + $object = new ListItem(); + $object->setNumStyle($expected); + $this->assertEquals($expected, $object->getNumStyle()); + } } diff --git a/tests/PhpWord/Tests/Style/NumberingLevelTest.php b/tests/PhpWord/Tests/Style/NumberingLevelTest.php new file mode 100644 index 00000000..f3e28a0e --- /dev/null +++ b/tests/PhpWord/Tests/Style/NumberingLevelTest.php @@ -0,0 +1,49 @@ + 1, + 'start' => 1, + 'format' => 'decimal', + 'restart' => 1, + 'suffix' => 'space', + 'text' => '%1.', + 'align' => 'left', + 'left' => 360, + 'hanging' => 360, + 'tabPos' => 360, + 'font' => 'Arial', + 'hint' => 'default', + ); + foreach ($attributes as $key => $value) { + $set = "set{$key}"; + $get = "get{$key}"; + $object->$set($value); + $this->assertEquals($value, $object->$get()); + } + } +} diff --git a/tests/PhpWord/Tests/Writer/HTMLTest.php b/tests/PhpWord/Tests/Writer/HTMLTest.php new file mode 100644 index 00000000..354da0e6 --- /dev/null +++ b/tests/PhpWord/Tests/Writer/HTMLTest.php @@ -0,0 +1,110 @@ +assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $object->getPhpWord()); + } + + /** + * Construct with null + * + * @expectedException \PhpOffice\PhpWord\Exception\Exception + * @expectedExceptionMessage No PhpWord assigned. + */ + public function testConstructWithNull() + { + $object = new HTML(); + $object->getPhpWord(); + } + + /** + * Save + */ + public function testSave() + { + $localImage = __DIR__ . "/../_files/images/PhpWord.png"; + $archiveImage = 'zip://' . __DIR__ . '/../_files/documents/reader.docx#word/media/image1.jpeg'; + $gdImage = 'http://php.net/images/logos/php-med-trans-light.gif'; + $objectSrc = __DIR__ . "/../_files/documents/sheet.xls"; + $file = __DIR__ . "/../_files/temp.html"; + + $phpWord = new PhpWord(); + + $docProps = $phpWord->getDocumentProperties(); + $docProps->setTitle('HTML Test'); + + $phpWord->addFontStyle('Font', array('name' => 'Verdana', 'size' => 11, 'color' => 'FF0000', 'fgColor' => 'FF0000')); + $phpWord->addParagraphStyle('Paragraph', array('align' => 'center')); + $section = $phpWord->addSection(); + $section->addText('Test 1', 'Font', 'Paragraph'); + $section->addTextBreak(); + $section->addText('Test 2', array('name' => 'Tahoma', 'bold' => true, 'italic' => true)); + $section->addLink('http://test.com'); + $section->addTitle('Test', 1); + $section->addPageBreak(); + $section->addListItem('Test'); + $section->addImage($localImage); + $section->addImage($archiveImage); + $section->addImage($gdImage); + $section->addObject($objectSrc); + $section->addFootnote(); + $section->addEndnote(); + + $section = $phpWord->addSection(); + + $textrun = $section->addTextRun(array('align' => 'center')); + $textrun->addText('Test 3'); + $textrun->addTextBreak(); + + $textrun = $section->addTextRun('Paragraph'); + $textrun->addLink('http://test.com'); + $textrun->addImage($localImage); + $textrun->addFootnote(); + $textrun->addEndnote(); + + $section = $phpWord->addSection(); + + $table = $section->addTable(); + $cell = $table->addRow()->addCell(); + $cell->addText('Test 1', array('superscript' => true, 'underline' => 'dash', 'strikethrough' => true)); + $cell->addTextRun(); + $cell->addLink('http://test.com'); + $cell->addTextBreak(); + $cell->addListItem('Test'); + $cell->addImage($localImage); + $cell->addObject($objectSrc); + $cell->addFootnote(); + $cell->addEndnote(); + $cell = $table->addRow()->addCell(); + + $writer = new HTML($phpWord); + $writer->save($file); + + $this->assertTrue(file_exists($file)); + + unlink($file); + } +} diff --git a/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php b/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php new file mode 100644 index 00000000..3282eecb --- /dev/null +++ b/tests/PhpWord/Tests/Writer/PDF/DomPDFTest.php @@ -0,0 +1,70 @@ +addSection(); + $section->addText('Test 1'); + + $rendererName = Settings::PDF_RENDERER_DOMPDF; + $rendererLibraryPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/dompdf/dompdf'); + Settings::setPdfRenderer($rendererName, $rendererLibraryPath); + $writer = new PDF($phpWord); + $writer->save($file); + + $this->assertTrue(file_exists($file)); + + unlink($file); + } + + /** + * Test set/get abstract renderer properties + */ + public function testSetGetAbstractRendererProperties() + { + define('DOMPDF_ENABLE_AUTOLOAD', false); + $file = __DIR__ . "/../../_files/temp.pdf"; + + $rendererName = Settings::PDF_RENDERER_DOMPDF; + $rendererLibraryPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/dompdf/dompdf'); + Settings::setPdfRenderer($rendererName, $rendererLibraryPath); + $writer = new PDF(new PhpWord()); + + $writer->setFont('arial'); + $this->assertEquals('arial', $writer->getFont()); + + $writer->setPaperSize(); + $this->assertEquals(9, $writer->getPaperSize()); + + $writer->setOrientation(); + $this->assertEquals('default', $writer->getOrientation()); + + $writer->setTempDir(sys_get_temp_dir()); + $this->assertEquals(sys_get_temp_dir(), $writer->getTempDir()); + } +} diff --git a/tests/PhpWord/Tests/Writer/PDFTest.php b/tests/PhpWord/Tests/Writer/PDFTest.php new file mode 100644 index 00000000..fba93054 --- /dev/null +++ b/tests/PhpWord/Tests/Writer/PDFTest.php @@ -0,0 +1,51 @@ +save($file); + + $this->assertTrue(file_exists($file)); + + unlink($file); + } + + /** + * Test construct exception + * + * @expectedException \PhpOffice\PhpWord\Exception\Exception + * @expectedExceptionMessage PDF rendering library or library path has not been defined. + */ + public function testConstructException() + { + $writer = new PDF(new PhpWord()); + } +} diff --git a/tests/PhpWord/Tests/Writer/Word2007/BaseTest.php b/tests/PhpWord/Tests/Writer/Word2007/BaseTest.php index d9d1b3c5..ba01cb3e 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/BaseTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/BaseTest.php @@ -28,7 +28,7 @@ class BaseTest extends \PHPUnit_Framework_TestCase } /** - * covers ::_writeText + * Test write text element */ public function testWriteText() { @@ -49,7 +49,7 @@ class BaseTest extends \PHPUnit_Framework_TestCase } /** - * covers ::_writeTextRun + * Test write textrun element */ public function testWriteTextRun() { @@ -74,7 +74,7 @@ class BaseTest extends \PHPUnit_Framework_TestCase } /** - * covers ::_writeLink + * Test write link element */ public function testWriteLink() { @@ -97,7 +97,7 @@ class BaseTest extends \PHPUnit_Framework_TestCase } /** - * covers ::_writePreserveText + * Test write preserve text element */ public function testWritePreserveText() { @@ -121,7 +121,7 @@ class BaseTest extends \PHPUnit_Framework_TestCase } /** - * covers ::_writeTextBreak + * Test write text break */ public function testWriteTextBreak() { @@ -146,30 +146,98 @@ class BaseTest extends \PHPUnit_Framework_TestCase } /** - * covers ::_writeParagraphStyle + * covers ::_writeImage */ - public function testWriteParagraphStyleAlign() + public function testWriteImage() { $phpWord = new PhpWord(); + $styles = array('align' => 'left', 'width' => 40, 'height' => 40, 'marginTop' => -1, 'marginLeft' => -1); + $wraps = array('inline', 'behind', 'infront', 'square', 'tight'); $section = $phpWord->addSection(); + foreach ($wraps as $wrap) { + $styles['wrappingStyle'] = $wrap; + $section->addImage(__DIR__ . "/../../_files/images/earth.jpg", $styles); + } - $section->addText('This is my text', null, array('align' => 'right')); + $archiveFile = realpath(__DIR__ . '/../../_files/documents/reader.docx'); + $imageFile = 'word/media/image1.jpeg'; + $source = 'zip://' . $archiveFile . '#' . $imageFile; + $section->addImage($source); $doc = TestHelperDOCX::getDocument($phpWord); - $element = $doc->getElement('/w:document/w:body/w:p/w:pPr/w:jc'); - $this->assertEquals('right', $element->getAttribute('w:val')); + // behind + $element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:pict/v:shape'); + $style = $element->getAttribute('style'); + $this->assertRegExp('/z\-index:\-[0-9]*/', $style); + + // square + $element = $doc->getElement('/w:document/w:body/w:p[4]/w:r/w:pict/v:shape/w10:wrap'); + $this->assertEquals('square', $element->getAttribute('type')); + } + + /** + * covers ::_writeWatermark + */ + public function testWriteWatermark() + { + $imageSrc = __DIR__ . "/../../_files/images/earth.jpg"; + + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $header = $section->addHeader(); + $header->addWatermark($imageSrc); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = $doc->getElement("/w:document/w:body/w:sectPr/w:headerReference"); + $this->assertStringStartsWith("rId", $element->getAttribute('r:id')); + } + + /** + * covers ::_writeTitle + */ + public function testWriteTitle() + { + $phpWord = new PhpWord(); + $phpWord->addTitleStyle(1, array('bold' => true), array('spaceAfter' => 240)); + $phpWord->addSection()->addTitle('Test', 1); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = "/w:document/w:body/w:p/w:pPr/w:pStyle"; + $this->assertEquals('Heading1', $doc->getElementAttribute($element, 'w:val')); + $element = "/w:document/w:body/w:p/w:r/w:fldChar"; + $this->assertEquals('end', $doc->getElementAttribute($element, 'w:fldCharType')); + } + + /** + * covers ::_writeCheckbox + */ + public function testWriteCheckbox() + { + $rStyle = 'rStyle'; + $pStyle = 'pStyle'; + + $phpWord = new PhpWord(); + $phpWord->addFontStyle($rStyle, array('bold' => true)); + $phpWord->addParagraphStyle($pStyle, array('hanging' => 120, 'indent' => 120)); + $section = $phpWord->addSection(); + $section->addCheckbox('Check1', 'Test', $rStyle, $pStyle); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r/w:fldChar/w:ffData/w:name'; + $this->assertEquals('Check1', $doc->getElementAttribute($element, 'w:val')); } /** * covers ::_writeParagraphStyle */ - public function testWriteParagraphStylePagination() + public function testWriteParagraphStyle() { // Create the doc $phpWord = new PhpWord(); $section = $phpWord->addSection(); $attributes = array( + 'align' => 'right', 'widowControl' => false, 'keepNext' => true, 'keepLines' => true, @@ -184,10 +252,13 @@ class BaseTest extends \PHPUnit_Framework_TestCase $i = 0; foreach ($attributes as $key => $value) { $i++; - $path = "/w:document/w:body/w:p[{$i}]/w:pPr/w:{$key}"; + $nodeName = ($key == 'align') ? 'jc' : $key; + $path = "/w:document/w:body/w:p[{$i}]/w:pPr/w:{$nodeName}"; + if ($key != 'align') { + $value = $value ? 1 : 0; + } $element = $doc->getElement($path); - $expected = $value ? 1 : 0; - $this->assertEquals($expected, $element->getAttribute('w:val')); + $this->assertEquals($value, $element->getAttribute('w:val')); } } @@ -316,81 +387,4 @@ class BaseTest extends \PHPUnit_Framework_TestCase $this->assertEquals(5, $element->getAttribute('w:val')); } - - /** - * covers ::_writeImage - */ - public function testWriteImagePosition() - { - $phpWord = new PhpWord(); - $section = $phpWord->addSection(); - $section->addImage( - __DIR__ . "/../../_files/images/earth.jpg", - array( - 'marginTop' => -1, - 'marginLeft' => -1, - 'wrappingStyle' => 'behind' - ) - ); - - $doc = TestHelperDOCX::getDocument($phpWord); - $element = $doc->getElement('/w:document/w:body/w:p/w:r/w:pict/v:shape'); - - $style = $element->getAttribute('style'); - - $this->assertRegExp('/z\-index:\-[0-9]*/', $style); - $this->assertRegExp('/position:absolute;/', $style); - } - - /** - * covers ::_writeWatermark - */ - public function testWriteWatermark() - { - $imageSrc = __DIR__ . "/../../_files/images/earth.jpg"; - - $phpWord = new PhpWord(); - $section = $phpWord->addSection(); - $header = $section->addHeader(); - $header->addWatermark($imageSrc); - $doc = TestHelperDOCX::getDocument($phpWord); - - $element = $doc->getElement("/w:document/w:body/w:sectPr/w:headerReference"); - $this->assertStringStartsWith("rId", $element->getAttribute('r:id')); - } - - /** - * covers ::_writeTitle - */ - public function testWriteTitle() - { - $phpWord = new PhpWord(); - $phpWord->addTitleStyle(1, array('bold' => true), array('spaceAfter' => 240)); - $phpWord->addSection()->addTitle('Test', 1); - $doc = TestHelperDOCX::getDocument($phpWord); - - $element = "/w:document/w:body/w:p/w:pPr/w:pStyle"; - $this->assertEquals('Heading1', $doc->getElementAttribute($element, 'w:val')); - $element = "/w:document/w:body/w:p/w:r/w:fldChar"; - $this->assertEquals('end', $doc->getElementAttribute($element, 'w:fldCharType')); - } - - /** - * covers ::_writeCheckbox - */ - public function testWriteCheckbox() - { - $rStyle = 'rStyle'; - $pStyle = 'pStyle'; - - $phpWord = new PhpWord(); - $phpWord->addFontStyle($rStyle, array('bold' => true)); - $phpWord->addParagraphStyle($pStyle, array('hanging' => 120, 'indent' => 120)); - $section = $phpWord->addSection(); - $section->addCheckbox('Check1', 'Test', $rStyle, $pStyle); - $doc = TestHelperDOCX::getDocument($phpWord); - - $element = '/w:document/w:body/w:p/w:r/w:fldChar/w:ffData/w:name'; - $this->assertEquals('Check1', $doc->getElementAttribute($element, 'w:val')); - } } diff --git a/tests/PhpWord/Tests/Writer/Word2007/DocumentTest.php b/tests/PhpWord/Tests/Writer/Word2007/DocumentTest.php index 8cceecb3..fa11638e 100644 --- a/tests/PhpWord/Tests/Writer/Word2007/DocumentTest.php +++ b/tests/PhpWord/Tests/Writer/Word2007/DocumentTest.php @@ -99,12 +99,12 @@ class DocumentTest extends \PHPUnit_Framework_TestCase $objectSrc = __DIR__ . "/../../_files/documents/sheet.xls"; $phpWord = new PhpWord(); - $phpWord->addParagraphStyle('pStyle', array('align' => 'center')); - $phpWord->addFontStyle('fStyle', array('size' => '20')); - $phpWord->addTitleStyle(1, array('color' => '333333', 'bold' => true)); + $phpWord->addParagraphStyle('pStyle', array('align' => 'center')); // Style #1 + $phpWord->addFontStyle('fStyle', array('size' => '20')); // Style #2 + $phpWord->addTitleStyle(1, array('color' => '333333', 'bold' => true)); // Style #3 $fontStyle = new Font('text', array('align' => 'center')); $section = $phpWord->addSection(); - $section->addListItem('List Item', 0, null, null, 'pStyle'); + $section->addListItem('List Item', 0, null, null, 'pStyle'); // Style #4 $section->addObject($objectSrc, array('align' => 'center')); $section->addTOC($fontStyle); $section->addTitle('Title 1', 1); @@ -113,7 +113,7 @@ class DocumentTest extends \PHPUnit_Framework_TestCase // List item $element = $doc->getElement('/w:document/w:body/w:p[1]/w:pPr/w:numPr/w:numId'); - $this->assertEquals(3, $element->getAttribute('w:val')); + $this->assertEquals(4, $element->getAttribute('w:val')); // Object $element = $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:object/o:OLEObject'); diff --git a/tests/PhpWord/Tests/_files/documents/reader.docx b/tests/PhpWord/Tests/_files/documents/reader.docx index 2143c628..5f37ef4b 100644 Binary files a/tests/PhpWord/Tests/_files/documents/reader.docx and b/tests/PhpWord/Tests/_files/documents/reader.docx differ