From 5a68ef600be7423ee5f16579ddce19b7d2b89332 Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Mon, 9 Sep 2019 13:49:16 +0200 Subject: [PATCH 001/112] Allow a closure to be passed with image replacement tags --- src/PhpWord/TemplateProcessor.php | 7 +++++++ tests/PhpWord/TemplateProcessorTest.php | 8 +++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7efc0f1a..3af8738c 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -447,6 +447,13 @@ class TemplateProcessor $width = null; $height = null; $ratio = null; + + // a closure can be passed as replacement value which after resolving, can contain the replacement info for the image + // use case: only when a image if found, the replacement tags can be generated + if (is_callable($replaceImage)) { + $replaceImage = $replaceImage(); + } + if (is_array($replaceImage) && isset($replaceImage['path'])) { $imgPath = $replaceImage['path']; if (isset($replaceImage['width'])) { diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 4caca77a..21203234 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -392,9 +392,11 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase $imagePath = __DIR__ . '/_files/images/earth.jpg'; $variablesReplace = array( - 'headerValue' => $imagePath, - 'documentContent' => array('path' => $imagePath, 'width' => 500, 'height' => 500), - 'footerValue' => array('path' => $imagePath, 'width' => 100, 'height' => 50, 'ratio' => false), + 'headerValue' => function () use ($imagePath) { + return $imagePath; + }, + 'documentContent' => array('path' => $imagePath, 'width' => 500, 'height' => 500), + 'footerValue' => array('path' => $imagePath, 'width' => 100, 'height' => 50, 'ratio' => false), ); $templateProcessor->setImageValue(array_keys($variablesReplace), $variablesReplace); From 6ed320311e6accc3874026a3fe1a38fc28d98c1b Mon Sep 17 00:00:00 2001 From: Michel Bardelmeijer Date: Mon, 9 Sep 2019 13:55:59 +0200 Subject: [PATCH 002/112] Add documentation for image closure support --- docs/templates-processing.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index 5b32aa18..e9c0b969 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -63,6 +63,11 @@ Example: $templateProcessor->setImageValue('CompanyLogo', 'path/to/company/logo.png'); $templateProcessor->setImageValue('UserLogo', array('path' => 'path/to/logo.png', 'width' => 100, 'height' => 100, 'ratio' => false)); + $templateProcessor->setImageValue('FeatureImage', function () { + // Closure will only be executed if the replacement tag is found in the template + + return array('path' => SlowFeatureImageGenerator::make(), 'width' => 100, 'height' => 100, 'ratio' => false); + }); cloneBlock """""""""" From 5e93950bc3746846ab1ca45d8c9fca10f917989f Mon Sep 17 00:00:00 2001 From: Hugo Carvalho Date: Wed, 2 Oct 2019 22:15:14 -0300 Subject: [PATCH 003/112] Update templates processing docs Adding save() and saveAs() methods docs --- docs/templates-processing.rst | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index 5b32aa18..2e8de3a4 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -244,3 +244,20 @@ See ``Sample_40_TemplateSetComplexValue.php`` for examples. $table->addCell(150)->addText('Cell B2'); $table->addCell(150)->addText('Cell B3'); $templateProcessor->setComplexBlock('table', $table); + +save +""""""""" +Saves the loaded template within the current directory. Returns the file path. + +.. code-block:: php + + $filepath = $templateProcessor->save(); + +saveAs +""""""""" +Saves a copy of the loaded template in the indicated path. + +.. code-block:: php + + $pathToSave = 'path/to/save/file.ext'; + $templateProcessor->saveAs($pathToSave); From b0de8e7d1d13ea4ee1f91bca70b4af021c858dd4 Mon Sep 17 00:00:00 2001 From: Manunchik <56105206+Manunchik@users.noreply.github.com> Date: Wed, 23 Oct 2019 13:41:35 +0500 Subject: [PATCH 004/112] Improve unit test --- tests/PhpWord/MediaTest.php | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/PhpWord/MediaTest.php b/tests/PhpWord/MediaTest.php index 3cf62b59..cca413f5 100644 --- a/tests/PhpWord/MediaTest.php +++ b/tests/PhpWord/MediaTest.php @@ -34,6 +34,22 @@ class MediaTest extends AbstractWebServerEmbeddedTest $this->assertEquals(array(), Media::getElements('section')); } + /** + * Get header media elements + */ + public function testGetHeaderMediaElementsWithNull() + { + $this->assertEquals(array(), Media::getElements('header')); + } + + /** + * Get footer media elements + */ + public function testGetFooterMediaElementsWithNull() + { + $this->assertEquals(array(), Media::getElements('footer')); + } + /** * Count section media elements */ From cb7ffd0ac2bc15b3c1900992d28427f7a67af081 Mon Sep 17 00:00:00 2001 From: Manunchik <56105206+Manunchik@users.noreply.github.com> Date: Wed, 23 Oct 2019 13:44:47 +0500 Subject: [PATCH 005/112] Improve unit test --- tests/PhpWord/PhpWordTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/PhpWord/PhpWordTest.php b/tests/PhpWord/PhpWordTest.php index d818e0f8..4acd0fe6 100644 --- a/tests/PhpWord/PhpWordTest.php +++ b/tests/PhpWord/PhpWordTest.php @@ -225,4 +225,13 @@ class PhpWordTest extends \PHPUnit\Framework\TestCase $this->assertEquals(2, $phpWord->getSection(0)->countElements()); $this->assertEquals(1, $phpWord->getSection(1)->countElements()); } + + /** + * @covers \PhpOffice\PhpWord\PhpWord::getSettings + */ + public function testGetSettings() + { + $phpWord = new PhpWord(); + $this->assertInstanceOf('PhpOffice\\PhpWord\\Metadata\\Settings', $phpWord->getSettings()); + } } From 21db2d40a4fb292c7a4a5a6bd9fef928835ca0be Mon Sep 17 00:00:00 2001 From: Manunchik <56105206+Manunchik@users.noreply.github.com> Date: Wed, 23 Oct 2019 13:46:58 +0500 Subject: [PATCH 006/112] Improve unit test --- tests/PhpWord/StyleTest.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/StyleTest.php b/tests/PhpWord/StyleTest.php index cbc39c87..d45bb749 100644 --- a/tests/PhpWord/StyleTest.php +++ b/tests/PhpWord/StyleTest.php @@ -33,6 +33,7 @@ class StyleTest extends \PHPUnit\Framework\TestCase * @covers ::addParagraphStyle * @covers ::addFontStyle * @covers ::addLinkStyle + * @covers ::addNumberingStyle * @covers ::addTitleStyle * @covers ::addTableStyle * @covers ::setDefaultParagraphStyle @@ -47,6 +48,20 @@ class StyleTest extends \PHPUnit\Framework\TestCase $paragraph = array('alignment' => Jc::CENTER); $font = array('italic' => true, '_bold' => true); $table = array('bgColor' => 'CCCCCC'); + $numbering = array( + 'type' => 'multilevel', + 'levels' => array( + array( + 'start' => 1, + 'format' => 'decimal', + 'restart' => 1, + 'suffix' => 'space', + 'text' => '%1.', + 'alignment' => Jc::START, + ), + ), + ); + $styles = array( 'Paragraph' => 'Paragraph', 'Font' => 'Font', @@ -54,12 +69,13 @@ class StyleTest extends \PHPUnit\Framework\TestCase 'Table' => 'Table', 'Heading_1' => 'Font', 'Normal' => 'Paragraph', + 'Numbering' => 'Numbering', ); Style::addParagraphStyle('Paragraph', $paragraph); Style::addFontStyle('Font', $font); Style::addLinkStyle('Link', $font); - // @todo Style::addNumberingStyle + Style::addNumberingStyle('Numbering', $numbering); Style::addTitleStyle(1, $font); Style::addTableStyle('Table', $table); Style::setDefaultParagraphStyle($paragraph); From 0ce843016b3cd24883ae75d41be9416662541a33 Mon Sep 17 00:00:00 2001 From: igronus Date: Thu, 24 Oct 2019 11:07:33 +0300 Subject: [PATCH 007/112] Update templates-processing.rst Typo fix. --- docs/templates-processing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index 5b32aa18..c4c3db4e 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -127,7 +127,7 @@ Given a template containing This block content will be replaced ${/block_name} -The following will replace everything between``${block_name}`` and ``${/block_name}`` with the value passed. +The following will replace everything between ``${block_name}`` and ``${/block_name}`` with the value passed. .. code-block:: php From b23024212709ee9d91e0403b20e03a5f74209f9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bc=2E=20=C5=A0tefan=20Kubini?= Date: Tue, 5 Nov 2019 10:46:24 +0100 Subject: [PATCH 008/112] fixed List item fail #1711 --- .../Writer/HTML/Element/ListItemRun.php | 50 +++++++++++++++++++ tests/PhpWord/Writer/HTML/ElementTest.php | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/PhpWord/Writer/HTML/Element/ListItemRun.php diff --git a/src/PhpWord/Writer/HTML/Element/ListItemRun.php b/src/PhpWord/Writer/HTML/Element/ListItemRun.php new file mode 100644 index 00000000..6842b239 --- /dev/null +++ b/src/PhpWord/Writer/HTML/Element/ListItemRun.php @@ -0,0 +1,50 @@ +element instanceof \PhpOffice\PhpWord\Element\ListItemRun) { + return ''; + } + + $writer = new Container($this->parentWriter, $this->element); + + if (Settings::isOutputEscapingEnabled()) { + $content = '

' . $this->escaper->escapeHtml($writer->write()) . '

' . PHP_EOL; + } else { + $content = '

' . $writer->write() . '

' . PHP_EOL; + } + + return $content; + } +} diff --git a/tests/PhpWord/Writer/HTML/ElementTest.php b/tests/PhpWord/Writer/HTML/ElementTest.php index 101e226f..c8ee4b26 100644 --- a/tests/PhpWord/Writer/HTML/ElementTest.php +++ b/tests/PhpWord/Writer/HTML/ElementTest.php @@ -34,7 +34,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase */ public function testUnmatchedElements() { - $elements = array('Container', 'Footnote', 'Image', 'Link', 'ListItem', 'Table', 'Title', 'Bookmark'); + $elements = array('Container', 'Footnote', 'Image', 'Link', 'ListItem', 'ListItemRun', 'Table', 'Title', 'Bookmark'); foreach ($elements as $element) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\HTML\\Element\\' . $element; $parentWriter = new HTML(); From a10fe823b2977010f82552f357bece578a8b2865 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Sat, 16 Nov 2019 21:37:57 -0800 Subject: [PATCH 009/112] Errors in RTF Escaping 1. Codes meant to be in hex are specified in decimal. Consequently characters which don't need escaping are escaped. 2. Special handling (prepend backslash) needed for {, }, and \. RTF docs generated with those characters cannot be opened in Word. 3. Tab character needs to be escaped as \tab. RTF docs drop these characters. While running test suite, found that Writer/RTF/ElementTest was coded only for Unix line endings, and fails on Windows. Changed so that it would work on either. --- src/PhpWord/Escaper/Rtf.php | 12 +++- tests/PhpWord/Escaper/RtfEscaper2Test.php | 81 +++++++++++++++++++++++ tests/PhpWord/Writer/RTF/ElementTest.php | 11 +-- 3 files changed, 97 insertions(+), 7 deletions(-) create mode 100644 tests/PhpWord/Escaper/RtfEscaper2Test.php diff --git a/src/PhpWord/Escaper/Rtf.php b/src/PhpWord/Escaper/Rtf.php index b8e0b216..42eb22a7 100644 --- a/src/PhpWord/Escaper/Rtf.php +++ b/src/PhpWord/Escaper/Rtf.php @@ -26,8 +26,14 @@ class Rtf extends AbstractEscaper { protected function escapeAsciiCharacter($code) { - if (20 > $code || $code >= 80) { - return '{\u' . $code . '}'; + if ($code == 9) { + return '{\\tab}'; + } + if (0x20 > $code || $code >= 0x80) { + return '{\\u' . $code . '}'; + } + if ($code == 123 || $code == 125 || $code == 92) { // open or close brace or backslash + return '\\' . chr($code); } return chr($code); @@ -35,7 +41,7 @@ class Rtf extends AbstractEscaper protected function escapeMultibyteCharacter($code) { - return '\uc0{\u' . $code . '}'; + return '\\uc0{\\u' . $code . '}'; } /** diff --git a/tests/PhpWord/Escaper/RtfEscaper2Test.php b/tests/PhpWord/Escaper/RtfEscaper2Test.php new file mode 100644 index 00000000..b16dc469 --- /dev/null +++ b/tests/PhpWord/Escaper/RtfEscaper2Test.php @@ -0,0 +1,81 @@ +write()); + + return $txt2; + } + + public function expect($str) + { + return self::HEADER . $str . self::TRAILER; + } + + /** + * Test special characters which require escaping + */ + public function testSpecial() + { + $str = 'Special characters { open brace } close brace \\ backslash'; + $expect = $this->expect('Special characters \\{ open brace \\} close brace \\\\ backslash'); + $this->assertEquals($expect, $this->escapestring($str)); + } + + /** + * Test accented character + */ + public function testAccent() + { + $str = 'Voilà - string with accented char'; + $expect = $this->expect('Voil\\uc0{\\u224} - string with accented char'); + $this->assertEquals($expect, $this->escapestring($str)); + } + + /** + * Test Hebrew + */ + public function testHebrew() + { + $str = 'Hebrew - שלום'; + $expect = $this->expect('Hebrew - \\uc0{\\u1513}\\uc0{\\u1500}\\uc0{\\u1493}\\uc0{\\u1501}'); + $this->assertEquals($expect, $this->escapestring($str)); + } + + /** + * Test tab + */ + public function testTab() + { + $str = "Here's a tab\tfollowed by more characters."; + $expect = $this->expect("Here's a tab{\\tab}followed by more characters."); + $this->assertEquals($expect, $this->escapestring($str)); + } +} diff --git a/tests/PhpWord/Writer/RTF/ElementTest.php b/tests/PhpWord/Writer/RTF/ElementTest.php index 4b01bacf..4c9dfc5e 100644 --- a/tests/PhpWord/Writer/RTF/ElementTest.php +++ b/tests/PhpWord/Writer/RTF/ElementTest.php @@ -24,6 +24,9 @@ use PhpOffice\PhpWord\Writer\RTF; */ class ElementTest extends \PHPUnit\Framework\TestCase { + public function removeCr($field) { + return str_replace("\r\n", "\n", $field->write()); + } /** * Test unmatched elements */ @@ -46,7 +49,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase $element = new \PhpOffice\PhpWord\Element\Field('PAGE'); $field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element); - $this->assertEquals("{\\field{\\*\\fldinst PAGE}{\\fldrslt}}\\par\n", $field->write()); + $this->assertEquals("{\\field{\\*\\fldinst PAGE}{\\fldrslt}}\\par\n", $this->removeCr($field)); } public function testNumpageField() @@ -55,7 +58,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase $element = new \PhpOffice\PhpWord\Element\Field('NUMPAGES'); $field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element); - $this->assertEquals("{\\field{\\*\\fldinst NUMPAGES}{\\fldrslt}}\\par\n", $field->write()); + $this->assertEquals("{\\field{\\*\\fldinst NUMPAGES}{\\fldrslt}}\\par\n", $this->removeCr($field)); } public function testDateField() @@ -64,7 +67,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase $element = new \PhpOffice\PhpWord\Element\Field('DATE', array('dateformat' => 'd MM yyyy H:mm:ss')); $field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element); - $this->assertEquals("{\\field{\\*\\fldinst DATE \\\\@ \"d MM yyyy H:mm:ss\"}{\\fldrslt}}\\par\n", $field->write()); + $this->assertEquals("{\\field{\\*\\fldinst DATE \\\\@ \"d MM yyyy H:mm:ss\"}{\\fldrslt}}\\par\n", $this->removeCr($field)); } public function testIndexField() @@ -73,6 +76,6 @@ class ElementTest extends \PHPUnit\Framework\TestCase $element = new \PhpOffice\PhpWord\Element\Field('INDEX'); $field = new \PhpOffice\PhpWord\Writer\RTF\Element\Field($parentWriter, $element); - $this->assertEquals("{}\\par\n", $field->write()); + $this->assertEquals("{}\\par\n", $this->removeCr($field)); } } From 2513e545406c81a117af7166cc3a053cc01f68e4 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Sat, 16 Nov 2019 23:20:02 -0800 Subject: [PATCH 010/112] Errors in RTF Escaping 1. Codes meant to be in hex are specified in decimal. Consequently characters which don't need escaping are escaped. 2. Special handling (prepend backslash) needed for {, }, and . RTF docs generated with those characters cannot be opened in Word. 3. Tab character needs to be escaped as \tab. RTF docs drop these characters. While running test suite, found that Writer/RTF/ElementTest was coded only for Unix line endings, and fails on Windows. Changed so that it would work on either. --- tests/PhpWord/Escaper/RtfEscaper2Test.php | 4 +++- tests/PhpWord/Writer/RTF/ElementTest.php | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/PhpWord/Escaper/RtfEscaper2Test.php b/tests/PhpWord/Escaper/RtfEscaper2Test.php index b16dc469..27e8a985 100644 --- a/tests/PhpWord/Escaper/RtfEscaper2Test.php +++ b/tests/PhpWord/Escaper/RtfEscaper2Test.php @@ -15,8 +15,10 @@ * @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3 */ +namespace PhpOffice\PhpWord\Escaper; + /** - * Test class for PhpOffice\PhpWord\Writer\RTF\Style subnamespace + * Test class for PhpOffice\PhpWord\Escaper\RTF */ class RtfEscaperTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/PhpWord/Writer/RTF/ElementTest.php b/tests/PhpWord/Writer/RTF/ElementTest.php index 4c9dfc5e..67a319e6 100644 --- a/tests/PhpWord/Writer/RTF/ElementTest.php +++ b/tests/PhpWord/Writer/RTF/ElementTest.php @@ -24,7 +24,8 @@ use PhpOffice\PhpWord\Writer\RTF; */ class ElementTest extends \PHPUnit\Framework\TestCase { - public function removeCr($field) { + public function removeCr($field) + { return str_replace("\r\n", "\n", $field->write()); } /** From 00f9bb5897b18c3507990cc34bc1c4d152e93db1 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Sun, 17 Nov 2019 00:07:02 -0800 Subject: [PATCH 011/112] Formatting changes in source code. --- tests/PhpWord/Escaper/RtfEscaper2Test.php | 2 +- tests/PhpWord/Writer/RTF/ElementTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/Escaper/RtfEscaper2Test.php b/tests/PhpWord/Escaper/RtfEscaper2Test.php index 27e8a985..21c8a8c3 100644 --- a/tests/PhpWord/Escaper/RtfEscaper2Test.php +++ b/tests/PhpWord/Escaper/RtfEscaper2Test.php @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Escaper; /** * Test class for PhpOffice\PhpWord\Escaper\RTF */ -class RtfEscaperTest extends \PHPUnit\Framework\TestCase +class RtfEscaper2Test extends \PHPUnit\Framework\TestCase { const HEADER = '\\pard\\nowidctlpar {\\cf0\\f0 '; const TRAILER = '}\\par'; diff --git a/tests/PhpWord/Writer/RTF/ElementTest.php b/tests/PhpWord/Writer/RTF/ElementTest.php index 67a319e6..3e9c235d 100644 --- a/tests/PhpWord/Writer/RTF/ElementTest.php +++ b/tests/PhpWord/Writer/RTF/ElementTest.php @@ -28,6 +28,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase { return str_replace("\r\n", "\n", $field->write()); } + /** * Test unmatched elements */ From ebf5cf784f1f67e09304984486a7ec2faac8cac3 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 19 Nov 2019 14:24:29 -0800 Subject: [PATCH 012/112] Convert named constant colors to RGB in Shared/Converter. Otherwise, colors will not be as expected for RTF and ODT. --- src/PhpWord/Shared/Converter.php | 45 ++++++++++++++++++++++++++ tests/PhpWord/Shared/ConverterTest.php | 2 ++ 2 files changed, 47 insertions(+) diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 7008ac5d..7c3048fc 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -272,6 +272,50 @@ class Converter return round($angle / self::DEGREE_TO_ANGLE); } + /** + * Convert colorname as string to RGB + * + * @param string $value color name + * @return string color as hex RGB string, or original value if unknown + */ + public static function stringToRgb($value) + { + switch ($value) { + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_YELLOW: + return 'FFFF00'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGREEN: + return '90EE90'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_CYAN: + return '00FFFF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_MAGENTA: + return 'FF00FF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLUE: + return '0000FF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_RED: + return 'FF0000'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKBLUE: + return '00008B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKCYAN: + return '008B8B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGREEN: + return '006400'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA: + return '8B008B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKRED: + return '8B0000'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKYELLOW: + return '8B8B00'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGRAY: + return 'A9A9A9'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGRAY: + return 'D3D3D3'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLACK: + return '000000'; + } + + return $value; + } + /** * Convert HTML hexadecimal to RGB * @@ -283,6 +327,7 @@ class Converter if ($value[0] == '#') { $value = substr($value, 1); } + $value = self::stringToRgb($value); if (strlen($value) == 6) { list($red, $green, $blue) = array($value[0] . $value[1], $value[2] . $value[3], $value[4] . $value[5]); diff --git a/tests/PhpWord/Shared/ConverterTest.php b/tests/PhpWord/Shared/ConverterTest.php index 15be8ec1..122385a9 100644 --- a/tests/PhpWord/Shared/ConverterTest.php +++ b/tests/PhpWord/Shared/ConverterTest.php @@ -114,6 +114,8 @@ class ConverterTest extends \PHPUnit\Framework\TestCase $values[] = array('FF99DD', array(255, 153, 221)); // 6 characters $values[] = array('F9D', array(255, 153, 221)); // 3 characters $values[] = array('0F9D', false); // 4 characters + $values[] = array(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA, array(139, 0, 139)); + $values[] = array('unknow', array(0, 0, 0)); // 6 characters, invalid // Conduct test foreach ($values as $value) { $result = Converter::htmlToRgb($value[0]); From d9ea6175451f0beb8be15d5e771bb2d3b0e7f7c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Vandoorine?= Date: Thu, 28 Nov 2019 09:24:06 +0100 Subject: [PATCH 013/112] Fixes #1750 added proper block cloning to put the image size part after the #number and fixing Process call to array instead of string --- src/PhpWord/TemplateProcessor.php | 2 +- tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7efc0f1a..96b379c1 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -1085,7 +1085,7 @@ class TemplateProcessor { $results = array(); for ($i = 1; $i <= $count; $i++) { - $results[] = preg_replace('/\$\{(.*?)\}/', '\${\\1#' . $i . '}', $xmlBlock); + $results[] = preg_replace('/\$\{(.*?)(:.*)?\}/', '\${\1#' . $i . '\2}', $xmlBlock); } return $results; diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..39714365 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,7 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + self::$httpServer = new Process(array('php', '-S', 'localhost:8080', '-t', 'tests/PhpWord/_files')); self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); From 1451fadc4ad1cecad3c567b9b7bea049cec71b87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bc=2E=20=C5=A0tefan=20Kubini?= Date: Thu, 28 Nov 2019 23:33:10 +0100 Subject: [PATCH 014/112] Add List for docx to html writer #1717 --- .../Writer/HTML/Element/ListItemRun.php | 11 ++------ tests/PhpWord/Writer/HTML/ElementTest.php | 25 +++++++++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/PhpWord/Writer/HTML/Element/ListItemRun.php b/src/PhpWord/Writer/HTML/Element/ListItemRun.php index 6842b239..a4d7e460 100644 --- a/src/PhpWord/Writer/HTML/Element/ListItemRun.php +++ b/src/PhpWord/Writer/HTML/Element/ListItemRun.php @@ -17,14 +17,12 @@ namespace PhpOffice\PhpWord\Writer\HTML\Element; -use PhpOffice\PhpWord\Settings; - /** * ListItem element HTML writer * * @since 0.10.0 */ -class ListItemRun extends ListItem +class ListItemRun extends TextRun { /** * Write list item @@ -38,12 +36,7 @@ class ListItemRun extends ListItem } $writer = new Container($this->parentWriter, $this->element); - - if (Settings::isOutputEscapingEnabled()) { - $content = '

' . $this->escaper->escapeHtml($writer->write()) . '

' . PHP_EOL; - } else { - $content = '

' . $writer->write() . '

' . PHP_EOL; - } + $content = $writer->write() . PHP_EOL; return $content; } diff --git a/tests/PhpWord/Writer/HTML/ElementTest.php b/tests/PhpWord/Writer/HTML/ElementTest.php index c8ee4b26..4eb92fe5 100644 --- a/tests/PhpWord/Writer/HTML/ElementTest.php +++ b/tests/PhpWord/Writer/HTML/ElementTest.php @@ -163,6 +163,31 @@ class ElementTest extends \PHPUnit\Framework\TestCase $this->assertContains($expected, $content); } + /** + * Test write element ListItemRun + */ + public function testListItemRun() + { + $expected1 = 'List item run 1'; + $expected2 = 'List item run 1 in bold'; + + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $listItemRun = $section->addListItemRun(0, null, 'MyParagraphStyle'); + $listItemRun->addText($expected1); + $listItemRun->addText($expected2, array('bold' => true)); + + $htmlWriter = new HTML($phpWord); + $content = $htmlWriter->getContent(); + + $dom = new \DOMDocument(); + $dom->loadHTML($content); + + $this->assertEquals($expected1, $dom->getElementsByTagName('p')->item(0)->textContent); + $this->assertEquals($expected2, $dom->getElementsByTagName('p')->item(1)->textContent); + } + /** * Tests writing table with layout */ From 0945a37c6198ff1b57fd7367227911828e60f52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Vandoorine?= Date: Mon, 2 Dec 2019 08:54:45 +0100 Subject: [PATCH 015/112] Fixed block capture so that it would work properly with blocks to be cloned in rows --- src/PhpWord/TemplateProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 96b379c1..75cc7839 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -1085,7 +1085,7 @@ class TemplateProcessor { $results = array(); for ($i = 1; $i <= $count; $i++) { - $results[] = preg_replace('/\$\{(.*?)(:.*)?\}/', '\${\1#' . $i . '\2}', $xmlBlock); + $results[] = preg_replace('/\$\{([^:]*?)(:.*?)?\}/', '\${\1#' . $i . '\2}', $xmlBlock); } return $results; From aa44594ed37b372574084eefa9f4239ea4c02c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Dupont?= Date: Mon, 2 Dec 2019 14:23:34 +0100 Subject: [PATCH 016/112] fix: PHPUnit test Process() format \Symfony\Component\Process\Process refuses being passed a string with version > 5, which is installed with PHP > 7.2.5. It also refuses being passed an array with version < 3.3, which is installed with PHP < 5.5.9. Solved by checking if Process::fromShellCommandLine() exists, which was introduced in version 4.2.0. --- .../AbstractWebServerEmbeddedTest.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..25fe836a 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,26 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); From f51811b96b08143573c7c489e5fe75b9d77d6467 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Dupont?= Date: Thu, 28 Nov 2019 11:32:29 +0100 Subject: [PATCH 017/112] fix: documentation about paragraph indentation Documentation contained the wrong unit for Paragraph indentation. --- docs/styles.rst | 6 ++++-- src/PhpWord/Style/Paragraph.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/styles.rst b/docs/styles.rst index 27f8ee66..18a9c2ec 100644 --- a/docs/styles.rst +++ b/docs/styles.rst @@ -75,8 +75,10 @@ Available Paragraph style options: - ``alignment``. Supports all alignment modes since 1st Edition of ECMA-376 standard up till ISO/IEC 29500:2012. See ``\PhpOffice\PhpWord\SimpleType\Jc`` class constants for possible values. - ``basedOn``. Parent style. -- ``hanging``. Hanging in *twip*. -- ``indent``. Indent in *twip*. +- ``hanging``. Hanging indentation in *half inches*. +- ``indent``. Indent (left indentation) in *half inches*. +- ``indentation``. An array of indentation key => value pairs in *twip*. Supports *left*, *right*, *firstLine* and *hanging* indentation. + See ``\PhpOffice\PhpWord\Style\Indentation`` for possible identation types. - ``keepLines``. Keep all lines on one page, *true* or *false*. - ``keepNext``. Keep paragraph with next paragraph, *true* or *false*. - ``lineHeight``. Text line height, e.g. *1.0*, *1.5*, etc. diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index 6e9aaf15..72f0f809 100644 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -198,7 +198,7 @@ class Paragraph extends Border { $key = Text::removeUnderscorePrefix($key); if ('indent' == $key || 'hanging' == $key) { - $value = $value * 720; + $value = $value * 720; // 720 twips is 0.5 inch } return parent::setStyleValue($key, $value); From ecfafd757667c2857586d5841dffad6d67b65b70 Mon Sep 17 00:00:00 2001 From: owen Date: Tue, 3 Dec 2019 07:46:16 -0800 Subject: [PATCH 018/112] RTF Changes 1. Converter is currently expecting colors as strings of hex digits, but PhpWord allows specification of colors by named constant, so result is random when one of those is used. This change handles all the named colors. 2. Table needs \pard at end; formatting may be wrong without it. 3. RTF writer will no longer ignore paragraph style for TextRun. 4. RTF writer will no longer ignore paragraph and font style for Title. 5. Add support for RTF headers and footers. 6. Add support for right-to-left in font. 7. Add support for PageBreakBefore and LineHeight for paragraphs. 8. Add support for PageNumberingStart for sections. There are test cases for all of these changes. --- src/PhpWord/Shared/Converter.php | 3 +- .../Writer/RTF/Element/AbstractElement.php | 4 +- src/PhpWord/Writer/RTF/Element/Table.php | 1 + src/PhpWord/Writer/RTF/Element/TextRun.php | 1 + src/PhpWord/Writer/RTF/Element/Title.php | 67 ++++++++++++++++ src/PhpWord/Writer/RTF/Part/Document.php | 69 ++++++++++++++++ src/PhpWord/Writer/RTF/Style/Font.php | 1 + src/PhpWord/Writer/RTF/Style/Paragraph.php | 10 +++ src/PhpWord/Writer/RTF/Style/Section.php | 1 + tests/PhpWord/Shared/ConverterTest.php | 20 ++--- tests/PhpWord/Writer/RTF/ElementTest.php | 76 ++++++++++++++++++ tests/PhpWord/Writer/RTF/HeaderFooterTest.php | 78 +++++++++++++++++++ tests/PhpWord/Writer/RTF/StyleTest.php | 45 +++++++++++ 13 files changed, 360 insertions(+), 16 deletions(-) create mode 100644 tests/PhpWord/Writer/RTF/HeaderFooterTest.php diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 7c3048fc..9206a3bc 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -326,8 +326,9 @@ class Converter { if ($value[0] == '#') { $value = substr($value, 1); + } else { + $value = self::stringToRgb($value); } - $value = self::stringToRgb($value); if (strlen($value) == 6) { list($red, $green, $blue) = array($value[0] . $value[1], $value[2] . $value[3], $value[4] . $value[5]); diff --git a/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/src/PhpWord/Writer/RTF/Element/AbstractElement.php index cf1aa391..132890e6 100644 --- a/src/PhpWord/Writer/RTF/Element/AbstractElement.php +++ b/src/PhpWord/Writer/RTF/Element/AbstractElement.php @@ -41,14 +41,14 @@ abstract class AbstractElement extends HTMLAbstractElement * * @var \PhpOffice\PhpWord\Style\Font */ - private $fontStyle; + protected $fontStyle; /** * Paragraph style * * @var \PhpOffice\PhpWord\Style\Paragraph */ - private $paragraphStyle; + protected $paragraphStyle; public function __construct(AbstractWriter $parentWriter, Element $element, $withoutP = false) { diff --git a/src/PhpWord/Writer/RTF/Element/Table.php b/src/PhpWord/Writer/RTF/Element/Table.php index 8154aa7c..63c3e6a3 100644 --- a/src/PhpWord/Writer/RTF/Element/Table.php +++ b/src/PhpWord/Writer/RTF/Element/Table.php @@ -58,6 +58,7 @@ class Table extends AbstractElement $content .= $this->writeRow($rows[$i]); $content .= '\row' . PHP_EOL; } + $content .= '\pard' . PHP_EOL; } return $content; diff --git a/src/PhpWord/Writer/RTF/Element/TextRun.php b/src/PhpWord/Writer/RTF/Element/TextRun.php index bfd161f0..e2865d82 100644 --- a/src/PhpWord/Writer/RTF/Element/TextRun.php +++ b/src/PhpWord/Writer/RTF/Element/TextRun.php @@ -32,6 +32,7 @@ class TextRun extends AbstractElement public function write() { $writer = new Container($this->parentWriter, $this->element); + $this->getStyles(); $content = ''; $content .= $this->writeOpening(); diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index a9940ca9..3d5e08ca 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -24,4 +24,71 @@ namespace PhpOffice\PhpWord\Writer\RTF\Element; */ class Title extends Text { + protected function getStyles() + { + /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + $element = $this->element; + $style = $element->getStyle(); + if (is_string($style)) { + $style = str_replace('Heading', 'Heading_', $style); + $style = \PhpOffice\PhpWord\Style::getStyle($style); + if ($style instanceof \PhpOffice\PhpWord\Style\Font) { + $this->fontStyle = $style; + $pstyle = $style->getParagraph(); + if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) { + $sect = $element->getParent(); + if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { + $elems = $sect->getElements(); + if ($elems[0] === $element) { + $pstyle = clone $pstyle; + $pstyle->setPageBreakBefore(false); + } + } + } + $this->paragraphStyle = $pstyle; + } + } + } + + /** + * Write element + * + * @return string + */ + public function write() + { + /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + $element = $this->element; + $elementClass = str_replace('\\Writer\\RTF', '', get_class($this)); + if (!$element instanceof $elementClass || !is_string($element->getText())) { + return ''; + } + + $this->getStyles(); + + $content = ''; + + $content .= $this->writeOpening(); + $endout = ''; + $style = $element->getStyle(); + if (is_string($style)) { + $style = str_replace('Heading', '', $style); + if (is_numeric($style)) { + $style = (int) $style - 1; + if ($style >= 0 && $style <= 8) { + $content .= '{\\outlinelevel' . $style; + $endout = '}'; + } + } + } + + $content .= '{'; + $content .= $this->writeFontStyle(); + $content .= $this->writeText($element->getText()); + $content .= '}'; + $content .= $this->writeClosing(); + $content .= $endout; + + return $content; + } } diff --git a/src/PhpWord/Writer/RTF/Part/Document.php b/src/PhpWord/Writer/RTF/Part/Document.php index d4bfadb4..5d4d23d9 100644 --- a/src/PhpWord/Writer/RTF/Part/Document.php +++ b/src/PhpWord/Writer/RTF/Part/Document.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpWord\Writer\RTF\Part; +use PhpOffice\PhpWord\Element\Footer; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\RTF\Element\Container; use PhpOffice\PhpWord\Writer\RTF\Style\Section as SectionStyleWriter; @@ -105,11 +106,36 @@ class Document extends AbstractPart $content .= '\lang' . $langId; $content .= '\kerning1'; // Point size (in half-points) above which to kern character pairs $content .= '\fs' . (Settings::getDefaultFontSize() * 2); // Set the font size in half-points + if ($docSettings->hasEvenAndOddHeaders()) { + $content .= '\\facingp'; + } $content .= PHP_EOL; return $content; } + /** + * Write titlepg directive if any "f" headers or footers + * + * @param \PhpOffice\PhpWord\PhpWord\Element\Section $section + * @return string + */ + private static function writeTitlepg($section) + { + foreach ($section->getHeaders() as $header) { + if ($header->getType() === Footer::FIRST) { + return '\\titlepg' . PHP_EOL; + } + } + foreach ($section->getFooters() as $header) { + if ($header->getType() === Footer::FIRST) { + return '\\titlepg' . PHP_EOL; + } + } + + return ''; + } + /** * Write sections * @@ -120,10 +146,53 @@ class Document extends AbstractPart $content = ''; $sections = $this->getParentWriter()->getPhpWord()->getSections(); + $evenOdd = $this->getParentWriter()->getPhpWord()->getSettings()->hasEvenAndOddHeaders(); foreach ($sections as $section) { $styleWriter = new SectionStyleWriter($section->getStyle()); $styleWriter->setParentWriter($this->getParentWriter()); $content .= $styleWriter->write(); + $content .= self::writeTitlepg($section); + + foreach ($section->getHeaders() as $header) { + $type = $header->getType(); + if ($evenOdd || $type !== FOOTER::EVEN) { + $content .= '{\\header'; + if ($type === Footer::FIRST) { + $content .= 'f'; + } elseif ($evenOdd) { + $content .= ($type === FOOTER::EVEN) ? 'l' : 'r'; + } + foreach ($header->getElements() as $element) { + $cl = get_class($element); + $cl2 = str_replace('Element', 'Writer\\RTF\\Element', $cl); + if (class_exists($cl2)) { + $elementWriter = new $cl2($this->getParentWriter(), $element); + $content .= $elementWriter->write(); + } + } + $content .= '}' . PHP_EOL; + } + } + foreach ($section->getFooters() as $footer) { + $type = $footer->getType(); + if ($evenOdd || $type !== FOOTER::EVEN) { + $content .= '{\\footer'; + if ($type === Footer::FIRST) { + $content .= 'f'; + } elseif ($evenOdd) { + $content .= ($type === FOOTER::EVEN) ? 'l' : 'r'; + } + foreach ($footer->getElements() as $element) { + $cl = get_class($element); + $cl2 = str_replace('Element', 'Writer\\RTF\\Element', $cl); + if (class_exists($cl2)) { + $elementWriter = new $cl2($this->getParentWriter(), $element); + $content .= $elementWriter->write(); + } + } + $content .= '}' . PHP_EOL; + } + } $elementWriter = new Container($this->getParentWriter(), $section); $content .= $elementWriter->write(); diff --git a/src/PhpWord/Writer/RTF/Style/Font.php b/src/PhpWord/Writer/RTF/Style/Font.php index b9001ea0..34f6c1af 100644 --- a/src/PhpWord/Writer/RTF/Style/Font.php +++ b/src/PhpWord/Writer/RTF/Style/Font.php @@ -49,6 +49,7 @@ class Font extends AbstractStyle } $content = ''; + $content .= $this->getValueIf($style->isRTL(), '\rtlch'); $content .= '\cf' . $this->colorIndex; $content .= '\f' . $this->nameIndex; diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index 8ef3e146..3394f9d4 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -52,6 +52,8 @@ class Paragraph extends AbstractStyle Jc::END => '\qr', Jc::CENTER => '\qc', Jc::BOTH => '\qj', + Jc::LEFT => '\ql', + Jc::RIGHT => '\qr', ); $spaceAfter = $style->getSpaceAfter(); @@ -67,6 +69,14 @@ class Paragraph extends AbstractStyle $content .= $this->writeIndentation($style->getIndentation()); $content .= $this->getValueIf($spaceBefore !== null, '\sb' . round($spaceBefore)); $content .= $this->getValueIf($spaceAfter !== null, '\sa' . round($spaceAfter)); + $lineHeight = $style->getLineHeight(); + if ($lineHeight !== null) { + $lineHeightAdjusted = (int) ($lineHeight * 240); + $content .= "\\sl$lineHeightAdjusted\\slmult1"; + } + if ($style->getPageBreakBefore()) { + $content .= '\\page'; + } $styles = $style->getStyleValues(); $content .= $this->writeTabs($styles['tabs']); diff --git a/src/PhpWord/Writer/RTF/Style/Section.php b/src/PhpWord/Writer/RTF/Style/Section.php index 190bb670..c6801947 100644 --- a/src/PhpWord/Writer/RTF/Style/Section.php +++ b/src/PhpWord/Writer/RTF/Style/Section.php @@ -53,6 +53,7 @@ class Section extends AbstractStyle $content .= $this->getValueIf($style->getHeaderHeight() !== null, '\headery' . round($style->getHeaderHeight())); $content .= $this->getValueIf($style->getFooterHeight() !== null, '\footery' . round($style->getFooterHeight())); $content .= $this->getValueIf($style->getGutter() !== null, '\guttersxn' . round($style->getGutter())); + $content .= $this->getValueIf($style->getPageNumberingStart() !== null, '\pgnstarts' . $style->getPageNumberingStart() . '\pgnrestart'); $content .= ' '; // Borders diff --git a/tests/PhpWord/Shared/ConverterTest.php b/tests/PhpWord/Shared/ConverterTest.php index 122385a9..0d10dc49 100644 --- a/tests/PhpWord/Shared/ConverterTest.php +++ b/tests/PhpWord/Shared/ConverterTest.php @@ -108,19 +108,13 @@ class ConverterTest extends \PHPUnit\Framework\TestCase */ public function testHtmlToRGB() { - // Prepare test values [ original, expected ] - $values = array(); - $values[] = array('#FF99DD', array(255, 153, 221)); // With # - $values[] = array('FF99DD', array(255, 153, 221)); // 6 characters - $values[] = array('F9D', array(255, 153, 221)); // 3 characters - $values[] = array('0F9D', false); // 4 characters - $values[] = array(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA, array(139, 0, 139)); - $values[] = array('unknow', array(0, 0, 0)); // 6 characters, invalid - // Conduct test - foreach ($values as $value) { - $result = Converter::htmlToRgb($value[0]); - $this->assertEquals($value[1], $result); - } + $flse = false; + $this->assertEquals(array(255, 153, 221), Converter::htmlToRgb('#FF99DD')); // With # + $this->assertEquals(array(224, 170, 29), Converter::htmlToRgb('E0AA1D')); // 6 characters + $this->assertEquals(array(102, 119, 136), Converter::htmlToRgb('678')); // 3 characters + $this->assertEquals($flse, Converter::htmlToRgb('0F9D')); // 4 characters + $this->assertEquals(array(0, 0, 0), Converter::htmlToRgb('unknow')); // 6 characters, invalid + $this->assertEquals(array(139, 0, 139), Converter::htmlToRgb(\PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA)); // Constant } /** diff --git a/tests/PhpWord/Writer/RTF/ElementTest.php b/tests/PhpWord/Writer/RTF/ElementTest.php index 3e9c235d..44287d71 100644 --- a/tests/PhpWord/Writer/RTF/ElementTest.php +++ b/tests/PhpWord/Writer/RTF/ElementTest.php @@ -80,4 +80,80 @@ class ElementTest extends \PHPUnit\Framework\TestCase $this->assertEquals("{}\\par\n", $this->removeCr($field)); } + + public function testTable() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\Table(); + $width = 100; + $width2 = 2 * $width; + $element->addRow(); + $tce = $element->addCell($width); + $tce->addText('1'); + $tce = $element->addCell($width); + $tce->addText('2'); + $element->addRow(); + $tce = $element->addCell($width); + $tce->addText('3'); + $tce = $element->addCell($width); + $tce->addText('4'); + $table = new \PhpOffice\PhpWord\Writer\RTF\Element\Table($parentWriter, $element); + $expect = implode("\n", array( + '\\pard', + "\\trowd \\cellx$width \\cellx$width2 ", + '\\intbl', + '{\\cf0\\f0 1}\\par', + '\\cell', + '\\intbl', + '{\\cf0\\f0 2}\\par', + '\\cell', + '\\row', + "\\trowd \\cellx$width \\cellx$width2 ", + '\\intbl', + '{\\cf0\\f0 3}\\par', + '\\cell', + '\\intbl', + '{\\cf0\\f0 4}\par', + '\\cell', + '\\row', + '\\pard', + '', + )); + + $this->assertEquals($expect, $this->removeCr($table)); + } + + public function testTextRun() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\TextRun(); + $element->addText('Hello '); + $element->addText('there.'); + $textrun = new \PhpOffice\PhpWord\Writer\RTF\Element\TextRun($parentWriter, $element); + $expect = "\\pard\\nowidctlpar {{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n"; + $this->assertEquals($expect, $this->removeCr($textrun)); + } + + public function testTextRunParagraphStyle() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\TextRun(array('spaceBefore' => 0, 'spaceAfter' => 0)); + $element->addText('Hello '); + $element->addText('there.'); + $textrun = new \PhpOffice\PhpWord\Writer\RTF\Element\TextRun($parentWriter, $element); + $expect = "\\pard\\nowidctlpar \\sb0\\sa0{{\\cf0\\f0 Hello }{\\cf0\\f0 there.}}\\par\n"; + $this->assertEquals($expect, $this->removeCr($textrun)); + } + + public function testTitle() + { + $parentWriter = new RTF(); + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->addTitleStyle(1, array(), array('spaceBefore' => 0, 'spaceAfter' => 0)); + $section = $phpWord->addSection(); + $element = $section->addTitle('First Heading', 1); + $elwrite = new \PhpOffice\PhpWord\Writer\RTF\Element\Title($parentWriter, $element); + $expect = "\\pard\\nowidctlpar \\sb0\\sa0{\\outlinelevel0{\\cf0\\f0 First Heading}\\par\n}"; + $this->assertEquals($expect, $this->removeCr($elwrite)); + } } diff --git a/tests/PhpWord/Writer/RTF/HeaderFooterTest.php b/tests/PhpWord/Writer/RTF/HeaderFooterTest.php new file mode 100644 index 00000000..81c589f4 --- /dev/null +++ b/tests/PhpWord/Writer/RTF/HeaderFooterTest.php @@ -0,0 +1,78 @@ +addSection(); + $section->addText('Doc without header or footer'); + $contents = $parentWriter->getWriterPart('Document')->write(); + $this->assertEquals(0, preg_match('/\\\\header[rlf]?\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\footer[rlf]?\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\titlepg\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\facingp\\b/', $contents)); + } + + public function testNoHeaderYesFooter() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $parentWriter = new RTF($phpWord); + $section = $phpWord->addSection(); + $footer = $section->addFooter(); + $footer->addText('Auto footer'); + $section->addText('Doc without header but with footer'); + $contents = $parentWriter->getWriterPart('Document')->write(); + $this->assertEquals(0, preg_match('/\\\\header[rlf]?\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\footer[rlf]?\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\titlepg\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\facingp\\b/', $contents)); + } + + public function testEvenHeaderFirstFooter() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->getSettings()->setEvenAndOddHeaders(true); + $parentWriter = new RTF($phpWord); + $section = $phpWord->addSection(); + $footer = $section->addFooter(Footer::FIRST); + $footer->addText('First footer'); + $footer = $section->addHeader(Footer::EVEN); + $footer->addText('Even footer'); + $footer = $section->addHeader(Footer::AUTO); + $footer->addText('Odd footer'); + $section->addText('Doc with even/odd header and first footer'); + $contents = $parentWriter->getWriterPart('Document')->write(); + $this->assertEquals(1, preg_match('/\\\\headerr\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\headerl\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\header[f]?\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\footerf\\b/', $contents)); + $this->assertEquals(0, preg_match('/\\\\footer[rl]?\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\titlepg\\b/', $contents)); + $this->assertEquals(1, preg_match('/\\\\facingp\\b/', $contents)); + } +} diff --git a/tests/PhpWord/Writer/RTF/StyleTest.php b/tests/PhpWord/Writer/RTF/StyleTest.php index 317014c6..6624f5ee 100644 --- a/tests/PhpWord/Writer/RTF/StyleTest.php +++ b/tests/PhpWord/Writer/RTF/StyleTest.php @@ -26,6 +26,11 @@ use PHPUnit\Framework\Assert; */ class StyleTest extends \PHPUnit\Framework\TestCase { + public function removeCr($field) + { + return str_replace("\r\n", "\n", $field->write()); + } + /** * Test empty styles */ @@ -108,4 +113,44 @@ class StyleTest extends \PHPUnit\Framework\TestCase Assert::assertEquals('\tqdec\tx0', $result); } + + public function testRTL() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\Text('אב גד', array('RTL'=> true)); + $text = new \PhpOffice\PhpWord\Writer\RTF\Element\Text($parentWriter, $element); + $expect = "\\pard\\nowidctlpar {\\rtlch\\cf0\\f0 \\uc0{\\u1488}\\uc0{\\u1489} \\uc0{\\u1490}\\uc0{\\u1491}}\\par\n"; + $this->assertEquals($expect, $this->removeCr($text)); + } + + public function testPageBreakLineHeight() + { + $parentWriter = new RTF(); + $element = new \PhpOffice\PhpWord\Element\Text('New page', null, array('lineHeight' => 1.08, 'pageBreakBefore' => true)); + $text = new \PhpOffice\PhpWord\Writer\RTF\Element\Text($parentWriter, $element); + $expect = "\\pard\\nowidctlpar \\sl259\\slmult1\\page{\\cf0\\f0 New page}\\par\n"; + $this->assertEquals($expect, $this->removeCr($text)); + } + + public function testPageNumberRestart() + { + //$parentWriter = new RTF(); + $phpword = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpword->addSection(array('pageNumberingStart' => 5)); + $styleWriter = new \PhpOffice\PhpWord\Writer\RTF\Style\Section($section->getStyle()); + $wstyle = $this->removeCr($styleWriter); + // following have default values which might change so don't use them + $wstyle = preg_replace('/\\\\pgwsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\pghsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\margtsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\margrsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\margbsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\marglsxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\headery\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\footery\\d+/', '', $wstyle); + $wstyle = preg_replace('/\\\\guttersxn\\d+/', '', $wstyle); + $wstyle = preg_replace('/ +/', ' ', $wstyle); + $expect = "\\sectd \\pgnstarts5\\pgnrestart \n"; + $this->assertEquals($expect, $wstyle); + } } From 11d82be21c8a7eef9a157201fdd833bb2aa736d3 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 5 Dec 2019 21:04:12 -0800 Subject: [PATCH 019/112] Word2007 Writer - Field Style, RTL, noProof 1. Add support for font styles for fields, and write those to Word docs. 2. Word seems to require explicit inline w:rtl tag even when rtl is specified in a named style. Without this tag, words are placed in ltr order. Allow PhpWord doc to specify rtl in named style and have it display correctly in resulting doc. 3. A recent change incorrectly changed how noProof tag was generated, omitting the third parameter of 4 parameters in the call. There was no test case for this change. The call is now corrected, and a test case has been added. --- docs/elements.rst | 4 +- src/PhpWord/Element/Field.php | 38 +++++++++- src/PhpWord/Writer/Word2007/Element/Field.php | 1 + src/PhpWord/Writer/Word2007/Style/Font.php | 6 +- tests/PhpWord/Writer/Word2007/ElementTest.php | 35 +++++++++ .../Writer/Word2007/Style/FontTest.php | 74 +++++++++++++++++++ 6 files changed, 155 insertions(+), 3 deletions(-) diff --git a/docs/elements.rst b/docs/elements.rst index 9d446b27..176de47b 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -403,7 +403,9 @@ Currently the following fields are supported: .. code-block:: php - $section->addField($fieldType, [$properties], [$options], [$fieldText]) + $section->addField($fieldType, [$properties], [$options], [$fieldText], [$fontStyle]) + +- ``$fontStyle``. See :ref:`font-style`. See ``\PhpOffice\PhpWord\Element\Field`` for list of properties and options available for each field type. Options which are not specifically defined can be added. Those must start with a ``\``. diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php index 2efc6b0b..e6777d20 100644 --- a/src/PhpWord/Element/Field.php +++ b/src/PhpWord/Element/Field.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Element; +use PhpOffice\PhpWord\Style\Font; + /** * Field element * @@ -119,6 +121,38 @@ class Field extends AbstractElement */ protected $fontStyle; + /** + * Set Font style + * + * @param string|array|\PhpOffice\PhpWord\Style\Font $style + * @return string|\PhpOffice\PhpWord\Style\Font + */ + public function setFontStyle($style = null) + { + if ($style instanceof Font) { + $this->fontStyle = $style; + } elseif (is_array($style)) { + $this->fontStyle = new Font('text'); + $this->fontStyle->setStyleByArray($style); + } elseif (null === $style) { + $this->fontStyle = null; + } else { + $this->fontStyle = $style; + } + + return $this->fontStyle; + } + + /** + * Get Font style + * + * @return string|\PhpOffice\PhpWord\Style\Font + */ + public function getFontStyle() + { + return $this->fontStyle; + } + /** * Create a new Field Element * @@ -126,13 +160,15 @@ class Field extends AbstractElement * @param array $properties * @param array $options * @param TextRun|string|null $text + * @param string|array|\PhpOffice\PhpWord\Style\Font $fontStyle */ - public function __construct($type = null, $properties = array(), $options = array(), $text = null) + public function __construct($type = null, $properties = array(), $options = array(), $text = null, $fontStyle = null) { $this->setType($type); $this->setProperties($properties); $this->setOptions($options); $this->setText($text); + $this->setFontStyle($fontStyle); } /** diff --git a/src/PhpWord/Writer/Word2007/Element/Field.php b/src/PhpWord/Writer/Word2007/Element/Field.php index e79dd24a..b800fbdd 100644 --- a/src/PhpWord/Writer/Word2007/Element/Field.php +++ b/src/PhpWord/Writer/Word2007/Element/Field.php @@ -65,6 +65,7 @@ class Field extends Text $instruction .= $this->buildPropertiesAndOptions($element); } $xmlWriter->startElement('w:r'); + $this->writeFontStyle(); $xmlWriter->startElement('w:instrText'); $xmlWriter->writeAttribute('xml:space', 'preserve'); $xmlWriter->text($instruction); diff --git a/src/PhpWord/Writer/Word2007/Style/Font.php b/src/PhpWord/Writer/Word2007/Style/Font.php index dd4fac4f..2f2218fa 100644 --- a/src/PhpWord/Writer/Word2007/Style/Font.php +++ b/src/PhpWord/Writer/Word2007/Style/Font.php @@ -44,6 +44,10 @@ class Font extends AbstractStyle $xmlWriter->startElement('w:rStyle'); $xmlWriter->writeAttribute('w:val', $this->style); $xmlWriter->endElement(); + $style = \PhpOffice\PhpWord\Style::getStyle($this->style); + if ($style instanceof \PhpOffice\PhpWord\Style\Font) { + $xmlWriter->writeElementIf($style->isRTL(), 'w:rtl'); + } $xmlWriter->endElement(); } else { $this->writeStyle(); @@ -139,7 +143,7 @@ class Font extends AbstractStyle $xmlWriter->writeElementIf($style->getKerning() !== null, 'w:kern', 'w:val', $style->getKerning() * 2); // noProof - $xmlWriter->writeElementIf($style->isNoProof() !== null, 'w:noProof', $this->writeOnOf($style->isNoProof())); + $xmlWriter->writeElementIf($style->isNoProof() !== null, 'w:noProof', 'w:val', $this->writeOnOf($style->isNoProof())); // Background-Color $shading = $style->getShading(); diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php index 6a295965..e9c1ea5b 100644 --- a/tests/PhpWord/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Writer/Word2007/ElementTest.php @@ -296,6 +296,41 @@ class ElementTest extends \PHPUnit\Framework\TestCase $this->assertEquals(' INDEX \\c "3" ', $doc->getElement($element)->textContent); } + public function testUnstyledFieldElement() + { + $phpWord = new PhpWord(); + $phpWord->addFontStyle('h1', array('name' => 'Courier New', 'size' => 8)); + $section = $phpWord->addSection(); + + $section->addField('PAGE'); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' PAGE ', $doc->getElement($element)->textContent); + $sty = '/w:document/w:body/w:p/w:r[2]/w:rPr'; + $this->assertFalse($doc->elementExists($sty)); + } + + public function testStyledFieldElement() + { + $phpWord = new PhpWord(); + $stnam = 'h1'; + $phpWord->addFontStyle($stnam, array('name' => 'Courier New', 'size' => 8)); + $section = $phpWord->addSection(); + + $fld = $section->addField('PAGE'); + $fld->setFontStyle($stnam); + $doc = TestHelperDOCX::getDocument($phpWord); + + $element = '/w:document/w:body/w:p/w:r[2]/w:instrText'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals(' PAGE ', $doc->getElement($element)->textContent); + $sty = '/w:document/w:body/w:p/w:r[2]/w:rPr'; + $this->assertTrue($doc->elementExists($sty)); + $this->assertEquals($stnam, $doc->getElementAttribute($sty . '/w:rStyle', 'w:val')); + } + public function testFieldElementWithComplexText() { $phpWord = new PhpWord(); diff --git a/tests/PhpWord/Writer/Word2007/Style/FontTest.php b/tests/PhpWord/Writer/Word2007/Style/FontTest.php index ccfffbfb..41e52ab0 100644 --- a/tests/PhpWord/Writer/Word2007/Style/FontTest.php +++ b/tests/PhpWord/Writer/Word2007/Style/FontTest.php @@ -51,6 +51,80 @@ class FontTest extends \PHPUnit\Framework\TestCase $this->assertTrue($doc->elementExists($path, $file)); } + public function testFontRTLNamed() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $stnam = 'fstyle'; + $phpWord->addFontStyle($stnam, array( + 'rtl' => true, + 'name' => 'Courier New', + 'size' => 8, + )); + $section = $phpWord->addSection(); + $txt = 'היום יום שני'; // Translation = Today is Monday + $section->addText($txt, $stnam); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $element = '/w:document/w:body/w:p/w:r'; + $txtelem = $element . '/w:t'; + $styelem = $element . '/w:rPr'; + $this->assertTrue($doc->elementExists($txtelem)); + $this->assertEquals($txt, $doc->getElement($txtelem)->textContent); + $this->assertTrue($doc->elementExists($styelem)); + $this->assertTrue($doc->elementExists($styelem . '/w:rStyle')); + $this->assertEquals($stnam, $doc->getElementAttribute($styelem . '/w:rStyle', 'w:val')); + $this->assertTrue($doc->elementExists($styelem . '/w:rtl')); + } + + public function testFontNotRTLNamed() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $stnam = 'fstyle'; + $phpWord->addFontStyle($stnam, array( + //'rtl' => true, + 'name' => 'Courier New', + 'size' => 8, + )); + $section = $phpWord->addSection(); + $txt = 'היום יום שני'; // Translation = Today is Monday + $section->addText($txt, $stnam); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $element = '/w:document/w:body/w:p/w:r'; + $txtelem = $element . '/w:t'; + $styelem = $element . '/w:rPr'; + $this->assertTrue($doc->elementExists($txtelem)); + $this->assertEquals($txt, $doc->getElement($txtelem)->textContent); + $this->assertTrue($doc->elementExists($styelem)); + $this->assertTrue($doc->elementExists($styelem . '/w:rStyle')); + $this->assertEquals($stnam, $doc->getElementAttribute($styelem . '/w:rStyle', 'w:val')); + $this->assertFalse($doc->elementExists($styelem . '/w:rtl')); + } + + public function testNoProof() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $fontStyle = array( + 'noProof' => true, + 'name' => 'Courier New', + 'size' => 8, + ); + $section = $phpWord->addSection(); + $txt = 'spellung error'; + $section->addText($txt, $fontStyle); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $element = '/w:document/w:body/w:p/w:r'; + $txtelem = $element . '/w:t'; + $styelem = $element . '/w:rPr'; + $noproofelem = $styelem . '/w:noProof'; + $this->assertTrue($doc->elementExists($txtelem)); + $this->assertEquals($txt, $doc->getElement($txtelem)->textContent); + $this->assertTrue($doc->elementExists($styelem)); + $this->assertTrue($doc->elementExists($noproofelem)); + $this->assertEquals('1', $doc->getElementAttribute($noproofelem, 'w:val')); + } + /** * Test writing font with language */ From 30e3981ed2a3cae5df4b5902541ac69d0b65b3a9 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 5 Dec 2019 21:51:09 -0800 Subject: [PATCH 020/112] Documentation Change Changed 1 doc-block comment suggested by Scrutinizer. --- src/PhpWord/Element/Field.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php index e6777d20..3d1503fe 100644 --- a/src/PhpWord/Element/Field.php +++ b/src/PhpWord/Element/Field.php @@ -117,7 +117,7 @@ class Field extends AbstractElement /** * Font style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|\PhpOffice\PhpWord\Style\Font */ protected $fontStyle; From 7657992a83a272c44ec6f50e868173a8a52c233d Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 22:51:00 -0800 Subject: [PATCH 021/112] Scrutinizer-suggested changes Changes to doc-blocks and code suggested by Scrutinizer. --- src/PhpWord/Writer/RTF/Element/Title.php | 2 +- src/PhpWord/Writer/RTF/Part/Document.php | 2 +- src/PhpWord/Writer/RTF/Style/Paragraph.php | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index 3d5e08ca..d5c8d455 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -26,7 +26,7 @@ class Title extends Text { protected function getStyles() { - /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + /** @var \PhpOffice\PhpWord\Element\Title $element Type hint */ $element = $this->element; $style = $element->getStyle(); if (is_string($style)) { diff --git a/src/PhpWord/Writer/RTF/Part/Document.php b/src/PhpWord/Writer/RTF/Part/Document.php index 5d4d23d9..14d90094 100644 --- a/src/PhpWord/Writer/RTF/Part/Document.php +++ b/src/PhpWord/Writer/RTF/Part/Document.php @@ -117,7 +117,7 @@ class Document extends AbstractPart /** * Write titlepg directive if any "f" headers or footers * - * @param \PhpOffice\PhpWord\PhpWord\Element\Section $section + * @param \PhpOffice\PhpWord\Element\Section $section * @return string */ private static function writeTitlepg($section) diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index 3394f9d4..a9c060ac 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -52,8 +52,8 @@ class Paragraph extends AbstractStyle Jc::END => '\qr', Jc::CENTER => '\qc', Jc::BOTH => '\qj', - Jc::LEFT => '\ql', - Jc::RIGHT => '\qr', + "left" => '\ql', + "right" => '\qr', ); $spaceAfter = $style->getSpaceAfter(); @@ -70,11 +70,11 @@ class Paragraph extends AbstractStyle $content .= $this->getValueIf($spaceBefore !== null, '\sb' . round($spaceBefore)); $content .= $this->getValueIf($spaceAfter !== null, '\sa' . round($spaceAfter)); $lineHeight = $style->getLineHeight(); - if ($lineHeight !== null) { + if ($lineHeight) { $lineHeightAdjusted = (int) ($lineHeight * 240); $content .= "\\sl$lineHeightAdjusted\\slmult1"; } - if ($style->getPageBreakBefore()) { + if ($style->hasPageBreakBefore()) { $content .= '\\page'; } From 5e64b264512265f8bdd3510b86f758be9cffd210 Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 23:24:03 -0800 Subject: [PATCH 022/112] Additional Scrutinizer Recommendations Some more editorial changes. --- src/PhpWord/Writer/RTF/Element/Title.php | 30 ++++++++++------------ src/PhpWord/Writer/RTF/Style/Paragraph.php | 2 -- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/src/PhpWord/Writer/RTF/Element/Title.php b/src/PhpWord/Writer/RTF/Element/Title.php index d5c8d455..77ebff17 100644 --- a/src/PhpWord/Writer/RTF/Element/Title.php +++ b/src/PhpWord/Writer/RTF/Element/Title.php @@ -29,24 +29,22 @@ class Title extends Text /** @var \PhpOffice\PhpWord\Element\Title $element Type hint */ $element = $this->element; $style = $element->getStyle(); - if (is_string($style)) { - $style = str_replace('Heading', 'Heading_', $style); - $style = \PhpOffice\PhpWord\Style::getStyle($style); - if ($style instanceof \PhpOffice\PhpWord\Style\Font) { - $this->fontStyle = $style; - $pstyle = $style->getParagraph(); - if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) { - $sect = $element->getParent(); - if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { - $elems = $sect->getElements(); - if ($elems[0] === $element) { - $pstyle = clone $pstyle; - $pstyle->setPageBreakBefore(false); - } + $style = str_replace('Heading', 'Heading_', $style); + $style = \PhpOffice\PhpWord\Style::getStyle($style); + if ($style instanceof \PhpOffice\PhpWord\Style\Font) { + $this->fontStyle = $style; + $pstyle = $style->getParagraph(); + if ($pstyle instanceof \PhpOffice\PhpWord\Style\Paragraph && $pstyle->hasPageBreakBefore()) { + $sect = $element->getParent(); + if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { + $elems = $sect->getElements(); + if ($elems[0] === $element) { + $pstyle = clone $pstyle; + $pstyle->setPageBreakBefore(false); } } - $this->paragraphStyle = $pstyle; } + $this->paragraphStyle = $pstyle; } } @@ -57,7 +55,7 @@ class Title extends Text */ public function write() { - /** @var \PhpOffice\PhpWord\Element\Text $element Type hint */ + /** @var \PhpOffice\PhpWord\Element\Title $element Type hint */ $element = $this->element; $elementClass = str_replace('\\Writer\\RTF', '', get_class($this)); if (!$element instanceof $elementClass || !is_string($element->getText())) { diff --git a/src/PhpWord/Writer/RTF/Style/Paragraph.php b/src/PhpWord/Writer/RTF/Style/Paragraph.php index a9c060ac..9f8cf9df 100644 --- a/src/PhpWord/Writer/RTF/Style/Paragraph.php +++ b/src/PhpWord/Writer/RTF/Style/Paragraph.php @@ -52,8 +52,6 @@ class Paragraph extends AbstractStyle Jc::END => '\qr', Jc::CENTER => '\qc', Jc::BOTH => '\qj', - "left" => '\ql', - "right" => '\qr', ); $spaceAfter = $style->getSpaceAfter(); From 9b5483a1e075e93218b437acdd1d0862568ef20e Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 5 Dec 2019 23:42:54 -0800 Subject: [PATCH 023/112] Incorporating Pull Request 1771 That Pull Request is "Fix PHPUnit tests on Develop Branch". --- .../AbstractWebServerEmbeddedTest.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..5d1e3c12 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,25 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); From 122aaf17b11cbc69afffbfb8a91d79fb4480f79b Mon Sep 17 00:00:00 2001 From: owen Date: Thu, 5 Dec 2019 23:48:44 -0800 Subject: [PATCH 024/112] Incorporate Pull Request 1771 Fix PHPUnit tests on develop branch --- .../AbstractWebServerEmbeddedTest.php | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..5d1e3c12 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,25 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); From 072c3bfdb327f6984b8bb378b08f83ae2293a708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Dupont?= Date: Mon, 2 Dec 2019 14:23:34 +0100 Subject: [PATCH 025/112] fix: PHPUnit test Process() format \Symfony\Component\Process\Process refuses being passed a string with version > 5, which is installed with PHP > 7.2.5. It also refuses being passed an array with version < 3.3, which is installed with PHP < 5.5.9. Solved by checking if Process::fromShellCommandLine() exists, which was introduced in version 4.2.0. --- .../AbstractWebServerEmbeddedTest.php | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..25fe836a 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,26 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); From 485202874370e7cbd35197735e870eb3ed700f27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Dupont?= Date: Mon, 9 Dec 2019 11:29:18 +0100 Subject: [PATCH 026/112] fix: typo in getFootnoteProperties() method name Was "getFootnotePropoperties()". Former bogus spelling is still working, albeit deprecated. --- src/PhpWord/Element/Section.php | 12 +++++++++++ src/PhpWord/Writer/Word2007/Part/Document.php | 20 +++++++++---------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/PhpWord/Element/Section.php b/src/PhpWord/Element/Section.php index b495ef7b..b6da9f3b 100644 --- a/src/PhpWord/Element/Section.php +++ b/src/PhpWord/Element/Section.php @@ -146,6 +146,18 @@ class Section extends AbstractContainer * * @return FootnoteProperties */ + public function getFootnoteProperties() + { + return $this->footnoteProperties; + } + + /** + * Get the footnote properties + * + * @deprecated Use the `getFootnoteProperties` method instead + * + * @return FootnoteProperties + */ public function getFootnotePropoperties() { return $this->footnoteProperties; diff --git a/src/PhpWord/Writer/Word2007/Part/Document.php b/src/PhpWord/Writer/Word2007/Part/Document.php index 986b4985..e0cabd7e 100644 --- a/src/PhpWord/Writer/Word2007/Part/Document.php +++ b/src/PhpWord/Writer/Word2007/Part/Document.php @@ -126,27 +126,27 @@ class Document extends AbstractPart $xmlWriter->endElement(); } - //footnote properties - if ($section->getFootnotePropoperties() !== null) { + // Footnote properties + if ($section->getFootnoteProperties() !== null) { $xmlWriter->startElement('w:footnotePr'); - if ($section->getFootnotePropoperties()->getPos() != null) { + if ($section->getFootnoteProperties()->getPos() != null) { $xmlWriter->startElement('w:pos'); - $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getPos()); + $xmlWriter->writeAttribute('w:val', $section->getFootnoteProperties()->getPos()); $xmlWriter->endElement(); } - if ($section->getFootnotePropoperties()->getNumFmt() != null) { + if ($section->getFootnoteProperties()->getNumFmt() != null) { $xmlWriter->startElement('w:numFmt'); - $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getNumFmt()); + $xmlWriter->writeAttribute('w:val', $section->getFootnoteProperties()->getNumFmt()); $xmlWriter->endElement(); } - if ($section->getFootnotePropoperties()->getNumStart() != null) { + if ($section->getFootnoteProperties()->getNumStart() != null) { $xmlWriter->startElement('w:numStart'); - $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getNumStart()); + $xmlWriter->writeAttribute('w:val', $section->getFootnoteProperties()->getNumStart()); $xmlWriter->endElement(); } - if ($section->getFootnotePropoperties()->getNumRestart() != null) { + if ($section->getFootnoteProperties()->getNumRestart() != null) { $xmlWriter->startElement('w:numRestart'); - $xmlWriter->writeAttribute('w:val', $section->getFootnotePropoperties()->getNumRestart()); + $xmlWriter->writeAttribute('w:val', $section->getFootnoteProperties()->getNumRestart()); $xmlWriter->endElement(); } $xmlWriter->endElement(); From cb3e2111350e964ce30615d1811f8d083a41b302 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Dupont?= Date: Mon, 9 Dec 2019 11:22:08 +0100 Subject: [PATCH 027/112] fix(documentation): snippet for FootnoteProperties The documentation contained an incorrect code snippet for configuring FootnoteProperties. Now the code is valid. --- docs/elements.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/elements.rst b/docs/elements.rst index 9d446b27..e4974915 100644 --- a/docs/elements.rst +++ b/docs/elements.rst @@ -359,17 +359,17 @@ The footnote numbering can be controlled by setting the FootnoteProperties on th .. code-block:: php - $fp = new PhpWord\SimpleType\FootnoteProperties(); + $fp = new \PhpOffice\PhpWord\ComplexType\FootnoteProperties(); //sets the position of the footnote (pageBottom (default), beneathText, sectEnd, docEnd) - $fp->setPos(FootnoteProperties::POSITION_DOC_END); + $fp->setPos(\PhpOffice\PhpWord\ComplexType\FootnoteProperties::POSITION_BENEATH_TEXT); //set the number format to use (decimal (default), upperRoman, upperLetter, ...) - $fp->setNumFmt(FootnoteProperties::NUMBER_FORMAT_LOWER_ROMAN); + $fp->setNumFmt(\PhpOffice\PhpWord\SimpleType\NumberFormat::LOWER_ROMAN); //force starting at other than 1 $fp->setNumStart(2); //when to restart counting (continuous (default), eachSect, eachPage) - $fp->setNumRestart(FootnoteProperties::RESTART_NUMBER_EACH_PAGE); + $fp->setNumRestart(\PhpOffice\PhpWord\ComplexType\FootnoteProperties::RESTART_NUMBER_EACH_PAGE); //And finaly, set it on the Section - $section->setFootnoteProperties($properties); + $section->setFootnoteProperties($fp); Checkboxes ---------- From fa0ba2e2ababa7f9ea70a25c29513f0e40e9dcf7 Mon Sep 17 00:00:00 2001 From: Ernestas Staugaitis Date: Thu, 2 Jan 2020 00:34:30 +0200 Subject: [PATCH 028/112] Added support for cloud convert image inclusion --- src/PhpWord/Reader/Word2007/AbstractPart.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index bb4a3a49..06dfe37b 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -270,6 +270,10 @@ abstract class AbstractPart $name = $xmlReader->getAttribute('name', $node, 'wp:inline/a:graphic/a:graphicData/pic:pic/pic:nvPicPr/pic:cNvPr'); $embedId = $xmlReader->getAttribute('r:embed', $node, 'wp:inline/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip'); + if ($name === null && $embedId === null) { // some Converters puts images on a different path + $name = $xmlReader->getAttribute('name', $node, 'wp:anchor/a:graphic/a:graphicData/pic:pic/pic:nvPicPr/pic:cNvPr'); + $embedId = $xmlReader->getAttribute('r:embed', $node, 'wp:anchor/a:graphic/a:graphicData/pic:pic/pic:blipFill/a:blip'); + } $target = $this->getMediaTarget($docPart, $embedId); if (!is_null($target)) { $imageSource = "zip://{$this->docFile}#{$target}"; From e24b2e1ba78f828c79db5b8538c4cc4934c4b560 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Sun, 5 Jan 2020 10:27:45 -0800 Subject: [PATCH 029/112] ODT Changes Implement a number of features implemented in PhpWord, but not yet supported in PhpWord ODT Writer. 1. Add default file to tests/PhpWord/_includes/XmlDocument.php to make it considerably easier to test ODT changes (and Word2007 changes involving files other that document.xml). 2. Page break before each section. 3. Page numbering start. 4. Font style for Headings. 5. Alignment for images. 6. Paragraph style for TextRun. 7. "Hide grammatical errors" for whole document. 8. Page layout for each section. 9. For each page layout, support user-specified page width, page height, orientation, margin top, margin bottom, margin left, margin right. 10. Page header and footer. 11. Named colors. 12. NoProof font style. 13. Paragraph Style - spaceBefore, spaceAfter, lineHeight, pageBreakBefore, indentation, text alignment. 14. Tab stops. 15. Basic support for some Fields (DATE, PAGE, NUMPAGES). 16. Link had an error in how it was handling internal links (needs leading #). 17. In addition to tests for all the above, added some tests for Tables. Item 11 above needs 1 module from Pull Request 1775, which is targeted for v0.18.0 but not yet merged, so the relevant module is also here. Item 15 above needs 1 module from Pull Request 1774, which is targeted for v0.18.0 but not yet merged, so the relevant module is also here. Testing change from Pull Request 1771 is included here, but was merged after my fork. --- src/PhpWord/Element/Field.php | 40 +- src/PhpWord/Shared/Converter.php | 46 ++ src/PhpWord/Writer/ODText/Element/Field.php | 81 ++++ src/PhpWord/Writer/ODText/Element/Image.php | 2 +- src/PhpWord/Writer/ODText/Element/Link.php | 2 +- .../Writer/ODText/Element/PageBreak.php | 2 +- src/PhpWord/Writer/ODText/Element/Text.php | 48 +- src/PhpWord/Writer/ODText/Element/TextRun.php | 7 +- src/PhpWord/Writer/ODText/Element/Title.php | 19 +- src/PhpWord/Writer/ODText/Part/Content.php | 109 ++++- src/PhpWord/Writer/ODText/Part/Styles.php | 143 +++++- src/PhpWord/Writer/ODText/Style/Font.php | 19 +- src/PhpWord/Writer/ODText/Style/Paragraph.php | 113 ++++- .../Writer/ODText/Element/ImageTest.php | 66 +++ tests/PhpWord/Writer/ODText/ElementTest.php | 174 ++++++- .../Writer/ODText/Part/ContentTest.php | 2 +- .../PhpWord/Writer/ODText/Style/FontTest.php | 131 ++++++ .../Writer/ODText/Style/ParagraphTest.php | 434 ++++++++++++++++++ .../Writer/ODText/Style/SectionTest.php | 249 ++++++++++ tests/PhpWord/_includes/TestHelperDOCX.php | 7 +- tests/PhpWord/_includes/XmlDocument.php | 62 ++- 21 files changed, 1679 insertions(+), 77 deletions(-) create mode 100644 src/PhpWord/Writer/ODText/Element/Field.php create mode 100644 tests/PhpWord/Writer/ODText/Element/ImageTest.php create mode 100644 tests/PhpWord/Writer/ODText/Style/FontTest.php create mode 100644 tests/PhpWord/Writer/ODText/Style/ParagraphTest.php create mode 100644 tests/PhpWord/Writer/ODText/Style/SectionTest.php diff --git a/src/PhpWord/Element/Field.php b/src/PhpWord/Element/Field.php index 2efc6b0b..3d1503fe 100644 --- a/src/PhpWord/Element/Field.php +++ b/src/PhpWord/Element/Field.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Element; +use PhpOffice\PhpWord\Style\Font; + /** * Field element * @@ -115,10 +117,42 @@ class Field extends AbstractElement /** * Font style * - * @var \PhpOffice\PhpWord\Style\Font + * @var string|\PhpOffice\PhpWord\Style\Font */ protected $fontStyle; + /** + * Set Font style + * + * @param string|array|\PhpOffice\PhpWord\Style\Font $style + * @return string|\PhpOffice\PhpWord\Style\Font + */ + public function setFontStyle($style = null) + { + if ($style instanceof Font) { + $this->fontStyle = $style; + } elseif (is_array($style)) { + $this->fontStyle = new Font('text'); + $this->fontStyle->setStyleByArray($style); + } elseif (null === $style) { + $this->fontStyle = null; + } else { + $this->fontStyle = $style; + } + + return $this->fontStyle; + } + + /** + * Get Font style + * + * @return string|\PhpOffice\PhpWord\Style\Font + */ + public function getFontStyle() + { + return $this->fontStyle; + } + /** * Create a new Field Element * @@ -126,13 +160,15 @@ class Field extends AbstractElement * @param array $properties * @param array $options * @param TextRun|string|null $text + * @param string|array|\PhpOffice\PhpWord\Style\Font $fontStyle */ - public function __construct($type = null, $properties = array(), $options = array(), $text = null) + public function __construct($type = null, $properties = array(), $options = array(), $text = null, $fontStyle = null) { $this->setType($type); $this->setProperties($properties); $this->setOptions($options); $this->setText($text); + $this->setFontStyle($fontStyle); } /** diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 7008ac5d..9206a3bc 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -272,6 +272,50 @@ class Converter return round($angle / self::DEGREE_TO_ANGLE); } + /** + * Convert colorname as string to RGB + * + * @param string $value color name + * @return string color as hex RGB string, or original value if unknown + */ + public static function stringToRgb($value) + { + switch ($value) { + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_YELLOW: + return 'FFFF00'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGREEN: + return '90EE90'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_CYAN: + return '00FFFF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_MAGENTA: + return 'FF00FF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLUE: + return '0000FF'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_RED: + return 'FF0000'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKBLUE: + return '00008B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKCYAN: + return '008B8B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGREEN: + return '006400'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKMAGENTA: + return '8B008B'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKRED: + return '8B0000'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKYELLOW: + return '8B8B00'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGRAY: + return 'A9A9A9'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_LIGHTGRAY: + return 'D3D3D3'; + case \PhpOffice\PhpWord\Style\Font::FGCOLOR_BLACK: + return '000000'; + } + + return $value; + } + /** * Convert HTML hexadecimal to RGB * @@ -282,6 +326,8 @@ class Converter { if ($value[0] == '#') { $value = substr($value, 1); + } else { + $value = self::stringToRgb($value); } if (strlen($value) == 6) { diff --git a/src/PhpWord/Writer/ODText/Element/Field.php b/src/PhpWord/Writer/ODText/Element/Field.php new file mode 100644 index 00000000..c95139ac --- /dev/null +++ b/src/PhpWord/Writer/ODText/Element/Field.php @@ -0,0 +1,81 @@ +getElement(); + if (!$element instanceof \PhpOffice\PhpWord\Element\Field) { + return; + } + + $type = strtolower($element->getType()); + switch ($type) { + case 'date': // Owen 2020-01-02 + case 'page': + case 'numpages': + $this->writeDefault($element, $type); + break; + } + } + + private function writeDefault(\PhpOffice\PhpWord\Element\Field $element, $type) + { + $xmlWriter = $this->getXmlWriter(); + + $xmlWriter->startElement('text:span'); + if (method_exists($element, 'getFontStyle')) { + $fstyle = $element->getFontStyle(); + if (is_string($fstyle)) { + $xmlWriter->writeAttribute('text:style-name', $fstyle); + } + } + switch ($type) { + case 'date': // Owen 2019-01-02 + $xmlWriter->startElement('text:date'); + $xmlWriter->writeAttribute('text:fixed', 'false'); + $xmlWriter->endElement(); + break; + case 'page': + $xmlWriter->startElement('text:page-number'); + $xmlWriter->writeAttribute('text:fixed', 'false'); + $xmlWriter->endElement(); + break; + case 'numpages': + $xmlWriter->startElement('text:page-count'); + $xmlWriter->endElement(); + break; + } + $xmlWriter->endElement(); // text:span + } +} diff --git a/src/PhpWord/Writer/ODText/Element/Image.php b/src/PhpWord/Writer/ODText/Element/Image.php index add45e10..57aa546a 100644 --- a/src/PhpWord/Writer/ODText/Element/Image.php +++ b/src/PhpWord/Writer/ODText/Element/Image.php @@ -44,7 +44,7 @@ class Image extends AbstractElement $height = Converter::pixelToCm($style->getHeight()); $xmlWriter->startElement('text:p'); - $xmlWriter->writeAttribute('text:style-name', 'Standard'); + $xmlWriter->writeAttribute('text:style-name', 'IM' . $mediaIndex); $xmlWriter->startElement('draw:frame'); $xmlWriter->writeAttribute('draw:style-name', 'fr' . $mediaIndex); diff --git a/src/PhpWord/Writer/ODText/Element/Link.php b/src/PhpWord/Writer/ODText/Element/Link.php index d6fec507..23c9804b 100644 --- a/src/PhpWord/Writer/ODText/Element/Link.php +++ b/src/PhpWord/Writer/ODText/Element/Link.php @@ -41,7 +41,7 @@ class Link extends AbstractElement $xmlWriter->startElement('text:a'); $xmlWriter->writeAttribute('xlink:type', 'simple'); - $xmlWriter->writeAttribute('xlink:href', $element->getSource()); + $xmlWriter->writeAttribute('xlink:href', ($element->isInternal() ? '#' : '') . $element->getSource()); $this->writeText($element->getText()); $xmlWriter->endElement(); // text:a diff --git a/src/PhpWord/Writer/ODText/Element/PageBreak.php b/src/PhpWord/Writer/ODText/Element/PageBreak.php index ecf47607..8e4f4695 100644 --- a/src/PhpWord/Writer/ODText/Element/PageBreak.php +++ b/src/PhpWord/Writer/ODText/Element/PageBreak.php @@ -30,7 +30,7 @@ class PageBreak extends AbstractElement $xmlWriter = $this->getXmlWriter(); $xmlWriter->startElement('text:p'); - $xmlWriter->writeAttribute('text:style-name', 'P1'); + $xmlWriter->writeAttribute('text:style-name', 'PB'); $xmlWriter->endElement(); } } diff --git a/src/PhpWord/Writer/ODText/Element/Text.php b/src/PhpWord/Writer/ODText/Element/Text.php index 7dcd28a0..2bf9908d 100644 --- a/src/PhpWord/Writer/ODText/Element/Text.php +++ b/src/PhpWord/Writer/ODText/Element/Text.php @@ -59,18 +59,26 @@ class Text extends AbstractElement } else { if (empty($fontStyle)) { if (empty($paragraphStyle)) { - $xmlWriter->writeAttribute('text:style-name', 'P1'); + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', 'Normal'); + } } elseif (is_string($paragraphStyle)) { - $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + } } $this->writeChangeInsertion(true, $element->getTrackChange()); - $this->writeText($element->getText()); + $this->replaceTabs($element->getText(), $xmlWriter); $this->writeChangeInsertion(false, $element->getTrackChange()); } else { if (empty($paragraphStyle)) { - $xmlWriter->writeAttribute('text:style-name', 'Standard'); + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', 'Normal'); + } } elseif (is_string($paragraphStyle)) { - $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + if (!$this->withoutP) { + $xmlWriter->writeAttribute('text:style-name', $paragraphStyle); + } } // text:span $xmlWriter->startElement('text:span'); @@ -78,7 +86,7 @@ class Text extends AbstractElement $xmlWriter->writeAttribute('text:style-name', $fontStyle); } $this->writeChangeInsertion(true, $element->getTrackChange()); - $this->writeText($element->getText()); + $this->replaceTabs($element->getText(), $xmlWriter); $this->writeChangeInsertion(false, $element->getTrackChange()); $xmlWriter->endElement(); } @@ -88,6 +96,34 @@ class Text extends AbstractElement } } + private function replacetabs($text, $xmlWriter) + { + if (preg_match('/^ +/', $text, $matches)) { + $num = strlen($matches[0]); + $xmlWriter->startElement('text:s'); + $xmlWriter->writeAttributeIf($num > 1, 'text:c', "$num"); + $xmlWriter->endElement(); + $text = preg_replace('/^ +/', '', $text); + } + preg_match_all('/([\\s\\S]*?)(\\t| +| ?$)/', $text, $matches, PREG_SET_ORDER); + foreach ($matches as $match) { + $this->writeText($match[1]); + if ($match[2] === '') { + break; + } elseif ($match[2] === "\t") { + $xmlWriter->writeElement('text:tab'); + } elseif ($match[2] === ' ') { + $xmlWriter->writeElement('text:s'); + break; + } else { + $num = strlen($match[2]); + $xmlWriter->startElement('text:s'); + $xmlWriter->writeAttributeIf($num > 1, 'text:c', "$num"); + $xmlWriter->endElement(); + } + } + } + private function writeChangeInsertion($start = true, TrackChange $trackChange = null) { if ($trackChange == null || $trackChange->getChangeType() != TrackChange::INSERTED) { diff --git a/src/PhpWord/Writer/ODText/Element/TextRun.php b/src/PhpWord/Writer/ODText/Element/TextRun.php index 78e5a8ad..cde996f6 100644 --- a/src/PhpWord/Writer/ODText/Element/TextRun.php +++ b/src/PhpWord/Writer/ODText/Element/TextRun.php @@ -22,7 +22,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Element; * * @since 0.10.0 */ -class TextRun extends AbstractElement +class TextRun extends Text { /** * Write element @@ -33,6 +33,11 @@ class TextRun extends AbstractElement $element = $this->getElement(); $xmlWriter->startElement('text:p'); + $pStyle = $element->getParagraphStyle(); + if (!is_string($pStyle)) { + $pStyle = 'Normal'; + } + $xmlWriter->writeAttribute('text:style-name', $pStyle); $containerWriter = new Container($xmlWriter, $element); $containerWriter->write(); diff --git a/src/PhpWord/Writer/ODText/Element/Title.php b/src/PhpWord/Writer/ODText/Element/Title.php index 8b9440ab..99153b5e 100644 --- a/src/PhpWord/Writer/ODText/Element/Title.php +++ b/src/PhpWord/Writer/ODText/Element/Title.php @@ -36,7 +36,23 @@ class Title extends AbstractElement } $xmlWriter->startElement('text:h'); - $xmlWriter->writeAttribute('text:outline-level', $element->getDepth()); + $hdname = 'HD'; + $sect = $element->getParent(); + if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { + $elems = $sect->getElements(); + if ($elems[0] === $element) { + $hdname = 'HE'; + } + } + $depth = $element->getDepth(); + $xmlWriter->writeAttribute('text:style-name', "$hdname$depth"); + $xmlWriter->writeAttribute('text:outline-level', $depth); + $xmlWriter->startElement('text:span'); + if ($depth > 0) { + $xmlWriter->writeAttribute('text:style-name', 'Heading_' . $depth); + } else { + $xmlWriter->writeAttribute('text:style-name', 'Title'); + } $text = $element->getText(); if (is_string($text)) { $this->writeText($text); @@ -44,6 +60,7 @@ class Title extends AbstractElement $containerWriter = new Container($xmlWriter, $text); $containerWriter->write(); } + $xmlWriter->endElement(); // text:span $xmlWriter->endElement(); // text:h } } diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index 99ee9353..8eaad40f 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -46,6 +46,7 @@ class Content extends AbstractPart * @var array */ private $autoStyles = array('Section' => array(), 'Image' => array(), 'Table' => array()); + private $imageParagraphStyles = array(); /** * Write part @@ -128,6 +129,9 @@ class Content extends AbstractPart $xmlWriter->startElement('text:section'); $xmlWriter->writeAttribute('text:name', $name); $xmlWriter->writeAttribute('text:style-name', $name); + $xmlWriter->startElement('text:p'); + $xmlWriter->writeAttribute('text:style-name', 'SB' . $section->getSectionId()); + $xmlWriter->endElement(); $containerWriter = new Container($xmlWriter, $section); $containerWriter->write(); $xmlWriter->endElement(); // text:section @@ -174,28 +178,58 @@ class Content extends AbstractPart { $styles = Style::getStyles(); $paragraphStyleCount = 0; - if (count($styles) > 0) { - foreach ($styles as $style) { - if ($style->isAuto() === true) { - $styleClass = str_replace('\\Style\\', '\\Writer\\ODText\\Style\\', get_class($style)); - if (class_exists($styleClass)) { - /** @var \PhpOffice\PhpWord\Writer\ODText\Style\AbstractStyle $styleWriter Type hint */ - $styleWriter = new $styleClass($xmlWriter, $style); - $styleWriter->write(); - } - if ($style instanceof Paragraph) { - $paragraphStyleCount++; - } - } - } - if ($paragraphStyleCount == 0) { + + $style = new Paragraph(); + $style->setStyleName('PB'); + $style->setAuto(); + $styleWriter = new ParagraphStyleWriter($xmlWriter, $style); + $styleWriter->write(); + + $sects = $this->getParentWriter()->getPhpWord()->getSections(); + for ($i = 0; $i < count($sects); ++$i) { + $iplus1 = $i + 1; + $style = new Paragraph(); + $style->setStyleName("SB$iplus1"); + $style->setAuto(); + $pnstart = $sects[$i]->getStyle()->getPageNumberingStart(); + $style->setNumLevel($pnstart); + $styleWriter = new ParagraphStyleWriter($xmlWriter, $style); + $styleWriter->write(); + } + + foreach ($styles as $style) { + $sty = $style->getStyleName(); + if (substr($sty, 0, 8) === 'Heading_') { $style = new Paragraph(); - $style->setStyleName('P1'); + $style->setStyleName('HD' . substr($sty, 8)); + $style->setAuto(); + $styleWriter = new ParagraphStyleWriter($xmlWriter, $style); + $styleWriter->write(); + $style = new Paragraph(); + $style->setStyleName('HE' . substr($sty, 8)); $style->setAuto(); $styleWriter = new ParagraphStyleWriter($xmlWriter, $style); $styleWriter->write(); } } + + foreach ($styles as $style) { + if ($style->isAuto() === true) { + $styleClass = str_replace('\\Style\\', '\\Writer\\ODText\\Style\\', get_class($style)); + if (class_exists($styleClass)) { + /** @var \PhpOffice\PhpWord\Writer\ODText\Style\AbstractStyle $styleWriter Type hint */ + $styleWriter = new $styleClass($xmlWriter, $style); + $styleWriter->write(); + } + if ($style instanceof Paragraph) { + $paragraphStyleCount++; + } + } + } + foreach ($this->imageParagraphStyles as $style) { + $styleWriter = new \PhpOffice\PhpWord\Writer\ODText\Style\Paragraph($xmlWriter, $style); + $styleWriter->write(); + } } /** @@ -231,6 +265,7 @@ class Content extends AbstractPart $elements = $container->getElements(); foreach ($elements as $element) { if ($element instanceof TextRun) { + $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); $this->getContainerStyle($element, $paragraphStyleCount, $fontStyleCount); } elseif ($element instanceof Text) { $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); @@ -238,13 +273,19 @@ class Content extends AbstractPart $style = $element->getStyle(); $style->setStyleName('fr' . $element->getMediaIndex()); $this->autoStyles['Image'][] = $style; + $sty = new \PhpOffice\PhpWord\Style\Paragraph(); + $sty->setStyleName('IM' . $element->getMediaIndex()); + $sty->setAuto(); + $sty->setAlignment($style->getAlignment()); + $this->imageParagraphStyles[] = $sty; } elseif ($element instanceof Table) { /** @var \PhpOffice\PhpWord\Style\Table $style */ $style = $element->getStyle(); + if (is_string($style)) { + $style = Style::getStyle($style); + } if ($style === null) { $style = new TableStyle(); - } elseif (is_string($style)) { - $style = Style::getStyle($style); } $style->setStyleName($element->getElementId()); $style->setColumnWidths($element->findFirstDefinedCellWidths()); @@ -268,16 +309,34 @@ class Content extends AbstractPart if ($fontStyle instanceof Font) { // Font - $fontStyleCount++; - $style = $phpWord->addFontStyle("T{$fontStyleCount}", $fontStyle); - $style->setAuto(); - $element->setFontStyle("T{$fontStyleCount}"); - } elseif ($paragraphStyle instanceof Paragraph) { + $name = $fontStyle->getStyleName(); + if (!$name) { + $fontStyleCount++; + $style = $phpWord->addFontStyle("T{$fontStyleCount}", $fontStyle, null); + $style->setAuto(); + $style->setParagraph(null); + $element->setFontStyle("T{$fontStyleCount}"); + } else { + $element->setFontStyle($name); + } + } + if ($paragraphStyle instanceof Paragraph) { // Paragraph + $name = $paragraphStyle->getStyleName(); + if (!$name) { + $paragraphStyleCount++; + $style = $phpWord->addParagraphStyle("P{$paragraphStyleCount}", $paragraphStyle); + $style->setAuto(); + $element->setParagraphStyle("P{$paragraphStyleCount}"); + } else { + $element->setParagraphStyle($name); + } + } elseif (is_string($paragraphStyle)) { $paragraphStyleCount++; - $style = $phpWord->addParagraphStyle("P{$paragraphStyleCount}", array()); + $parstylename = "P$paragraphStyleCount" . "_$paragraphStyle"; + $style = $phpWord->addParagraphStyle($parstylename, $paragraphStyle); $style->setAuto(); - $element->setParagraphStyle("P{$paragraphStyleCount}"); + $element->setParagraphStyle($parstylename); } } diff --git a/src/PhpWord/Writer/ODText/Part/Styles.php b/src/PhpWord/Writer/ODText/Part/Styles.php index e7635e98..862f4b2a 100644 --- a/src/PhpWord/Writer/ODText/Part/Styles.php +++ b/src/PhpWord/Writer/ODText/Part/Styles.php @@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Shared\Converter; use PhpOffice\PhpWord\Style; /** @@ -86,6 +87,9 @@ class Styles extends AbstractPart $latinLang = $language != null && is_string($language->getLatin()) ? explode('-', $language->getLatin()) : array('fr', 'FR'); $asianLang = $language != null && is_string($language->getEastAsia()) ? explode('-', $language->getEastAsia()) : array('zh', 'CN'); $complexLang = $language != null && is_string($language->getBidirectional()) ? explode('-', $language->getBidirectional()) : array('hi', 'IN'); + if ($this->getParentWriter()->getPhpWord()->getSettings()->hasHideGrammaticalErrors()) { + $latinLang = $asianLang = $complexLang = array('zxx', 'none'); + } // Font $xmlWriter->startElement('style:text-properties'); @@ -134,24 +138,87 @@ class Styles extends AbstractPart } /** - * Write page layout styles. + * Convert int in twips to inches/cm then to string and append unit + * + * @param int $twips + * @param string $dflt + * @param float $factor + * return string + */ + private static function cvttwiptostr($twips, $dflt, $factor = 1.0) // Owen 2019-08-06 + { + if ($twips === null) { + return $dflt; + } + $ins = (string) ($twips * $factor / Converter::INCH_TO_TWIP) . 'in'; + $cms = (string) ($twips * $factor * Converter::INCH_TO_CM / Converter::INCH_TO_TWIP) . 'cm'; + + return (strlen($ins) < strlen($cms)) ? $ins : $cms; + } + + /** + * call writePageLayoutIndiv to write page layout styles for each page * * @param \PhpOffice\Common\XMLWriter $xmlWriter */ - private function writePageLayout(XMLWriter $xmlWriter) + private function writePageLayout(XMLWriter $xmlWriter) // Owen 2019-06-19 { + $sections = $this->getParentWriter()->getPhpWord()->getSections(); + for ($i = 0; $i < count($sections); ++$i) { + $this->writePageLayoutIndiv($xmlWriter, $sections[$i], $i + 1); + } + } + + /** + * Write page layout styles. + * + * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Element\Section $section + * @param int $sectionNbr + */ + private function writePageLayoutIndiv(XMLWriter $xmlWriter, $section, $sectionNbr) + { + $sty = $section->getStyle(); + if (count($section->getHeaders()) > 0) { + $topfactor = 0.5; + } else { + $topfactor = 1.0; + } + if (count($section->getFooters()) > 0) { + $botfactor = 0.5; + } else { + $botfactor = 1.0; + } + $pwidth = '21.001cm'; + $pheight = '29.7cm'; + $orient = 'portrait'; + $mtop = $mleft = $mright = '2.501cm'; + $mbottom = '2cm'; + if ($sty instanceof \PhpOffice\PhpWord\Style\Section) { + $ori = $sty->getOrientation(); + if ($ori !== null) { + $orient = $ori; + } + $pwidth = self::cvttwiptostr($sty->getPageSizeW(), $pwidth); + $pheight = self::cvttwiptostr($sty->getPageSizeH(), $pheight); + $mtop = self::cvttwiptostr($sty->getMarginTop(), $mtop, $topfactor); + $mbottom = self::cvttwiptostr($sty->getMarginBottom(), $mbottom, $botfactor); + $mleft = self::cvttwiptostr($sty->getMarginRight(), $mleft); + $mright = self::cvttwiptostr($sty->getMarginLeft(), $mright); + } + $xmlWriter->startElement('style:page-layout'); - $xmlWriter->writeAttribute('style:name', 'Mpm1'); + $xmlWriter->writeAttribute('style:name', "Mpm$sectionNbr"); $xmlWriter->startElement('style:page-layout-properties'); - $xmlWriter->writeAttribute('fo:page-width', '21.001cm'); - $xmlWriter->writeAttribute('fo:page-height', '29.7cm'); + $xmlWriter->writeAttribute('fo:page-width', $pwidth); + $xmlWriter->writeAttribute('fo:page-height', $pheight); $xmlWriter->writeAttribute('style:num-format', '1'); - $xmlWriter->writeAttribute('style:print-orientation', 'portrait'); - $xmlWriter->writeAttribute('fo:margin-top', '2.501cm'); - $xmlWriter->writeAttribute('fo:margin-bottom', '2cm'); - $xmlWriter->writeAttribute('fo:margin-left', '2.501cm'); - $xmlWriter->writeAttribute('fo:margin-right', '2.501cm'); + $xmlWriter->writeAttribute('style:print-orientation', $orient); + $xmlWriter->writeAttribute('fo:margin-top', $mtop); + $xmlWriter->writeAttribute('fo:margin-bottom', $mbottom); + $xmlWriter->writeAttribute('fo:margin-left', $mleft); + $xmlWriter->writeAttribute('fo:margin-right', $mright); $xmlWriter->writeAttribute('style:writing-mode', 'lr-tb'); $xmlWriter->writeAttribute('style:layout-grid-color', '#c0c0c0'); $xmlWriter->writeAttribute('style:layout-grid-lines', '25199'); @@ -176,9 +243,23 @@ class Styles extends AbstractPart $xmlWriter->endElement(); // style:page-layout-properties $xmlWriter->startElement('style:header-style'); + if ($topfactor < 1.0) { + $xmlWriter->startElement('style:header-footer-properties'); + $xmlWriter->writeAttribute('fo:min-height', $mtop); + $xmlWriter->writeAttribute('fo:margin-bottom', $mtop); + $xmlWriter->writeAttribute('style:dynamic-spacing', 'true'); + $xmlWriter->endElement(); // style:header-footer-properties + } $xmlWriter->endElement(); // style:header-style $xmlWriter->startElement('style:footer-style'); + if ($botfactor < 1.0) { // Owen 2019-08-03 + $xmlWriter->startElement('style:header-footer-properties'); + $xmlWriter->writeAttribute('fo:min-height', $mbottom); + $xmlWriter->writeAttribute('fo:margin-top', $mbottom); + $xmlWriter->writeAttribute('style:dynamic-spacing', 'true'); + $xmlWriter->endElement(); // style:header-footer-properties + } $xmlWriter->endElement(); // style:footer-style $xmlWriter->endElement(); // style:page-layout @@ -193,11 +274,43 @@ class Styles extends AbstractPart { $xmlWriter->startElement('office:master-styles'); - $xmlWriter->startElement('style:master-page'); - $xmlWriter->writeAttribute('style:name', 'Standard'); - $xmlWriter->writeAttribute('style:page-layout-name', 'Mpm1'); - $xmlWriter->endElement(); // style:master-page - + $sections = $this->getParentWriter()->getPhpWord()->getSections(); + for ($i = 0; $i < count($sections); ++$i) { + $iplus1 = $i + 1; + $xmlWriter->startElement('style:master-page'); + $xmlWriter->writeAttribute('style:name', "Standard$iplus1"); + $xmlWriter->writeAttribute('style:page-layout-name', "Mpm$iplus1"); + // Multiple headers and footers probably not supported, + // and, even if they are, I'm not sure how, + // so quit after generating one. + foreach ($sections[$i]->getHeaders() as $hdr) { + $xmlWriter->startElement('style:header'); + foreach ($hdr->getElements() as $elem) { + $cl1 = get_class($elem); + $cl2 = str_replace('\\Element\\', '\\Writer\\ODText\\Element\\', $cl1); + if (class_exists($cl2)) { + $wtr = new $cl2($xmlWriter, $elem); + $wtr->write(); + } + } + $xmlWriter->endElement(); // style:header + break; + } + foreach ($sections[$i]->getFooters() as $hdr) { + $xmlWriter->startElement('style:footer'); + foreach ($hdr->getElements() as $elem) { + $cl1 = get_class($elem); + $cl2 = str_replace('\\Element\\', '\\Writer\\ODText\\Element\\', $cl1); + if (class_exists($cl2)) { + $wtr = new $cl2($xmlWriter, $elem); + $wtr->write(); + } + } + $xmlWriter->endElement(); // style:footer + break; + } + $xmlWriter->endElement(); // style:master-page + } $xmlWriter->endElement(); // office:master-styles } } diff --git a/src/PhpWord/Writer/ODText/Style/Font.php b/src/PhpWord/Writer/ODText/Style/Font.php index 29657c5a..ae9c417e 100644 --- a/src/PhpWord/Writer/ODText/Style/Font.php +++ b/src/PhpWord/Writer/ODText/Style/Font.php @@ -35,6 +35,14 @@ class Font extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); + $stylep = (method_exists($style, 'getParagraph')) ? $style->getParagraph() : null; + if ($stylep instanceof \PhpOffice\PhpWord\Style\Paragraph) { + $temp1 = clone $stylep; + $temp1->setStyleName($style->getStyleName()); + $temp2 = new \PhpOffice\PhpWord\Writer\ODText\Style\Paragraph($xmlWriter, $temp1); + $temp2->write(); + } + $xmlWriter->startElement('style:style'); $xmlWriter->writeAttribute('style:name', $style->getStyleName()); $xmlWriter->writeAttribute('style:family', 'text'); @@ -53,7 +61,7 @@ class Font extends AbstractStyle // Color $color = $style->getColor(); - $xmlWriter->writeAttributeIf($color != '', 'fo:color', '#' . $color); + $xmlWriter->writeAttributeIf($color != '', 'fo:color', '#' . \PhpOffice\PhpWord\Shared\Converter::stringToRgb($color)); // Bold & italic $xmlWriter->writeAttributeIf($style->isBold(), 'fo:font-weight', 'bold'); @@ -82,6 +90,15 @@ class Font extends AbstractStyle $xmlWriter->writeAttributeIf($style->isSuperScript(), 'style:text-position', 'super'); $xmlWriter->writeAttributeIf($style->isSubScript(), 'style:text-position', 'sub'); + if ($style->isNoProof()) { + $xmlWriter->writeAttribute('fo:language', 'zxx'); + $xmlWriter->writeAttribute('style:language-asian', 'zxx'); + $xmlWriter->writeAttribute('style:language-complex', 'zxx'); + $xmlWriter->writeAttribute('fo:country', 'none'); + $xmlWriter->writeAttribute('style:country-asian', 'none'); + $xmlWriter->writeAttribute('style:country-complex', 'none'); + } + // @todo Foreground-Color // @todo Background color diff --git a/src/PhpWord/Writer/ODText/Style/Paragraph.php b/src/PhpWord/Writer/ODText/Style/Paragraph.php index f247dcc1..555a4825 100644 --- a/src/PhpWord/Writer/ODText/Style/Paragraph.php +++ b/src/PhpWord/Writer/ODText/Style/Paragraph.php @@ -17,6 +17,8 @@ namespace PhpOffice\PhpWord\Writer\ODText\Style; +use PhpOffice\PhpWord\Shared\Converter; + /** * Font style writer * @@ -35,31 +37,120 @@ class Paragraph extends AbstractStyle } $xmlWriter = $this->getXmlWriter(); - $marginTop = (is_null($style->getSpaceBefore()) || $style->getSpaceBefore() == 0) ? '0' : round(17.6 / $style->getSpaceBefore(), 2); - $marginBottom = (is_null($style->getSpaceAfter()) || $style->getSpaceAfter() == 0) ? '0' : round(17.6 / $style->getSpaceAfter(), 2); + $marginTop = $style->getSpaceBefore(); + $marginBottom = $style->getSpaceAfter(); $xmlWriter->startElement('style:style'); + + $styleName = $style->getStyleName(); + $styleAuto = false; + $mpm = ''; + $psm = ''; + $pagestart = -1; + $breakafter = $breakbefore = $breakauto = false; + if ($style->isAuto()) { + if (substr($styleName, 0, 2) === 'PB') { + $styleAuto = true; + $breakafter = true; + } elseif (substr($styleName, 0, 2) === 'SB') { + $styleAuto = true; + $mpm = 'Standard' . substr($styleName, 2); + $psn = $style->getNumLevel(); + if (is_numeric($psn)) { + $pagestart = (int) $psn; + } + } elseif (substr($styleName, 0, 2) === 'HD') { + $styleAuto = true; + $psm = 'Heading_' . substr($styleName, 2); + $stylep = \PhpOffice\PhpWord\Style::getStyle($psm); + if ($stylep instanceof \PhpOffice\PhpWord\Style\Font) { + if (method_exists($stylep, 'getParagraph')) { + $stylep = $stylep->getParagraph(); + } + } + if ($stylep instanceof \PhpOffice\PhpWord\Style\Paragraph) { + if ($stylep->hasPageBreakBefore()) { + $breakbefore = true; + } + } + } elseif (substr($styleName, 0, 2) === 'HE') { + $styleAuto = true; + $psm = 'Heading_' . substr($styleName, 2); + $breakauto = true; + } else { + $styleAuto = true; + $psm = 'Normal'; + if (preg_match('/^P\\d+_(\\w+)$/', $styleName, $matches)) { + $psm = $matches[1]; + } + } + } + $xmlWriter->writeAttribute('style:name', $style->getStyleName()); $xmlWriter->writeAttribute('style:family', 'paragraph'); - if ($style->isAuto()) { - $xmlWriter->writeAttribute('style:parent-style-name', 'Standard'); - $xmlWriter->writeAttribute('style:master-page-name', 'Standard'); + if ($styleAuto) { + $xmlWriter->writeAttributeIf($psm !== '', 'style:parent-style-name', $psm); + $xmlWriter->writeAttributeIf($mpm !== '', 'style:master-page-name', $mpm); } $xmlWriter->startElement('style:paragraph-properties'); - if ($style->isAuto()) { - $xmlWriter->writeAttribute('style:page-number', 'auto'); - } else { - $xmlWriter->writeAttribute('fo:margin-top', $marginTop . 'cm'); - $xmlWriter->writeAttribute('fo:margin-bottom', $marginBottom . 'cm'); - $xmlWriter->writeAttribute('fo:text-align', $style->getAlignment()); + if ($styleAuto) { + if ($breakafter) { + $xmlWriter->writeAttribute('fo:break-after', 'page'); + $xmlWriter->writeAttribute('fo:margin-top', '0cm'); + $xmlWriter->writeAttribute('fo:margin-bottom', '0cm'); + } elseif ($breakbefore) { + $xmlWriter->writeAttribute('fo:break-before', 'page'); + } elseif ($breakauto) { + $xmlWriter->writeAttribute('fo:break-before', 'auto'); + } + if ($pagestart > 0) { + $xmlWriter->writeAttribute('style:page-number', $pagestart); + } + } + if (!$breakafter && !$breakbefore && !$breakauto) { + $twipToPoint = Converter::INCH_TO_TWIP / Converter::INCH_TO_POINT; // 20 + $xmlWriter->writeAttributeIf($marginTop !== null, 'fo:margin-top', ($marginTop / $twipToPoint) . 'pt'); + $xmlWriter->writeAttributeIf($marginBottom !== null, 'fo:margin-bottom', ($marginBottom / $twipToPoint) . 'pt'); + } + $temp = $style->getAlignment(); + $xmlWriter->writeAttributeIf($temp !== '', 'fo:text-align', $temp); + $temp = $style->getLineHeight(); + $xmlWriter->writeAttributeIf($temp !== null, 'fo:line-height', ((string) ($temp * 100) . '%')); + $xmlWriter->writeAttributeIf($style->getPageBreakBefore() === true, 'fo:break-before', 'page'); + + $tabs = $style->getTabs(); + if ($tabs !== null && count($tabs) > 0) { + $xmlWriter->startElement('style:tab-stops'); + foreach ($tabs as $tab) { + $xmlWriter->startElement('style:tab-stop'); + $xmlWriter->writeAttribute('style:type', $tab->getType()); + $xmlWriter->writeAttribute('style:position', (string) ($tab->getPosition() / Converter::INCH_TO_TWIP) . 'in'); + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); } //Right to left $xmlWriter->writeAttributeIf($style->isBidi(), 'style:writing-mode', 'rl-tb'); + //Indentation + $indent = $style->getIndentation(); + if ($indent instanceof \PhpOffice\PhpWord\Style\Indentation) { + $marg = $indent->getLeft(); + $xmlWriter->writeAttributeIf($marg !== null, 'fo:margin-left', (string) ($marg / Converter::INCH_TO_TWIP) . 'in'); + $marg = $indent->getRight(); + $xmlWriter->writeAttributeIf($marg !== null, 'fo:margin-right', (string) ($marg / Converter::INCH_TO_TWIP) . 'in'); + } + $xmlWriter->endElement(); //style:paragraph-properties + if ($styleAuto && substr($styleName, 0, 2) === 'SB') { + $xmlWriter->startElement('style:text-properties'); + $xmlWriter->writeAttribute('text:display', 'none'); + $xmlWriter->endElement(); + } + $xmlWriter->endElement(); //style:style } } diff --git a/tests/PhpWord/Writer/ODText/Element/ImageTest.php b/tests/PhpWord/Writer/ODText/Element/ImageTest.php new file mode 100644 index 00000000..bc861104 --- /dev/null +++ b/tests/PhpWord/Writer/ODText/Element/ImageTest.php @@ -0,0 +1,66 @@ +addSection(); + $section->addImage(__DIR__ . '/../../../_files/images/earth.jpg'); + $section->addImage(__DIR__ . '/../../../_files/images/mario.gif', array('align' => 'end')); + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $element = "$s2a/style:style[3]"; + $this->assertEquals('IM1', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:text-align')); + $element = "$s2a/style:style[4]"; + $this->assertEquals('IM2', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('end', $doc->getElementAttribute($element, 'fo:text-align')); + + $path = '/office:document-content/office:body/office:text/text:section/text:p[2]'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals('IM1', $doc->getElementAttribute($path, 'text:style-name')); + $path = '/office:document-content/office:body/office:text/text:section/text:p[3]'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals('IM2', $doc->getElementAttribute($path, 'text:style-name')); + } +} diff --git a/tests/PhpWord/Writer/ODText/ElementTest.php b/tests/PhpWord/Writer/ODText/ElementTest.php index 37f0d1ef..500ee247 100644 --- a/tests/PhpWord/Writer/ODText/ElementTest.php +++ b/tests/PhpWord/Writer/ODText/ElementTest.php @@ -26,6 +26,14 @@ use PhpOffice\PhpWord\TestHelperDOCX; */ class ElementTest extends \PHPUnit\Framework\TestCase { + /** + * Executed after each method of the class + */ + public function tearDown() + { + TestHelperDOCX::clear(); + } + /** * Test unmatched elements */ @@ -39,10 +47,168 @@ class ElementTest extends \PHPUnit\Framework\TestCase $object = new $objectClass($xmlWriter, $newElement); $object->write(); - $this->assertEquals('', $xmlWriter->getData()); + self::assertEquals('', $xmlWriter->getData()); } } + // ODT Line Element not yet implemented + // ODT Bookmark not yet implemented + // ODT Table with style name not yet implemented (Word test defective) + // ODT Shape Elements not yet implemented + // ODT Chart Elements not yet implemented + // ODT adding Field to Section not yet implemented + // ODT List not yet implemented + // ODT Macro Button not yet implemented + // ODT Form Field not yet implemented + // ODT SDT not yet implemented + // ODT Comment not yet implemented + // ODT Track Changes implemented, possibly not correctly + // ODT List Item not yet implemented + + /** + * Test link element + */ + public function testLinkElement() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $extlink = 'https://github.com/PHPOffice/PHPWord'; + $section->addLink($extlink); + $intlink = 'internal_link'; + $section->addLink($intlink, null, null, null, true); + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $p2t = '/office:document-content/office:body/office:text/text:section'; + $element = "$p2t/text:p[2]/text:a"; + self::assertTrue($doc->elementExists($element)); + self::assertEquals($extlink, $doc->getElementAttribute($element, 'xlink:href')); + + $element = "$p2t/text:p[3]/text:a"; + self::assertTrue($doc->elementExists($element)); + self::assertEquals("#$intlink", $doc->getElementAttribute($element, 'xlink:href')); + } + + /** + * Basic test for table element + */ + public function testTableElements() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + + $table = $section->addTable(array('alignment' => \PhpOffice\PhpWord\SimpleType\JcTable::CENTER)); + $table->addRow(900); + $table->addCell(2000)->addText('Row 1'); + $table->addCell(2000)->addText('Row 2'); + $table->addCell(2000)->addText('Row 3'); + $table->addCell(2000)->addText('Row 4'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $p2s = '/office:document-content/office:automatic-styles'; + $tableStyleNum = 1; + $tableStyleName = ''; + while ($tableStyleName === '') { + $element = "$p2s/style:style[$tableStyleNum]"; + if (!$doc->elementExists($element)) { + break; + } + if ($doc->getElementAttribute($element, 'style:family') === 'table') { + $tableStyleName = $doc->getElementAttribute($element, 'style:name'); + break; + } + ++$tableStyleNum; + } + self::AssertNotEquals('', $tableStyleName); + $element = "$element/style:table-properties"; + self::assertTrue($doc->elementExists($element)); + self::assertEquals(\PhpOffice\PhpWord\SimpleType\JcTable::CENTER, $doc->getElementAttribute($element, 'table:align')); + $p2t = '/office:document-content/office:body/office:text/text:section'; + $tableRootElement = "$p2t/table:table"; + self::assertTrue($doc->elementExists($tableRootElement)); + self::assertEquals($tableStyleName, $doc->getElementAttribute($tableRootElement, 'table:style')); + self::assertTrue($doc->elementExists($tableRootElement . '/table:table-column[4]')); + } + + /** + * Test Title and Headings + */ + public function testTitleAndHeading() + { + $phpWord = new PhpWord(); + $phpWord->addTitleStyle(0, array('size' => 14, 'italic' => true)); + $phpWord->addTitleStyle(1, array('size' => 20, 'color' => '333333', 'bold' => true)); + + $section = $phpWord->addSection(); + $section->addTitle('This is a title', 0); + $section->addTitle('Heading 1', 1); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $p2t = '/office:document-content/office:body/office:text/text:section'; + $element = "$p2t/text:h[1]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('HE0', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('0', $doc->getElementAttribute($element, 'text:outline-level')); + $span = "$element/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals('This is a title', $doc->getElement($span)->textContent); + $this->assertEquals('Title', $doc->getElementAttribute($span, 'text:style-name')); + + $element = "$p2t/text:h[2]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('HD1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('1', $doc->getElementAttribute($element, 'text:outline-level')); + $span = "$element/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals('Heading 1', $doc->getElement($span)->textContent); + $this->assertEquals('Heading_1', $doc->getElementAttribute($span, 'text:style-name')); + + $doc->setDefaultFile('styles.xml'); + $element = '/office:document-styles/office:styles/style:style[1]'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Title', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('14pt', $doc->getElementAttribute($element, 'fo:font-size')); + $this->assertEquals('italic', $doc->getElementAttribute($element, 'fo:font-style')); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:font-weight')); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:color')); + + $element = '/office:document-styles/office:styles/style:style[2]'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('20pt', $doc->getElementAttribute($element, 'fo:font-size')); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:font-style')); + $this->assertEquals('bold', $doc->getElementAttribute($element, 'fo:font-weight')); + $this->assertEquals('#333333', $doc->getElementAttribute($element, 'fo:color')); + } + + /** + * Test correct writing of text with ampersand in it + */ + public function testTextWithAmpersand() + { + $esc = \PhpOffice\PhpWord\Settings::isOutputEscapingEnabled(); + \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true); + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $txt = 'this text contains an & (ampersand)'; + $section->addText($txt); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled($esc); + $p2t = '/office:document-content/office:body/office:text/text:section'; + $element = "$p2t/text:p[2]"; + $this->assertTrue($doc->elementExists($element)); + $span = "$element/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($txt, $doc->getElement($span)->nodeValue); + } + /** * Test PageBreak */ @@ -55,8 +221,8 @@ class ElementTest extends \PHPUnit\Framework\TestCase $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); - $element = '/office:document-content/office:body/office:text/text:section/text:p[2]'; - $this->assertTrue($doc->elementExists($element, 'content.xml')); - $this->assertEquals('P1', $doc->getElementAttribute($element, 'text:style-name', 'content.xml')); + $element = '/office:document-content/office:body/office:text/text:section/text:p[3]'; + self::assertTrue($doc->elementExists($element, 'content.xml')); + self::assertEquals('PB', $doc->getElementAttribute($element, 'text:style-name', 'content.xml')); } } diff --git a/tests/PhpWord/Writer/ODText/Part/ContentTest.php b/tests/PhpWord/Writer/ODText/Part/ContentTest.php index 2e501c60..34eb8068 100644 --- a/tests/PhpWord/Writer/ODText/Part/ContentTest.php +++ b/tests/PhpWord/Writer/ODText/Part/ContentTest.php @@ -92,7 +92,7 @@ class ContentTest extends \PHPUnit\Framework\TestCase $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); - $element = '/office:document-content/office:body/office:text/text:section/text:p'; + $element = '/office:document-content/office:body/office:text/text:section/text:p[2]'; $this->assertEquals($expected, $doc->getElement($element, 'content.xml')->nodeValue); } diff --git a/tests/PhpWord/Writer/ODText/Style/FontTest.php b/tests/PhpWord/Writer/ODText/Style/FontTest.php new file mode 100644 index 00000000..5306c6b0 --- /dev/null +++ b/tests/PhpWord/Writer/ODText/Style/FontTest.php @@ -0,0 +1,131 @@ +addSection(); + $section->addText('This is red (800) in rtf/html, default in docx/odt', array('color' => '800')); + $section->addText('This should be cyanish (008787)', array('color' => '008787')); + $section->addText('This should be dark green (FGCOLOR_DARKGREEN)', array('color' => \PhpOffice\PhpWord\Style\Font::FGCOLOR_DARKGREEN)); + $section->addText('This color is default (unknow)', array('color' => 'unknow')); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + $s2t = '/office:document-content/office:body/office:text/text:section'; + $this->assertTrue($doc->elementExists($s2t)); + + $element = "$s2a/style:style[5]"; + $this->assertTrue($doc->elementExists($element)); + $style = $doc->getElementAttribute($element, 'style:name'); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('#008787', $doc->getElementAttribute($element, 'fo:color')); + $span = "$s2t/text:p[3]/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); + $this->assertEquals('This should be cyanish (008787)', $doc->getElement($span)->nodeValue); + + $element = "$s2a/style:style[7]"; + $this->assertTrue($doc->elementExists($element)); + $style = $doc->getElementAttribute($element, 'style:name'); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('#006400', $doc->getElementAttribute($element, 'fo:color')); + $span = "$s2t/text:p[4]/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); + $this->assertEquals('This should be dark green (FGCOLOR_DARKGREEN)', $doc->getElement($span)->nodeValue); + } + + /** + * Test noproof + */ + public function testNoProof() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('Noproof not specified', array('color' => 'black')); + $section->addText('Noproof is true', array('color' => 'black', 'noproof' => true)); + $section->addText('Noproof is false', array('color' => 'black', 'noproof' => false)); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + $s2t = '/office:document-content/office:body/office:text/text:section'; + $this->assertTrue($doc->elementExists($s2t)); + + $element = "$s2a/style:style[3]"; + $this->assertTrue($doc->elementExists($element)); + $style = $doc->getElementAttribute($element, 'style:name'); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:language')); + $span = "$s2t/text:p[2]/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); + $this->assertEquals('Noproof not specified', $doc->getElement($span)->nodeValue); + + $element = "$s2a/style:style[5]"; + $this->assertTrue($doc->elementExists($element)); + $style = $doc->getElementAttribute($element, 'style:name'); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('zxx', $doc->getElementAttribute($element, 'fo:language')); + $this->assertEquals('zxx', $doc->getElementAttribute($element, 'style:language-asian')); + $this->assertEquals('zxx', $doc->getElementAttribute($element, 'style:language-complex')); + $this->assertEquals('none', $doc->getElementAttribute($element, 'fo:country')); + $this->assertEquals('none', $doc->getElementAttribute($element, 'style:country-asian')); + $this->assertEquals('none', $doc->getElementAttribute($element, 'style:country-complex')); + $span = "$s2t/text:p[3]/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); + $this->assertEquals('Noproof is true', $doc->getElement($span)->nodeValue); + + $element = "$s2a/style:style[7]"; + $this->assertTrue($doc->elementExists($element)); + $style = $doc->getElementAttribute($element, 'style:name'); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:language')); + $span = "$s2t/text:p[4]/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); + $this->assertEquals('Noproof is false', $doc->getElement($span)->nodeValue); + } +} diff --git a/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php b/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php new file mode 100644 index 00000000..0e9948cf --- /dev/null +++ b/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php @@ -0,0 +1,434 @@ +addSection(); + $section->addText('Text on first page'); + $section->addPageBreak(); + $section->addText('Text on second page'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $s2a = '/office:document-content/office:automatic-styles'; + $element = "$s2a/style:style[1]"; + $this->assertEquals('PB', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('page', $doc->getElementAttribute($element, 'fo:break-after')); + $this->assertEquals('0cm', $doc->getElementAttribute($element, 'fo:margin-top')); + $this->assertEquals('0cm', $doc->getElementAttribute($element, 'fo:margin-bottom')); + + $s2a = '/office:document-content/office:body/office:text/text:section'; + $element = "$s2a/text:p[3]"; + $this->assertEquals('PB', $doc->getElementAttribute($element, 'text:style-name')); + } + + /** + * Test normal/indent + */ + public function testNormalIndent() + { + $phpWord = new PhpWord(); + $cvt = Converter::INCH_TO_TWIP; + $indent1 = array('indentation' => array('left' => 0.50 * $cvt)); + $indent2 = array('indentation' => array('left' => 1.00 * $cvt, 'right' => 1.05 * $cvt)); + $indent3 = array('indentation' => array('left' => -0.50 * $cvt)); + $indent4 = array('indentation' => array('left' => 0 * $cvt)); + $phpWord->setDefaultParagraphStyle($indent1); + $section = $phpWord->addSection(); + $section->addText('Should use default indent (0.5)'); + $section->addText('Should use non-default indent (1.0) on both sides, and here\'s an extra long line to prove it', null, $indent2); + $section->addText('Should use non-default indent (-0.5)', null, $indent3); + $section->addText('Should use non-default indent (0)', null, $indent4); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:style[4]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:margin-left')); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:margin-right')); + + $element = "$s2a/style:style[6]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('1in', $doc->getElementAttribute($element, 'fo:margin-left')); + $this->assertEquals('1.05in', $doc->getElementAttribute($element, 'fo:margin-right')); + + $element = "$s2a/style:style[8]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('-0.5in', $doc->getElementAttribute($element, 'fo:margin-left')); + $this->assertEquals('0in', $doc->getElementAttribute($element, 'fo:margin-right')); + + $element = "$s2a/style:style[10]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('0in', $doc->getElementAttribute($element, 'fo:margin-left')); + $this->assertEquals('0in', $doc->getElementAttribute($element, 'fo:margin-right')); + + $doc->setDefaultFile('styles.xml'); + $element = '/office:document-styles/office:styles/style:style'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('0.5in', $doc->getElementAttribute($element, 'fo:margin-left')); + $this->assertEquals('0in', $doc->getElementAttribute($element, 'fo:margin-right')); + } + + /** + * Test textAlign + */ + public function testTextAlign() + { + $phpWord = new PhpWord(); + $align1 = array('alignment' => 'end'); + $align2 = array('alignment' => 'start'); + $phpWord->setDefaultParagraphStyle($align1); + $section = $phpWord->addSection(); + $section->addText('Should use default alignment (right for this doc)'); + $section->addText('Explicit left alignment', null, $align2); + $section->addText('Explicit right alignment', null, $align1); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:style[4]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:text-align')); + + $element = "$s2a/style:style[6]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('start', $doc->getElementAttribute($element, 'fo:text-align')); + + $element = "$s2a/style:style[8]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('end', $doc->getElementAttribute($element, 'fo:text-align')); + + $doc->setDefaultFile('styles.xml'); + $element = '/office:document-styles/office:styles/style:style'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('end', $doc->getElementAttribute($element, 'fo:text-align')); + } + + /** + * Test lineHeight + */ + public function testLineHeight() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $section->addText('Should use line height 1.08, and here\'s a long line which ought to overflow onto a second line to prove it', null, array('lineHeight' => 1.08)); + $section->addText('Should use line height 1.20, and here\'s a long line which ought to overflow onto a second line to prove it', null, array('lineHeight' => 1.20)); + $section->addText('Should use line height 0.90, and here\'s a long line which ought to overflow onto a second line to prove it', null, array('lineHeight' => 0.90)); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:style[4]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('108%', $doc->getElementAttribute($element, 'fo:line-height')); + + $element = "$s2a/style:style[6]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('120%', $doc->getElementAttribute($element, 'fo:line-height')); + + $element = "$s2a/style:style[8]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('90%', $doc->getElementAttribute($element, 'fo:line-height')); + } + + /** + * Test SpaceBeforeAfter + */ + public function testSpaceBeforeAfter() + { + $phpWord = new PhpWord(); + $phpWord->setDefaultParagraphStyle(array('spaceBefore' => 0, 'spaceAfter' => 0)); + $section = $phpWord->addSection(); + $section->addText('No spacing between this paragraph and next'); + $section->addText('No spacing between this paragraph and previous'); + $section->addText('No spacing before this but 100 after', null, array('spaceAfter' => 100)); + $section->addText('No spacing for this paragraph but previous specified 100 after and next specifies 100 before'); + $section->addText('No spacing after this but 100 before', null, array('spaceBefore' => 100)); + $section->addText('No spacing before this paragraph'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:style[8]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:margin-top')); + $this->assertEquals('5pt', $doc->getElementAttribute($element, 'fo:margin-bottom')); + + $element = "$s2a/style:style[12]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('5pt', $doc->getElementAttribute($element, 'fo:margin-top')); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:margin-bottom')); + + $doc->setDefaultFile('styles.xml'); + $element = '/office:document-styles/office:styles/style:style'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('0pt', $doc->getElementAttribute($element, 'fo:margin-top')); + $this->assertEquals('0pt', $doc->getElementAttribute($element, 'fo:margin-bottom')); + } + + /** + * Test Page Break Before + */ + public function testPageBreakBefore() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $section->addText('This is my first paragraph.'); + $section->addText('This is my second paragraph, on a new page.', null, array('pageBreakBefore' => true)); + $section->addText('This is my third paragraph, on same page as second.'); + $section->addText('This is my fourth paragraph, on a new page.', null, array('pageBreakBefore' => true)); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:style[4]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:break-before')); + $element = "$s2a/style:style[6]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('page', $doc->getElementAttribute($element, 'fo:break-before')); + $element = "$s2a/style:style[8]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:break-before')); + $element = "$s2a/style:style[10]/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('page', $doc->getElementAttribute($element, 'fo:break-before')); + } + + /** + * Test Heading Page Break Before + */ + public function testHeadingPageBreakBefore() + { + $phpWord = new PhpWord(); + $phpWord->addTitleStyle(1, null, array('pageBreakBefore' => true)); + $phpWord->addTitleStyle(2, null, array()); + $section = $phpWord->addSection(); + $section->addTitle('Section1 Heading1 #1', 1); + $section->addTitle('Section1 Heading2 #1', 2); + $section->addTitle('Section1 Heading1 #2', 1); + $section->addTitle('Section1 Heading2 #2', 2); + $section = $phpWord->addSection(); + $section->addTitle('Section2 Heading1 #1', 1); + $section->addTitle('Section2 Heading2 #1', 2); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:style[4]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('HD1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('page', $doc->getElementAttribute($element, 'fo:break-before')); + + $element = "$s2a/style:style[5]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('HE1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('auto', $doc->getElementAttribute($element, 'fo:break-before')); + + $element = "$s2a/style:style[6]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('HD2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Heading_2', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:break-before')); + + $element = "$s2a/style:style[7]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('HE2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Heading_2', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('auto', $doc->getElementAttribute($element, 'fo:break-before')); + + $s2a = '/office:document-content/office:body/office:text/text:section[1]'; + $this->assertTrue($doc->elementExists($s2a)); + $element = "$s2a/text:h[1]"; + $this->assertEquals('HE1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('1', $doc->getElementAttribute($element, 'text:outline-level')); + $element .= '/text:span'; + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2a/text:h[2]"; + $this->assertEquals('HD2', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('2', $doc->getElementAttribute($element, 'text:outline-level')); + $element .= '/text:span'; + $this->assertEquals('Heading_2', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2a/text:h[3]"; + $this->assertEquals('HD1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('1', $doc->getElementAttribute($element, 'text:outline-level')); + $element .= '/text:span'; + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2a/text:h[4]"; + $this->assertEquals('HD2', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('2', $doc->getElementAttribute($element, 'text:outline-level')); + $element .= '/text:span'; + $this->assertEquals('Heading_2', $doc->getElementAttribute($element, 'text:style-name')); + + $s2a = '/office:document-content/office:body/office:text/text:section[2]'; + $this->assertTrue($doc->elementExists($s2a)); + $element = "$s2a/text:h[1]"; + $this->assertEquals('HE1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('1', $doc->getElementAttribute($element, 'text:outline-level')); + $element .= '/text:span'; + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2a/text:h[2]"; + $this->assertEquals('HD2', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('2', $doc->getElementAttribute($element, 'text:outline-level')); + $element .= '/text:span'; + $this->assertEquals('Heading_2', $doc->getElementAttribute($element, 'text:style-name')); + + $doc->setDefaultFile('styles.xml'); + $s2a = '/office:document-styles/office:styles'; + $this->assertTrue($doc->elementExists($s2a)); + $element = "$s2a/style:style[1]"; + $this->assertEquals('Heading_1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('paragraph', $doc->getElementAttribute($element, 'style:family')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('page', $doc->getElementAttribute($element, 'fo:break-before')); + $element = "$s2a/style:style[3]"; + $this->assertEquals('Heading_2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('paragraph', $doc->getElementAttribute($element, 'style:family')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('', $doc->getElementAttribute($element, 'fo:break-before')); + } + + /** + * Test text run paragraph style using named style + */ + public function testTextRun() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->addParagraphStyle('parstyle1', array('align' => 'start')); + $phpWord->addParagraphStyle('parstyle2', array('align' => 'end')); + $section = $phpWord->addSection(); + $trx = $section->addTextRun('parstyle1'); + $trx->addText('First text in textrun. '); + $trx->addText('Second text - paragraph style is specified but ignored.', null, 'parstyle2'); + $section->addText('Third text added to section not textrun - paragraph style is specified and used.', null, 'parstyle2'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $element = "$s2a/style:style[3]"; + $this->assertEquals('P1_parstyle1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('parstyle1', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element = "$s2a/style:style[9]"; + $this->assertEquals('P4_parstyle2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('parstyle2', $doc->getElementAttribute($element, 'style:parent-style-name')); + + $s2a = '/office:document-content/office:body/office:text/text:section'; + $element = "$s2a/text:p[2]"; + $this->assertEquals('P1_parstyle1', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2a/text:p[3]"; + $this->assertEquals('P4_parstyle2', $doc->getElementAttribute($element, 'text:style-name')); + + $doc->setDefaultFile('styles.xml'); + $element = '/office:document-styles/office:styles/style:style[1]'; + $this->assertEquals('parstyle1', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('start', $doc->getElementAttribute($element, 'fo:text-align')); + $element = '/office:document-styles/office:styles/style:style[2]'; + $this->assertEquals('parstyle2', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('end', $doc->getElementAttribute($element, 'fo:text-align')); + } + + /** + * Test text run paragraph style using unnamed style + */ + public function testTextRunUnnamed() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $parstyle1 = array('align' => 'start'); + $parstyle2 = array('align' => 'end'); + $section = $phpWord->addSection(); + $trx = $section->addTextRun($parstyle1); + $trx->addText('First text in textrun. '); + $trx->addText('Second text - paragraph style is specified but ignored.', null, $parstyle2); + $section->addText('Third text added to section not textrun - paragraph style is specified and used.', null, $parstyle2); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $element = "$s2a/style:style[3]"; + $this->assertEquals('P1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('start', $doc->getElementAttribute($element, 'fo:text-align')); + $element = "$s2a/style:style[9]"; + $this->assertEquals('P4', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'style:parent-style-name')); + $element .= '/style:paragraph-properties'; + $this->assertEquals('end', $doc->getElementAttribute($element, 'fo:text-align')); + + $s2a = '/office:document-content/office:body/office:text/text:section'; + $element = "$s2a/text:p[2]"; + $this->assertEquals('P1', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2a/text:p[3]"; + $this->assertEquals('P4', $doc->getElementAttribute($element, 'text:style-name')); + } +} diff --git a/tests/PhpWord/Writer/ODText/Style/SectionTest.php b/tests/PhpWord/Writer/ODText/Style/SectionTest.php new file mode 100644 index 00000000..d471c7f0 --- /dev/null +++ b/tests/PhpWord/Writer/ODText/Style/SectionTest.php @@ -0,0 +1,249 @@ +addFontStyle('hdrstyle1', array('name' => 'Courier New', 'size' => 8)); + $section = $phpWord->addSection(array('paperSize' => 'Letter', 'marginTop' => $margins, 'marginBottom' => $margins)); + $header = $section->createHeader(); + $phpWord->addParagraphStyle('centerheader', array('align' => 'center')); + $header->addText('Centered Header', 'hdrstyle1', 'centerheader'); + $footer = $section->createFooter(); + $sizew = $section->getStyle()->getPageSizeW(); + $sizel = $section->getStyle()->getMarginLeft(); + $sizer = $section->getStyle()->getMarginRight(); + $footerwidth = $sizew - $sizel - $sizer; + $phpWord->addParagraphStyle( + 'footerTab', + array( + 'tabs' => array( + new \PhpOffice\PhpWord\Style\Tab('center', (int) ($footerwidth / 2)), + new \PhpOffice\PhpWord\Style\Tab('right', (int) $footerwidth), + ), + ) + ); + $textrun = $footer->addTextRun('footerTab'); + $textrun->addText('Left footer', 'hdrstyle1'); + $textrun->addText("\t", 'hdrstyle1'); + $fld = $textrun->addField('DATE'); + $fld->setFontStyle('hdrstyle1'); + $textrun->addText("\t", 'hdrstyle1'); + $textrun->addText('Page ', 'hdrstyle1'); + $fld = $textrun->addField('PAGE'); + $fld->setFontStyle('hdrstyle1'); + $textrun->addText(' of ', 'hdrstyle1'); + $fld = $textrun->addField('NUMPAGES'); + $fld->setFontStyle('hdrstyle1'); + $section->addText('First page'); + $section->addPageBreak(); + $section->addText('Second page'); + $section->addPageBreak(); + $section->addText('Third page'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $doc->setDefaultFile('styles.xml'); + $s2a = '/office:document-styles/office:automatic-styles'; + $element = "$s2a/style:page-layout/style:page-layout-properties"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('8.5in', $doc->getElementAttribute($element, 'fo:page-width')); + $this->assertEquals('11in', $doc->getElementAttribute($element, 'fo:page-height')); + $this->assertEquals('0.5in', $doc->getElementAttribute($element, 'fo:margin-top')); + $this->assertEquals('0.5in', $doc->getElementAttribute($element, 'fo:margin-bottom')); + + $s2s = '/office:document-styles/office:styles'; + $element = "$s2s/style:style[1]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('hdrstyle1', $doc->getElementAttribute($element, 'style:name')); + $tprop = "$element/style:text-properties"; + $this->assertTrue($doc->elementExists($tprop)); + $this->assertEquals('Courier New', $doc->getElementAttribute($tprop, 'style:font-name')); + + $element = "$s2s/style:style[2]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('centerheader', $doc->getElementAttribute($element, 'style:name')); + $tprop = "$element/style:paragraph-properties"; + $this->assertTrue($doc->elementExists($tprop)); + $this->assertEquals('center', $doc->getElementAttribute($tprop, 'fo:text-align')); + + $element = "$s2s/style:style[3]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('footerTab', $doc->getElementAttribute($element, 'style:name')); + $tprop = "$element/style:paragraph-properties/style:tab-stops"; + $this->assertTrue($doc->elementExists($tprop)); + $tstop = "$tprop/style:tab-stop[1]"; + $this->assertTrue($doc->elementExists($tstop)); + $this->assertEquals('center', $doc->getElementAttribute($tstop, 'style:type')); + $this->assertEquals('3.25in', $doc->getElementAttribute($tstop, 'style:position')); + $tstop = "$tprop/style:tab-stop[2]"; + $this->assertTrue($doc->elementExists($tstop)); + $this->assertEquals('right', $doc->getElementAttribute($tstop, 'style:type')); + $this->assertEquals('6.5in', $doc->getElementAttribute($tstop, 'style:position')); + + $s2s = '/office:document-styles/office:master-styles/style:master-page/style:footer/text:p'; + $this->assertTrue($doc->elementExists($s2s)); + $element = "$s2s/text:span[1]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('hdrstyle1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('Left footer', $doc->getElement($element)->nodeValue); + $element = "$s2s/text:span[2]/text:tab"; + $this->assertTrue($doc->elementExists($element)); + $element = "$s2s/text:span[3]/text:date"; + $this->assertTrue($doc->elementExists($element)); + $element = "$s2s/text:span[4]/text:tab"; + $this->assertTrue($doc->elementExists($element)); + $element = "$s2s/text:span[5]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Page', $doc->getElement($element)->nodeValue); + $this->assertTrue($doc->elementExists("$element/text:s")); + $element = "$s2s/text:span[6]/text:page-number"; + $this->assertTrue($doc->elementExists($element)); + $element = "$s2s/text:span[7]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('of', $doc->getElement($element)->nodeValue); + $this->assertTrue($doc->elementExists("$element/text:s")); + $this->assertTrue($doc->elementExists("$element/text:s[2]")); + $element = "$s2s/text:span[8]/text:page-count"; + $this->assertTrue($doc->elementExists($element)); + } + + /** + * Test HideErrors + */ + public function testHideErrors() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->setHideGrammaticalErrors(true); + $phpWord->getSettings()->setHideSpellingErrors(true); + $phpWord->getSettings()->setThemeFontLang(new \PhpOffice\PhpWord\Style\Language('en-US')); + $phpWord->getSettings()->getThemeFontLang()->setLangId(\PhpOffice\PhpWord\Style\Language::EN_US_ID); + $section = $phpWord->addSection(); + $section->addText('Here is a paragraph with some speling errorz'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $doc->setDefaultFile('styles.xml'); + $element = '/office:document-styles/office:styles/style:default-style/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('zxx', $doc->getElementAttribute($element, 'fo:language')); + $this->assertEquals('zxx', $doc->getElementAttribute($element, 'style:language-asian')); + $this->assertEquals('zxx', $doc->getElementAttribute($element, 'style:language-complex')); + $this->assertEquals('none', $doc->getElementAttribute($element, 'fo:country')); + $this->assertEquals('none', $doc->getElementAttribute($element, 'style:country-asian')); + $this->assertEquals('none', $doc->getElementAttribute($element, 'style:country-complex')); + } + + /** + * Test SpaceBeforeAfter + */ + public function testMultipleSections() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(array('paperSize' => 'Letter', 'Orientation' => 'portrait')); + $section->addText('This section uses Letter paper in portrait orientation.'); + $section = $phpWord->addSection(array('paperSize' => 'A4', 'Orientation' => 'landscape', 'pageNumberingStart' => '9')); + $header = $section->createHeader(); + $header->addField('PAGE'); + $section->addText('This section uses A4 paper in landscape orientation. It should have a page break beforehand. It artificially starts on page 9.'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $s2t = '/office:document-content/office:body/office:text'; + $this->assertTrue($doc->elementExists($s2a)); + $this->assertTrue($doc->elementExists($s2t)); + + $element = "$s2a/style:style[2]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('SB1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Standard1', $doc->getElementAttribute($element, 'style:master-page-name')); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('none', $doc->getElementAttribute($element, 'text:display')); + $element = "$s2a/style:style[3]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('SB2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Standard2', $doc->getElementAttribute($element, 'style:master-page-name')); + $elemen2 = "$element/style:paragraph-properties"; + $this->assertEquals('9', $doc->getElementAttribute($elemen2, 'style:page-number')); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('none', $doc->getElementAttribute($element, 'text:display')); + + $element = "$s2t/text:section[1]"; + $this->assertTrue($doc->elementExists($element)); + $element .= '/text:p[1]'; + $this->assertEquals('SB1', $doc->getElementAttribute($element, 'text:style-name')); + $element = "$s2t/text:section[2]"; + $this->assertTrue($doc->elementExists($element)); + $element .= '/text:p[1]'; + $this->assertEquals('SB2', $doc->getElementAttribute($element, 'text:style-name')); + + $doc->setDefaultFile('styles.xml'); + $s2a = '/office:document-styles/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + + $element = "$s2a/style:page-layout[1]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Mpm1', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:page-layout-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('8.5in', $doc->getElementAttribute($element, 'fo:page-width')); + $this->assertEquals('11in', $doc->getElementAttribute($element, 'fo:page-height')); + $this->assertEquals('portrait', $doc->getElementAttribute($element, 'style:print-orientation')); + + $element = "$s2a/style:page-layout[2]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Mpm2', $doc->getElementAttribute($element, 'style:name')); + $element .= '/style:page-layout-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('29.7cm', $doc->getElementAttribute($element, 'fo:page-width')); + $this->assertEquals('21cm', $doc->getElementAttribute($element, 'fo:page-height')); + $this->assertEquals('landscape', $doc->getElementAttribute($element, 'style:print-orientation')); + + $s2a = '/office:document-styles/office:master-styles'; + $this->assertTrue($doc->elementExists($s2a)); + $element = "$s2a/style:master-page[1]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Standard1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Mpm1', $doc->getElementAttribute($element, 'style:page-layout-name')); + $element = "$s2a/style:master-page[2]"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('Standard2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('Mpm2', $doc->getElementAttribute($element, 'style:page-layout-name')); + } +} diff --git a/tests/PhpWord/_includes/TestHelperDOCX.php b/tests/PhpWord/_includes/TestHelperDOCX.php index 02fa7d78..d35f0e3f 100644 --- a/tests/PhpWord/_includes/TestHelperDOCX.php +++ b/tests/PhpWord/_includes/TestHelperDOCX.php @@ -63,7 +63,12 @@ class TestHelperDOCX $zip->close(); } - return new XmlDocument(Settings::getTempDir() . '/PhpWord_Unit_Test/'); + $doc = new XmlDocument(Settings::getTempDir() . '/PhpWord_Unit_Test/'); + if ($writerName === 'ODText') { + $doc->setDefaultFile('content.xml'); + } + + return $doc; } /** diff --git a/tests/PhpWord/_includes/XmlDocument.php b/tests/PhpWord/_includes/XmlDocument.php index 3a7869bc..41a9d9db 100644 --- a/tests/PhpWord/_includes/XmlDocument.php +++ b/tests/PhpWord/_includes/XmlDocument.php @@ -50,6 +50,37 @@ class XmlDocument */ private $file; + /** + * Default file name + * + * @var string + */ + private $defaultFile = 'word/document.xml'; + + /** + * Get default file + * + * @return string + */ + public function getDefaultFile() + { + return $this->defaultFile; + } + + /** + * Set default file + * + * @param string $file + * @return string + */ + public function setDefaultFile($file) + { + $temp = $this->defaultFile; + $this->defaultFile = $file; + + return $temp; + } + /** * Create new instance * @@ -66,8 +97,11 @@ class XmlDocument * @param string $file * @return \DOMDocument */ - public function getFileDom($file = 'word/document.xml') + public function getFileDom($file = '') { + if (!$file) { + $file = $this->defaultFile; + } if (null !== $this->dom && $file === $this->file) { return $this->dom; } @@ -91,8 +125,11 @@ class XmlDocument * @param string $file * @return \DOMNodeList */ - public function getNodeList($path, $file = 'word/document.xml') + public function getNodeList($path, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } if (null === $this->dom || $file !== $this->file) { $this->getFileDom($file); } @@ -112,8 +149,11 @@ class XmlDocument * @param string $file * @return \DOMElement */ - public function getElement($path, $file = 'word/document.xml') + public function getElement($path, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } $elements = $this->getNodeList($path, $file); return $elements->item(0); @@ -147,8 +187,12 @@ class XmlDocument * @param string $file * @return string */ - public function getElementAttribute($path, $attribute, $file = 'word/document.xml') + public function getElementAttribute($path, $attribute, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } + return $this->getElement($path, $file)->getAttribute($attribute); } @@ -159,8 +203,11 @@ class XmlDocument * @param string $file * @return string */ - public function elementExists($path, $file = 'word/document.xml') + public function elementExists($path, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } $nodeList = $this->getNodeList($path, $file); return $nodeList->length != 0; @@ -173,8 +220,11 @@ class XmlDocument * @param string $file * @return string */ - public function printXml($path = '/', $file = 'word/document.xml') + public function printXml($path = '/', $file = '') { + if (!$file) { + $file = $this->defaultFile; + } $element = $this->getElement($path, $file); if ($element instanceof \DOMDocument) { $element->formatOutput = true; From cfa29cc1c2bb746f1413ada91b748e9712bd003b Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Sun, 5 Jan 2020 13:52:20 -0800 Subject: [PATCH 030/112] Applying Scrutinizer Suggestions I do not understand one suggestion, and I believe one is wrong. I will add comments to my ticket once this is pushed. One that I can discuss up front PhpWord/Style/Paragraph indicates that Indentation must be of type \PhpOffice\PhpWord\Style\Indentation, but it can also be null. My test for instanceof ... is one of the Scrutinizer reports. I did not change PhpWord/Style/Paragraph, but this commit does so by updating @var for indentation. --- src/PhpWord/Style/Paragraph.php | 2 +- src/PhpWord/Writer/ODText/Element/Field.php | 4 +- src/PhpWord/Writer/ODText/Part/Content.php | 7 ++-- src/PhpWord/Writer/ODText/Part/Styles.php | 42 +++++++------------ src/PhpWord/Writer/ODText/Style/Paragraph.php | 6 +-- 5 files changed, 24 insertions(+), 37 deletions(-) diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index 72f0f809..580ef54a 100644 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -85,7 +85,7 @@ class Paragraph extends Border /** * Indentation * - * @var \PhpOffice\PhpWord\Style\Indentation + * @var \PhpOffice\PhpWord\Style\Indentation|null */ private $indentation; diff --git a/src/PhpWord/Writer/ODText/Element/Field.php b/src/PhpWord/Writer/ODText/Element/Field.php index c95139ac..f7a74c1d 100644 --- a/src/PhpWord/Writer/ODText/Element/Field.php +++ b/src/PhpWord/Writer/ODText/Element/Field.php @@ -41,7 +41,7 @@ class Field extends Text $type = strtolower($element->getType()); switch ($type) { - case 'date': // Owen 2020-01-02 + case 'date': case 'page': case 'numpages': $this->writeDefault($element, $type); @@ -61,7 +61,7 @@ class Field extends Text } } switch ($type) { - case 'date': // Owen 2019-01-02 + case 'date': $xmlWriter->startElement('text:date'); $xmlWriter->writeAttribute('text:fixed', 'false'); $xmlWriter->endElement(); diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index 8eaad40f..ea4c87d2 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -186,7 +186,8 @@ class Content extends AbstractPart $styleWriter->write(); $sects = $this->getParentWriter()->getPhpWord()->getSections(); - for ($i = 0; $i < count($sects); ++$i) { + $countsects = count($sects); + for ($i = 0; $i < $countsects; ++$i) { $iplus1 = $i + 1; $style = new Paragraph(); $style->setStyleName("SB$iplus1"); @@ -297,7 +298,7 @@ class Content extends AbstractPart /** * Get style of individual element * - * @param \PhpOffice\PhpWord\Element\Text $element + * @param \PhpOffice\PhpWord\Element\Text|\PhpOffice\PhpWord\Element\TextRun $element * @param int $paragraphStyleCount * @param int $fontStyleCount */ @@ -331,7 +332,7 @@ class Content extends AbstractPart } else { $element->setParagraphStyle($name); } - } elseif (is_string($paragraphStyle)) { + } else { $paragraphStyleCount++; $parstylename = "P$paragraphStyleCount" . "_$paragraphStyle"; $style = $phpWord->addParagraphStyle($parstylename, $paragraphStyle); diff --git a/src/PhpWord/Writer/ODText/Part/Styles.php b/src/PhpWord/Writer/ODText/Part/Styles.php index 862f4b2a..bcd57ad5 100644 --- a/src/PhpWord/Writer/ODText/Part/Styles.php +++ b/src/PhpWord/Writer/ODText/Part/Styles.php @@ -140,16 +140,12 @@ class Styles extends AbstractPart /** * Convert int in twips to inches/cm then to string and append unit * - * @param int $twips - * @param string $dflt + * @param int|float $twips * @param float $factor * return string */ - private static function cvttwiptostr($twips, $dflt, $factor = 1.0) // Owen 2019-08-06 + private static function cvttwiptostr($twips, $factor = 1.0) { - if ($twips === null) { - return $dflt; - } $ins = (string) ($twips * $factor / Converter::INCH_TO_TWIP) . 'in'; $cms = (string) ($twips * $factor * Converter::INCH_TO_CM / Converter::INCH_TO_TWIP) . 'cm'; @@ -161,10 +157,11 @@ class Styles extends AbstractPart * * @param \PhpOffice\Common\XMLWriter $xmlWriter */ - private function writePageLayout(XMLWriter $xmlWriter) // Owen 2019-06-19 + private function writePageLayout(XMLWriter $xmlWriter) { $sections = $this->getParentWriter()->getPhpWord()->getSections(); - for ($i = 0; $i < count($sections); ++$i) { + $countsects = count($sections); + for ($i = 0; $i < $countsects; ++$i) { $this->writePageLayoutIndiv($xmlWriter, $sections[$i], $i + 1); } } @@ -189,23 +186,13 @@ class Styles extends AbstractPart } else { $botfactor = 1.0; } - $pwidth = '21.001cm'; - $pheight = '29.7cm'; - $orient = 'portrait'; - $mtop = $mleft = $mright = '2.501cm'; - $mbottom = '2cm'; - if ($sty instanceof \PhpOffice\PhpWord\Style\Section) { - $ori = $sty->getOrientation(); - if ($ori !== null) { - $orient = $ori; - } - $pwidth = self::cvttwiptostr($sty->getPageSizeW(), $pwidth); - $pheight = self::cvttwiptostr($sty->getPageSizeH(), $pheight); - $mtop = self::cvttwiptostr($sty->getMarginTop(), $mtop, $topfactor); - $mbottom = self::cvttwiptostr($sty->getMarginBottom(), $mbottom, $botfactor); - $mleft = self::cvttwiptostr($sty->getMarginRight(), $mleft); - $mright = self::cvttwiptostr($sty->getMarginLeft(), $mright); - } + $orient = $sty->getOrientation(); + $pwidth = self::cvttwiptostr($sty->getPageSizeW()); + $pheight = self::cvttwiptostr($sty->getPageSizeH()); + $mtop = self::cvttwiptostr($sty->getMarginTop(), $topfactor); + $mbottom = self::cvttwiptostr($sty->getMarginBottom(), $botfactor); + $mleft = self::cvttwiptostr($sty->getMarginRight()); + $mright = self::cvttwiptostr($sty->getMarginLeft()); $xmlWriter->startElement('style:page-layout'); $xmlWriter->writeAttribute('style:name', "Mpm$sectionNbr"); @@ -253,7 +240,7 @@ class Styles extends AbstractPart $xmlWriter->endElement(); // style:header-style $xmlWriter->startElement('style:footer-style'); - if ($botfactor < 1.0) { // Owen 2019-08-03 + if ($botfactor < 1.0) { $xmlWriter->startElement('style:header-footer-properties'); $xmlWriter->writeAttribute('fo:min-height', $mbottom); $xmlWriter->writeAttribute('fo:margin-top', $mbottom); @@ -275,7 +262,8 @@ class Styles extends AbstractPart $xmlWriter->startElement('office:master-styles'); $sections = $this->getParentWriter()->getPhpWord()->getSections(); - for ($i = 0; $i < count($sections); ++$i) { + $countsects = count($sections); + for ($i = 0; $i < $countsects; ++$i) { $iplus1 = $i + 1; $xmlWriter->startElement('style:master-page'); $xmlWriter->writeAttribute('style:name', "Standard$iplus1"); diff --git a/src/PhpWord/Writer/ODText/Style/Paragraph.php b/src/PhpWord/Writer/ODText/Style/Paragraph.php index 555a4825..be974e72 100644 --- a/src/PhpWord/Writer/ODText/Style/Paragraph.php +++ b/src/PhpWord/Writer/ODText/Style/Paragraph.php @@ -56,9 +56,7 @@ class Paragraph extends AbstractStyle $styleAuto = true; $mpm = 'Standard' . substr($styleName, 2); $psn = $style->getNumLevel(); - if (is_numeric($psn)) { - $pagestart = (int) $psn; - } + $pagestart = $psn; } elseif (substr($styleName, 0, 2) === 'HD') { $styleAuto = true; $psm = 'Heading_' . substr($styleName, 2); @@ -117,7 +115,7 @@ class Paragraph extends AbstractStyle $xmlWriter->writeAttributeIf($temp !== '', 'fo:text-align', $temp); $temp = $style->getLineHeight(); $xmlWriter->writeAttributeIf($temp !== null, 'fo:line-height', ((string) ($temp * 100) . '%')); - $xmlWriter->writeAttributeIf($style->getPageBreakBefore() === true, 'fo:break-before', 'page'); + $xmlWriter->writeAttributeIf($style->hasPageBreakBefore() === true, 'fo:break-before', 'page'); $tabs = $style->getTabs(); if ($tabs !== null && count($tabs) > 0) { From 46c41c5ac182f8eadc9493f4089ca740d034c6e4 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Sun, 5 Jan 2020 15:05:00 -0800 Subject: [PATCH 031/112] More Scrutinizer Changes Still one report that I don't understand at all, and one I'm not sure of. --- src/PhpWord/Writer/ODText/Element/TextRun.php | 1 + src/PhpWord/Writer/ODText/Part/Content.php | 35 +++++++++++++++++-- src/PhpWord/Writer/ODText/Style/Paragraph.php | 3 +- 3 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/Writer/ODText/Element/TextRun.php b/src/PhpWord/Writer/ODText/Element/TextRun.php index cde996f6..9a005773 100644 --- a/src/PhpWord/Writer/ODText/Element/TextRun.php +++ b/src/PhpWord/Writer/ODText/Element/TextRun.php @@ -33,6 +33,7 @@ class TextRun extends Text $element = $this->getElement(); $xmlWriter->startElement('text:p'); + /** @scrutinizer ignore-call */ $pStyle = $element->getParagraphStyle(); if (!is_string($pStyle)) { $pStyle = 'Normal'; diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index ea4c87d2..9d6a50c4 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -266,7 +266,7 @@ class Content extends AbstractPart $elements = $container->getElements(); foreach ($elements as $element) { if ($element instanceof TextRun) { - $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); + $this->getElementStyleTextRun($element, $paragraphStyleCount); $this->getContainerStyle($element, $paragraphStyleCount, $fontStyleCount); } elseif ($element instanceof Text) { $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); @@ -298,7 +298,7 @@ class Content extends AbstractPart /** * Get style of individual element * - * @param \PhpOffice\PhpWord\Element\Text|\PhpOffice\PhpWord\Element\TextRun $element + * @param \PhpOffice\PhpWord\Element\Text $element * @param int $paragraphStyleCount * @param int $fontStyleCount */ @@ -341,6 +341,37 @@ class Content extends AbstractPart } } + /** + * Get style of individual element + * + * @param \PhpOffice\PhpWord\Element\TextRun $element + * @param int $paragraphStyleCount + */ + private function getElementStyleTextRun(&$element, &$paragraphStyleCount) + { + $paragraphStyle = $element->getParagraphStyle(); + $phpWord = $this->getParentWriter()->getPhpWord(); + + if ($paragraphStyle instanceof Paragraph) { + // Paragraph + $name = $paragraphStyle->getStyleName(); + if (!$name) { + $paragraphStyleCount++; + $style = $phpWord->addParagraphStyle("P{$paragraphStyleCount}", $paragraphStyle); + $style->setAuto(); + $element->setParagraphStyle("P{$paragraphStyleCount}"); + } else { + $element->setParagraphStyle($name); + } + } else { + $paragraphStyleCount++; + $parstylename = "P$paragraphStyleCount" . "_$paragraphStyle"; + $style = $phpWord->addParagraphStyle($parstylename, $paragraphStyle); + $style->setAuto(); + $element->setParagraphStyle($parstylename); + } + } + /** * Finds all tracked changes * diff --git a/src/PhpWord/Writer/ODText/Style/Paragraph.php b/src/PhpWord/Writer/ODText/Style/Paragraph.php index be974e72..9d38de86 100644 --- a/src/PhpWord/Writer/ODText/Style/Paragraph.php +++ b/src/PhpWord/Writer/ODText/Style/Paragraph.php @@ -134,7 +134,8 @@ class Paragraph extends AbstractStyle //Indentation $indent = $style->getIndentation(); - if ($indent instanceof \PhpOffice\PhpWord\Style\Indentation) { + //if ($indent instanceof \PhpOffice\PhpWord\Style\Indentation) { + if (!empty($indent)) { $marg = $indent->getLeft(); $xmlWriter->writeAttributeIf($marg !== null, 'fo:margin-left', (string) ($marg / Converter::INCH_TO_TWIP) . 'in'); $marg = $indent->getRight(); From 1dee5f33cfdb5c6e788542dcdca64ceae4f0e0d5 Mon Sep 17 00:00:00 2001 From: Claas Augner Date: Tue, 4 Feb 2020 20:36:03 +0100 Subject: [PATCH 032/112] docs(Converter): fix @param allowing float --- src/PhpWord/Shared/Converter.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 7008ac5d..391fcabb 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -133,7 +133,7 @@ class Converter * Convert inch to EMU * * @param float $inch - * @return float + * @return int */ public static function inchToEmu($inch = 1) { @@ -143,7 +143,7 @@ class Converter /** * Convert pixel to twip * - * @param int $pixel + * @param float $pixel * @return float */ public static function pixelToTwip($pixel = 1) @@ -154,7 +154,7 @@ class Converter /** * Convert pixel to centimeter * - * @param int $pixel + * @param float $pixel * @return float */ public static function pixelToCm($pixel = 1) @@ -165,7 +165,7 @@ class Converter /** * Convert pixel to point * - * @param int $pixel + * @param float $pixel * @return float */ public static function pixelToPoint($pixel = 1) @@ -176,7 +176,7 @@ class Converter /** * Convert pixel to EMU * - * @param int $pixel + * @param float $pixel * @return int */ public static function pixelToEmu($pixel = 1) @@ -187,7 +187,7 @@ class Converter /** * Convert point to twip unit * - * @param int $point + * @param float $point * @return float */ public static function pointToTwip($point = 1) @@ -209,7 +209,7 @@ class Converter /** * Convert point to EMU * - * @param int $point + * @param float $point * @return float */ public static function pointToEmu($point = 1) @@ -231,7 +231,7 @@ class Converter /** * Convert EMU to pixel * - * @param int $emu + * @param float $emu * @return float */ public static function emuToPixel($emu = 1) @@ -242,7 +242,7 @@ class Converter /** * Convert pica to point * - * @param int $pica + * @param float $pica * @return float */ public static function picaToPoint($pica = 1) @@ -253,7 +253,7 @@ class Converter /** * Convert degree to angle * - * @param int $degree + * @param float $degree * @return int */ public static function degreeToAngle($degree = 1) @@ -264,7 +264,7 @@ class Converter /** * Convert angle to degrees * - * @param int $angle + * @param float $angle * @return int */ public static function angleToDegree($angle = 1) From d5149b2867a6887e9b35523194f8a379194099f4 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 6 Feb 2020 19:20:13 -0800 Subject: [PATCH 033/112] Coveralls Changes Changes to improve test coverage based on Coveralls report. --- src/PhpWord/Writer/ODText/Part/Content.php | 19 ++- tests/PhpWord/Shared/ConverterTest.php | 3 + .../Writer/ODText/Element/ImageTest.php | 5 +- tests/PhpWord/Writer/ODText/ElementTest.php | 2 +- .../Writer/ODText/Part/AbstractPartTest.php | 1 - .../Writer/ODText/Part/ContentTest.php | 1 - .../PhpWord/Writer/ODText/Style/FontTest.php | 116 ++++++++++++++++++ .../Writer/ODText/Style/ParagraphTest.php | 31 +++++ 8 files changed, 166 insertions(+), 12 deletions(-) diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index 9d6a50c4..88d7acbd 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -19,6 +19,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\AbstractContainer; +use PhpOffice\PhpWord\Element\Field; use PhpOffice\PhpWord\Element\Image; use PhpOffice\PhpWord\Element\Table; use PhpOffice\PhpWord\Element\Text; @@ -270,6 +271,8 @@ class Content extends AbstractPart $this->getContainerStyle($element, $paragraphStyleCount, $fontStyleCount); } elseif ($element instanceof Text) { $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); + } elseif ($element instanceof Field) { + $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); } elseif ($element instanceof Image) { $style = $element->getStyle(); $style->setStyleName('fr' . $element->getMediaIndex()); @@ -298,14 +301,18 @@ class Content extends AbstractPart /** * Get style of individual element * - * @param \PhpOffice\PhpWord\Element\Text $element + * @param \PhpOffice\PhpWord\Element\Text|\PhpOffice\PhpWord\Element\Field $element * @param int $paragraphStyleCount * @param int $fontStyleCount */ - private function getElementStyle(&$element, &$paragraphStyleCount, &$fontStyleCount) + private function getElementStyle($element, &$paragraphStyleCount, &$fontStyleCount) { $fontStyle = $element->getFontStyle(); - $paragraphStyle = $element->getParagraphStyle(); + if (method_exists($element, 'getParagraphStyle')) { + $paragraphStyle = $element->getParagraphStyle(); + } else { + $paragraphStyle = null; + } $phpWord = $this->getParentWriter()->getPhpWord(); if ($fontStyle instanceof Font) { @@ -332,7 +339,7 @@ class Content extends AbstractPart } else { $element->setParagraphStyle($name); } - } else { + } elseif ($paragraphStyle) { $paragraphStyleCount++; $parstylename = "P$paragraphStyleCount" . "_$paragraphStyle"; $style = $phpWord->addParagraphStyle($parstylename, $paragraphStyle); @@ -347,7 +354,7 @@ class Content extends AbstractPart * @param \PhpOffice\PhpWord\Element\TextRun $element * @param int $paragraphStyleCount */ - private function getElementStyleTextRun(&$element, &$paragraphStyleCount) + private function getElementStyleTextRun($element, &$paragraphStyleCount) { $paragraphStyle = $element->getParagraphStyle(); $phpWord = $this->getParentWriter()->getPhpWord(); @@ -363,7 +370,7 @@ class Content extends AbstractPart } else { $element->setParagraphStyle($name); } - } else { + } elseif ($paragraphStyle) { $paragraphStyleCount++; $parstylename = "P$paragraphStyleCount" . "_$paragraphStyle"; $style = $phpWord->addParagraphStyle($parstylename, $paragraphStyle); diff --git a/tests/PhpWord/Shared/ConverterTest.php b/tests/PhpWord/Shared/ConverterTest.php index 15be8ec1..39ffe090 100644 --- a/tests/PhpWord/Shared/ConverterTest.php +++ b/tests/PhpWord/Shared/ConverterTest.php @@ -135,5 +135,8 @@ class ConverterTest extends \PHPUnit\Framework\TestCase $this->assertEquals(120, Converter::cssToPoint('10pc')); $this->assertEquals(28.346457, Converter::cssToPoint('10mm'), '', 0.000001); $this->assertEquals(283.464567, Converter::cssToPoint('10cm'), '', 0.000001); + $this->assertEquals(40, Converter::cssToPixel('30pt')); + $this->assertEquals(1.27, Converter::cssToCm('36pt')); + $this->assertEquals(127000, Converter::cssToEmu('10pt')); } } diff --git a/tests/PhpWord/Writer/ODText/Element/ImageTest.php b/tests/PhpWord/Writer/ODText/Element/ImageTest.php index bc861104..2e0fdeef 100644 --- a/tests/PhpWord/Writer/ODText/Element/ImageTest.php +++ b/tests/PhpWord/Writer/ODText/Element/ImageTest.php @@ -21,10 +21,9 @@ use PhpOffice\PhpWord\Style\Image; use PhpOffice\PhpWord\TestHelperDOCX; /** - * Test class for PhpOffice\PhpWord\Writer\Word2007\Style\Font + * Test class for PhpOffice\PhpWord\Writer\ODText\Element\Image * - * @coversDefaultClass \PhpOffice\PhpWord\Writer\Word2007\Style\Frame - * @runTestsInSeparateProcesses + * @coversDefaultClass \PhpOffice\PhpWord\Writer\ODText\Element\Image */ class ImageTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/PhpWord/Writer/ODText/ElementTest.php b/tests/PhpWord/Writer/ODText/ElementTest.php index 500ee247..afad150d 100644 --- a/tests/PhpWord/Writer/ODText/ElementTest.php +++ b/tests/PhpWord/Writer/ODText/ElementTest.php @@ -39,7 +39,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase */ public function testUnmatchedElements() { - $elements = array('Image', 'Link', 'Table', 'Text', 'Title'); + $elements = array('Image', 'Link', 'Table', 'Text', 'Title', 'Field'); foreach ($elements as $element) { $objectClass = 'PhpOffice\\PhpWord\\Writer\\ODText\\Element\\' . $element; $xmlWriter = new XMLWriter(); diff --git a/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php b/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php index 51d893d2..3f0c8129 100644 --- a/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php +++ b/tests/PhpWord/Writer/ODText/Part/AbstractPartTest.php @@ -23,7 +23,6 @@ use PhpOffice\PhpWord\Writer\ODText; * Test class for PhpOffice\PhpWord\Writer\ODText\Part\AbstractPart * * @coversDefaultClass \PhpOffice\PhpWord\Writer\ODText\Part\AbstractPart - * @runTestsInSeparateProcesses */ class AbstractPartTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/PhpWord/Writer/ODText/Part/ContentTest.php b/tests/PhpWord/Writer/ODText/Part/ContentTest.php index 34eb8068..55d1a00e 100644 --- a/tests/PhpWord/Writer/ODText/Part/ContentTest.php +++ b/tests/PhpWord/Writer/ODText/Part/ContentTest.php @@ -25,7 +25,6 @@ use PhpOffice\PhpWord\TestHelperDOCX; * Test class for PhpOffice\PhpWord\Writer\ODText\Part\Content * * @coversDefaultClass \PhpOffice\PhpWord\Writer\ODText\Part\Content - * @runTestsInSeparateProcesses */ class ContentTest extends \PHPUnit\Framework\TestCase { diff --git a/tests/PhpWord/Writer/ODText/Style/FontTest.php b/tests/PhpWord/Writer/ODText/Style/FontTest.php index 5306c6b0..f1224179 100644 --- a/tests/PhpWord/Writer/ODText/Style/FontTest.php +++ b/tests/PhpWord/Writer/ODText/Style/FontTest.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Style; +use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\TestHelperDOCX; /** @@ -73,6 +74,61 @@ class FontTest extends \PHPUnit\Framework\TestCase $this->assertEquals('This should be dark green (FGCOLOR_DARKGREEN)', $doc->getElement($span)->nodeValue); } + public function providerAllNamedColors() + { + return array( + array(Font::FGCOLOR_YELLOW, 'FFFF00'), + array(Font::FGCOLOR_LIGHTGREEN, '90EE90'), + array(Font::FGCOLOR_CYAN, '00FFFF'), + array(Font::FGCOLOR_MAGENTA, 'FF00FF'), + array(Font::FGCOLOR_BLUE, '0000FF'), + array(Font::FGCOLOR_RED, 'FF0000'), + array(Font::FGCOLOR_DARKBLUE, '00008B'), + array(Font::FGCOLOR_DARKCYAN, '008B8B'), + array(Font::FGCOLOR_DARKGREEN, '006400'), + array(Font::FGCOLOR_DARKMAGENTA, '8B008B'), + array(Font::FGCOLOR_DARKRED, '8B0000'), + array(Font::FGCOLOR_DARKYELLOW, '8B8B00'), + array(Font::FGCOLOR_DARKGRAY, 'A9A9A9'), + array(Font::FGCOLOR_LIGHTGRAY, 'D3D3D3'), + array(Font::FGCOLOR_BLACK, '000000'), + array('unknow', 'unknow'), + array('unknown', 'unknown'), + ); + } + + /** + * @dataProvider providerAllNamedColors + * + * @param string $namedColor + * @param string $rgbColor + */ + public function testAllNamedColors($namedColor, $rgbColor) + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addText('This is red (800) in rtf/html, default in docx/odt', array('color' => '800')); + $section->addText('This should be cyanish (008787)', array('color' => '008787')); + $section->addText($namedColor, array('color' => $namedColor)); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $this->assertTrue($doc->elementExists($s2a)); + $s2t = '/office:document-content/office:body/office:text/text:section'; + $this->assertTrue($doc->elementExists($s2t)); + + $element = "$s2a/style:style[7]"; + $this->assertTrue($doc->elementExists($element)); + $style = $doc->getElementAttribute($element, 'style:name'); + $element .= '/style:text-properties'; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals("#$rgbColor", $doc->getElementAttribute($element, 'fo:color')); + $span = "$s2t/text:p[4]/text:span"; + $this->assertTrue($doc->elementExists($span)); + $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); + $this->assertEquals($namedColor, $doc->getElement($span)->nodeValue); + } + /** * Test noproof */ @@ -128,4 +184,64 @@ class FontTest extends \PHPUnit\Framework\TestCase $this->assertEquals($style, $doc->getElementAttribute($span, 'text:style-name')); $this->assertEquals('Noproof is false', $doc->getElement($span)->nodeValue); } + + /** + * Test using object with a name as font style for addText + */ + public function testNamedStyleAsObject() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $named = $phpWord->addFontStyle('namedobject', array('color' => '008787')); + $section = $phpWord->addSection(); + $section->addText('Let us see what color we wind up with', $named); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2t = '/office:document-content/office:body/office:text/text:section'; + $this->assertTrue($doc->elementExists($s2t)); + $element = "$s2t/text:p[2]/text:span"; + $this->assertTrue($doc->elementExists($element)); + $this->assertEquals('namedobject', $doc->getElementAttribute($element, 'text:style-name')); + } + + /** + * Test supplying field font style as array or object or string + */ + public function testFieldStyles() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->addFontStyle('namedstyle', array('color' => '800000')); + $section = $phpWord->addSection(); + $textrun = $section->addTextRun(); + $fld = $textrun->addField('DATE'); + $fld->setFontStyle('namedstyle'); + $textrun = $section->addTextRun(); + $fld = $textrun->addField('DATE'); + $fld->setFontStyle(array('color' => '008000')); + $textrun = $section->addTextRun(); + $fld = $textrun->addField('DATE'); + $font = new \PhpOffice\PhpWord\Style\Font(); + $font->setColor('000080'); + $fld->setFontStyle($font); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:automatic-styles'; + $s2t = '/office:document-content/office:body/office:text/text:section'; + + $element = "$s2a/style:style[5]"; + $this->assertEquals('T1', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('#008000', $doc->getElementAttribute("$element/style:text-properties", 'fo:color')); + $element = "$s2a/style:style[7]"; + $this->assertEquals('T2', $doc->getElementAttribute($element, 'style:name')); + $this->assertEquals('#000080', $doc->getElementAttribute("$element/style:text-properties", 'fo:color')); + + $element = "$s2t/text:p[2]/text:span"; + $this->assertEquals('namedstyle', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertTrue($doc->elementExists("$element/text:date")); + $element = "$s2t/text:p[3]/text:span"; + $this->assertEquals('T1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertTrue($doc->elementExists("$element/text:date")); + $element = "$s2t/text:p[4]/text:span"; + $this->assertEquals('T2', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertTrue($doc->elementExists("$element/text:date")); + } } diff --git a/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php b/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php index 0e9948cf..9ddb5fe1 100644 --- a/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php +++ b/tests/PhpWord/Writer/ODText/Style/ParagraphTest.php @@ -431,4 +431,35 @@ class ParagraphTest extends \PHPUnit\Framework\TestCase $element = "$s2a/text:p[3]"; $this->assertEquals('P4', $doc->getElementAttribute($element, 'text:style-name')); } + + /** + * Test Empty font and paragraph styles + */ + public function testEmptyFontAndParagraphStyles() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $phpWord->addFontStyle('namedfont', array('name' => 'Courier New', 'size' => 8)); + $phpWord->addParagraphStyle('namedpar', array('lineHeight' => 1.08)); + $section->addText('Empty Font Style and Empty Paragraph Style', '', ''); + $section->addText('Named Font Style and Empty Paragraph Style', 'namedfont', ''); + $section->addText('Empty Font Style and Named Paragraph Style', '', 'namedpar'); + $section->addText('Named Font Style and Named Paragraph Style', 'namedfont', 'namedpar'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + $s2a = '/office:document-content/office:body/office:text/text:section'; + $element = "$s2a/text:p[2]"; + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals(5, $doc->getElementAttribute("$element/text:s", 'text:c')); + $this->assertFalse($doc->elementExists("$element/text:span")); + $element = "$s2a/text:p[3]"; + $this->assertEquals('Normal', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('namedfont', $doc->getElementAttribute("$element/text:span", 'text:style-name')); + $element = "$s2a/text:p[4]"; + $this->assertEquals('P1_namedpar', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertFalse($doc->elementExists("$element/text:span")); + $element = "$s2a/text:p[5]"; + $this->assertEquals('P2_namedpar', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('namedfont', $doc->getElementAttribute("$element/text:span", 'text:style-name')); + } } From 677e042c3a13b51c1f90529e18c1ad569b3c9695 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 6 Feb 2020 20:41:40 -0800 Subject: [PATCH 034/112] Scrutinizer Workaroun Attempt to work around demonstrably incorrect Scrutinizer analysis (flags code as bug because "condition is always false" even though Coveralls reports that code which would be executed only if condition is true is indeed executed). --- src/PhpWord/Writer/ODText/Element/Title.php | 17 ++++++++-- src/PhpWord/Writer/ODText/Part/Content.php | 36 +++++++++++++++++---- 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/PhpWord/Writer/ODText/Element/Title.php b/src/PhpWord/Writer/ODText/Element/Title.php index 99153b5e..98ddbbf4 100644 --- a/src/PhpWord/Writer/ODText/Element/Title.php +++ b/src/PhpWord/Writer/ODText/Element/Title.php @@ -39,8 +39,7 @@ class Title extends AbstractElement $hdname = 'HD'; $sect = $element->getParent(); if ($sect instanceof \PhpOffice\PhpWord\Element\Section) { - $elems = $sect->getElements(); - if ($elems[0] === $element) { + if (self::compareToFirstElement($element, $sect->getElements())) { $hdname = 'HE'; } } @@ -63,4 +62,18 @@ class Title extends AbstractElement $xmlWriter->endElement(); // text:span $xmlWriter->endElement(); // text:h } + + /** + * Test if element is same as first element in array + * + * @param \PhpOffice\PhpWord\Element\AbstractElement $elem + * + * @param \PhpOffice\PhpWord\Element\AbstractElement[] $elemarray + * + * @return bool + */ + private static function compareToFirstElement($elem, $elemarray) + { + return $elem === $elemarray[0]; + } } diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index 88d7acbd..f0e60441 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -272,7 +272,7 @@ class Content extends AbstractPart } elseif ($element instanceof Text) { $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); } elseif ($element instanceof Field) { - $this->getElementStyle($element, $paragraphStyleCount, $fontStyleCount); + $this->getElementStyleField($element, $fontStyleCount); } elseif ($element instanceof Image) { $style = $element->getStyle(); $style->setStyleName('fr' . $element->getMediaIndex()); @@ -301,18 +301,14 @@ class Content extends AbstractPart /** * Get style of individual element * - * @param \PhpOffice\PhpWord\Element\Text|\PhpOffice\PhpWord\Element\Field $element + * @param \PhpOffice\PhpWord\Element\Text $element * @param int $paragraphStyleCount * @param int $fontStyleCount */ private function getElementStyle($element, &$paragraphStyleCount, &$fontStyleCount) { $fontStyle = $element->getFontStyle(); - if (method_exists($element, 'getParagraphStyle')) { - $paragraphStyle = $element->getParagraphStyle(); - } else { - $paragraphStyle = null; - } + $paragraphStyle = $element->getParagraphStyle(); $phpWord = $this->getParentWriter()->getPhpWord(); if ($fontStyle instanceof Font) { @@ -348,6 +344,32 @@ class Content extends AbstractPart } } + /** + * Get font style of individual field element + * + * @param \PhpOffice\PhpWord\Element\Field $element + * @param int $paragraphStyleCount + * @param int $fontStyleCount + */ + private function getElementStyleField($element, &$fontStyleCount) + { + $fontStyle = $element->getFontStyle(); + $phpWord = $this->getParentWriter()->getPhpWord(); + + if ($fontStyle instanceof Font) { + $name = $fontStyle->getStyleName(); + if (!$name) { + $fontStyleCount++; + $style = $phpWord->addFontStyle("T{$fontStyleCount}", $fontStyle, null); + $style->setAuto(); + $style->setParagraph(null); + $element->setFontStyle("T{$fontStyleCount}"); + } else { + $element->setFontStyle($name); + } + } + } + /** * Get style of individual element * From 4e347b33d7fd73668363c5bebaec3c7a110b9b88 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 6 Feb 2020 23:34:24 -0800 Subject: [PATCH 035/112] One Additional Coveralls Test Cover one line previously omitted from coverage. --- tests/PhpWord/Writer/ODText/Style/FontTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/PhpWord/Writer/ODText/Style/FontTest.php b/tests/PhpWord/Writer/ODText/Style/FontTest.php index f1224179..22a7151c 100644 --- a/tests/PhpWord/Writer/ODText/Style/FontTest.php +++ b/tests/PhpWord/Writer/ODText/Style/FontTest.php @@ -209,7 +209,7 @@ class FontTest extends \PHPUnit\Framework\TestCase public function testFieldStyles() { $phpWord = new \PhpOffice\PhpWord\PhpWord(); - $phpWord->addFontStyle('namedstyle', array('color' => '800000')); + $namedstyle = $phpWord->addFontStyle('namedstyle', array('color' => '800000')); $section = $phpWord->addSection(); $textrun = $section->addTextRun(); $fld = $textrun->addField('DATE'); @@ -222,6 +222,9 @@ class FontTest extends \PHPUnit\Framework\TestCase $font = new \PhpOffice\PhpWord\Style\Font(); $font->setColor('000080'); $fld->setFontStyle($font); + $textrun = $section->addTextRun(); + $fld = $textrun->addField('DATE'); + $fld->setFontStyle($namedstyle); $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); $s2a = '/office:document-content/office:automatic-styles'; @@ -243,5 +246,7 @@ class FontTest extends \PHPUnit\Framework\TestCase $element = "$s2t/text:p[4]/text:span"; $this->assertEquals('T2', $doc->getElementAttribute($element, 'text:style-name')); $this->assertTrue($doc->elementExists("$element/text:date")); + $element = "$s2t/text:p[5]/text:span"; + $this->assertEquals('namedstyle', $doc->getElementAttribute($element, 'text:style-name')); } } From 41a5b74f93ec919fcdefc59ffd6defcc84ab3ff2 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 16 Apr 2020 23:30:27 -0700 Subject: [PATCH 036/112] Make Default Paper Configurable Each section currently has a hard-coded default paper of A4. It would make sense to allow the user to set this default, and specify it in a configuration file, just as is done with default font name and size. --- docs/general.rst | 10 ++ phpword.ini.dist | 4 + src/PhpWord/Settings.php | 34 ++++++ src/PhpWord/Style/Section.php | 6 +- tests/PhpWord/SettingsTest.php | 108 +++++++++++++++++- .../AbstractWebServerEmbeddedTest.php | 21 +++- 6 files changed, 177 insertions(+), 6 deletions(-) diff --git a/docs/general.rst b/docs/general.rst index f40a08c3..d4b9b99c 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -130,6 +130,16 @@ To turn it on set ``outputEscapingEnabled`` option to ``true`` in your PHPWord c \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true); +Default Paper +~~~~~~~~~~~~~ + +By default, all sections of the document will print on A4 paper. +You can alter the default paper by using the following function: + +.. code-block:: php + + \PhpOffice\PhpWord\Settings::setDefaultPaper('Letter'); + Default font ~~~~~~~~~~~~ diff --git a/phpword.ini.dist b/phpword.ini.dist index 0f4cc358..f3f66dbe 100644 --- a/phpword.ini.dist +++ b/phpword.ini.dist @@ -14,3 +14,7 @@ outputEscapingEnabled = false defaultFontName = Arial defaultFontSize = 10 + +[Paper] + +defaultPaper = "A4" diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php index 8de1a8df..c493132f 100644 --- a/src/PhpWord/Settings.php +++ b/src/PhpWord/Settings.php @@ -70,6 +70,7 @@ class Settings const DEFAULT_FONT_SIZE = 10; const DEFAULT_FONT_COLOR = '000000'; const DEFAULT_FONT_CONTENT_TYPE = 'default'; // default|eastAsia|cs + const DEFAULT_PAPER = 'A4'; /** * Compatibility option for XMLWriter @@ -119,6 +120,12 @@ class Settings */ private static $defaultFontSize = self::DEFAULT_FONT_SIZE; + /** + * Default paper + * @var int + */ + private static $defaultPaper = self::DEFAULT_PAPER; + /** * The user defined temporary directory. * @@ -432,6 +439,33 @@ class Settings return $config; } + /** + * Get default paper + * + * @return string + */ + public static function getDefaultPaper() + { + return self::$defaultPaper; + } + + /** + * Set default paper + * + * @param string $value + * @return bool + */ + public static function setDefaultPaper($value) + { + if (is_string($value) && trim($value) !== '') { + self::$defaultPaper = $value; + + return true; + } + + return false; + } + /** * Return the compatibility option used by the XMLWriter * diff --git a/src/PhpWord/Style/Section.php b/src/PhpWord/Style/Section.php index ff9b0be0..697add74 100644 --- a/src/PhpWord/Style/Section.php +++ b/src/PhpWord/Style/Section.php @@ -17,6 +17,7 @@ namespace PhpOffice\PhpWord\Style; +use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\SimpleType\VerticalJc; /** @@ -200,8 +201,11 @@ class Section extends Border * @param string $value * @return self */ - public function setPaperSize($value = 'A4') + public function setPaperSize($value = '') { + if (!$value) { + $value = Settings::getDefaultPaper(); + } if ($this->paper === null) { $this->paper = new Paper(); } diff --git a/tests/PhpWord/SettingsTest.php b/tests/PhpWord/SettingsTest.php index afe59549..748ec71b 100644 --- a/tests/PhpWord/SettingsTest.php +++ b/tests/PhpWord/SettingsTest.php @@ -25,6 +25,45 @@ namespace PhpOffice\PhpWord; */ class SettingsTest extends \PHPUnit\Framework\TestCase { + private $compatibility; + private $defaultFontSize; + private $defaultFontName; + private $defaultPaper; + private $measurementUnit; + private $outputEscapingEnabled; + private $pdfRendererName; + private $pdfRendererPath; + private $tempDir; + private $zipClass; + + public function setUp() + { + $this->compatibility = Settings::hasCompatibility(); + $this->defaultFontSize = Settings::getDefaultFontSize(); + $this->defaultFontName = Settings::getDefaultFontName(); + $this->defaultPaper = Settings::getDefaultPaper(); + $this->measurementUnit = Settings::getMeasurementUnit(); + $this->outputEscapingEnabled = Settings::isOutputEscapingEnabled(); + $this->pdfRendererName = Settings::getPdfRendererName(); + $this->pdfRendererPath = Settings::getPdfRendererPath(); + $this->tempDir = Settings::getTempDir(); + $this->zipClass = Settings::getZipClass(); + } + + public function tearDown() + { + Settings::setCompatibility($this->compatibility); + Settings::setDefaultFontSize($this->defaultFontSize); + Settings::setDefaultFontName($this->defaultFontName); + Settings::setDefaultPaper($this->defaultPaper); + Settings::setMeasurementUnit($this->measurementUnit); + Settings::setOutputEscapingEnabled($this->outputEscapingEnabled); + Settings::setPdfRendererName($this->pdfRendererName); + Settings::setPdfRendererPath($this->pdfRendererPath); + Settings::setTempDir($this->tempDir); + Settings::setZipClass($this->zipClass); + } + /** * Test set/get compatibity option */ @@ -35,14 +74,28 @@ class SettingsTest extends \PHPUnit\Framework\TestCase $this->assertFalse(Settings::hasCompatibility()); } + /** + * Test set/get outputEscapingEnabled option + */ + public function testSetGetOutputEscapingEnabled() + { + $this->assertFalse(Settings::isOutputEscapingEnabled()); + Settings::setOutputEscapingEnabled(true); + $this->assertTrue(Settings::isOutputEscapingEnabled()); + } + /** * Test set/get zip class */ public function testSetGetZipClass() { $this->assertEquals(Settings::ZIPARCHIVE, Settings::getZipClass()); - $this->assertTrue(Settings::setZipClass(Settings::PCLZIP)); $this->assertFalse(Settings::setZipClass('foo')); + $this->assertEquals(Settings::ZIPARCHIVE, Settings::getZipClass()); + $this->assertTrue(Settings::setZipClass(Settings::PCLZIP)); + $this->assertEquals(Settings::getZipClass(), Settings::PCLZIP); + $this->assertFalse(Settings::setZipClass('foo')); + $this->assertEquals(Settings::getZipClass(), Settings::PCLZIP); } /** @@ -57,6 +110,7 @@ class SettingsTest extends \PHPUnit\Framework\TestCase $this->assertEquals(Settings::PDF_RENDERER_DOMPDF, Settings::getPdfRendererName()); $this->assertEquals($domPdfPath, Settings::getPdfRendererPath()); $this->assertFalse(Settings::setPdfRendererPath('dummy/path')); + $this->assertEquals($domPdfPath, Settings::getPdfRendererPath()); } /** @@ -65,8 +119,12 @@ class SettingsTest extends \PHPUnit\Framework\TestCase public function testSetGetMeasurementUnit() { $this->assertEquals(Settings::UNIT_TWIP, Settings::getMeasurementUnit()); - $this->assertTrue(Settings::setMeasurementUnit(Settings::UNIT_INCH)); $this->assertFalse(Settings::setMeasurementUnit('foo')); + $this->assertEquals(Settings::UNIT_TWIP, Settings::getMeasurementUnit()); + $this->assertTrue(Settings::setMeasurementUnit(Settings::UNIT_INCH)); + $this->assertEquals(Settings::UNIT_INCH, Settings::getMeasurementUnit()); + $this->assertFalse(Settings::setMeasurementUnit('foo')); + $this->assertEquals(Settings::UNIT_INCH, Settings::getMeasurementUnit()); } /** @@ -99,8 +157,12 @@ class SettingsTest extends \PHPUnit\Framework\TestCase public function testSetGetDefaultFontName() { $this->assertEquals(Settings::DEFAULT_FONT_NAME, Settings::getDefaultFontName()); - $this->assertTrue(Settings::setDefaultFontName('Times New Roman')); $this->assertFalse(Settings::setDefaultFontName(' ')); + $this->assertEquals(Settings::DEFAULT_FONT_NAME, Settings::getDefaultFontName()); + $this->assertTrue(Settings::setDefaultFontName('Times New Roman')); + $this->assertEquals('Times New Roman', Settings::getDefaultFontName()); + $this->assertFalse(Settings::setDefaultFontName(' ')); + $this->assertEquals('Times New Roman', Settings::getDefaultFontName()); } /** @@ -109,8 +171,35 @@ class SettingsTest extends \PHPUnit\Framework\TestCase public function testSetGetDefaultFontSize() { $this->assertEquals(Settings::DEFAULT_FONT_SIZE, Settings::getDefaultFontSize()); - $this->assertTrue(Settings::setDefaultFontSize(12)); $this->assertFalse(Settings::setDefaultFontSize(null)); + $this->assertEquals(Settings::DEFAULT_FONT_SIZE, Settings::getDefaultFontSize()); + $this->assertTrue(Settings::setDefaultFontSize(12)); + $this->assertEquals(12, Settings::getDefaultFontSize()); + $this->assertFalse(Settings::setDefaultFontSize(null)); + $this->assertEquals(12, Settings::getDefaultFontSize()); + } + + /** + * Test set/get default paper + */ + public function testSetGetDefaultPaper() + { + $dflt = Settings::DEFAULT_PAPER; + $chng = ($dflt === 'A4') ? 'Letter' : 'A4'; + $doc = new PhpWord(); + $this->assertEquals($dflt, Settings::getDefaultPaper()); + $sec1 = $doc->addSection(); + $this->assertEquals($dflt, $sec1->getStyle()->getPaperSize()); + $this->assertFalse(Settings::setDefaultPaper('')); + $this->assertEquals($dflt, Settings::getDefaultPaper()); + $this->assertTrue(Settings::setDefaultPaper($chng)); + $this->assertEquals($chng, Settings::getDefaultPaper()); + $sec2 = $doc->addSection(); + $this->assertEquals($chng, $sec2->getStyle()->getPaperSize()); + $sec3 = $doc->addSection(array('paperSize' => 'Legal')); + $this->assertEquals('Legal', $sec3->getStyle()->getPaperSize()); + $this->assertFalse(Settings::setDefaultPaper('')); + $this->assertEquals($chng, Settings::getDefaultPaper()); } /** @@ -126,6 +215,7 @@ class SettingsTest extends \PHPUnit\Framework\TestCase 'defaultFontName' => 'Arial', 'defaultFontSize' => 10, 'outputEscapingEnabled' => false, + 'defaultPaper' => 'A4', ); // Test default value @@ -133,6 +223,16 @@ class SettingsTest extends \PHPUnit\Framework\TestCase // Test with valid file $this->assertEquals($expected, Settings::loadConfig(__DIR__ . '/../../phpword.ini.dist')); + foreach ($expected as $key => $value) { + if ($key === 'compatibility') { + $meth = 'hasCompatibility'; + } elseif ($key === 'outputEscapingEnabled') { + $meth = 'isOutputEscapingEnabled'; + } else { + $meth = 'get' . ucfirst($key); + } + $this->assertEquals(Settings::$meth(), $value); + } // Test with invalid file $this->assertEmpty(Settings::loadConfig(__DIR__ . '/../../phpunit.xml.dist')); diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..25fe836a 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,26 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); From 6da3fd3c066bd004e314ea3a138b304b983b2646 Mon Sep 17 00:00:00 2001 From: oleibman Date: Thu, 16 Apr 2020 23:55:24 -0700 Subject: [PATCH 037/112] Correct Scrutinizer Warning Type was declared incorrectly in comment. --- src/PhpWord/Settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php index c493132f..80e512d2 100644 --- a/src/PhpWord/Settings.php +++ b/src/PhpWord/Settings.php @@ -122,7 +122,7 @@ class Settings /** * Default paper - * @var int + * @var string */ private static $defaultPaper = self::DEFAULT_PAPER; From ba3d6162825d23a999064dbc856410912e86939b Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Thu, 23 Apr 2020 17:25:56 -0700 Subject: [PATCH 038/112] Improve Test Coverage Coverage for Writer/ODText is now 100%. --- src/PhpWord/Element/Section.php | 2 + src/PhpWord/Writer/ODText/Element/Text.php | 10 +- tests/PhpWord/Element/ImageTest.php | 4 +- tests/PhpWord/Writer/ODText/ElementTest.php | 100 ++++++++++++++++++++ 4 files changed, 110 insertions(+), 6 deletions(-) diff --git a/src/PhpWord/Element/Section.php b/src/PhpWord/Element/Section.php index b6da9f3b..caf2ca27 100644 --- a/src/PhpWord/Element/Section.php +++ b/src/PhpWord/Element/Section.php @@ -157,6 +157,8 @@ class Section extends AbstractContainer * @deprecated Use the `getFootnoteProperties` method instead * * @return FootnoteProperties + * + * @codeCoverageIgnore */ public function getFootnotePropoperties() { diff --git a/src/PhpWord/Writer/ODText/Element/Text.php b/src/PhpWord/Writer/ODText/Element/Text.php index 2bf9908d..464d2777 100644 --- a/src/PhpWord/Writer/ODText/Element/Text.php +++ b/src/PhpWord/Writer/ODText/Element/Text.php @@ -42,12 +42,12 @@ class Text extends AbstractElement // @todo Commented for TextRun. Should really checkout this value // $fStyleIsObject = ($fontStyle instanceof Font) ? true : false; - $fStyleIsObject = false; + //$fStyleIsObject = false; - if ($fStyleIsObject) { - // Don't never be the case, because I browse all sections for cleaning all styles not declared - throw new Exception('PhpWord : $fStyleIsObject wouldn\'t be an object'); - } + //if ($fStyleIsObject) { + // Don't never be the case, because I browse all sections for cleaning all styles not declared + // throw new Exception('PhpWord : $fStyleIsObject wouldn\'t be an object'); + //} if (!$this->withoutP) { $xmlWriter->startElement('text:p'); // text:p diff --git a/tests/PhpWord/Element/ImageTest.php b/tests/PhpWord/Element/ImageTest.php index f56d0794..e83be708 100644 --- a/tests/PhpWord/Element/ImageTest.php +++ b/tests/PhpWord/Element/ImageTest.php @@ -77,10 +77,12 @@ class ImageTest extends AbstractWebServerEmbeddedTest foreach ($images as $imageData) { list($source, $type, $extension, $createFunction, $imageFunction) = $imageData; + $nam = ucfirst(strtok($source, '.')); $source = __DIR__ . "/../_files/images/{$source}"; - $image = new Image($source); + $image = new Image($source, null, null, $nam); $this->assertInstanceOf('PhpOffice\\PhpWord\\Element\\Image', $image); $this->assertEquals($source, $image->getSource()); + $this->assertEquals($nam, $image->getName()); $this->assertEquals(md5($source), $image->getMediaId()); $this->assertEquals($type, $image->getImageType()); $this->assertEquals($extension, $image->getImageExtension()); diff --git a/tests/PhpWord/Writer/ODText/ElementTest.php b/tests/PhpWord/Writer/ODText/ElementTest.php index afad150d..eda4568d 100644 --- a/tests/PhpWord/Writer/ODText/ElementTest.php +++ b/tests/PhpWord/Writer/ODText/ElementTest.php @@ -18,6 +18,7 @@ namespace PhpOffice\PhpWord\Writer\ODText; use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\TestHelperDOCX; @@ -187,6 +188,47 @@ class ElementTest extends \PHPUnit\Framework\TestCase $this->assertEquals('#333333', $doc->getElementAttribute($element, 'fo:color')); } + /** + * Test title specified as text run rather than text + */ + public function testTextRunTitle() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $phpWord->addTitleStyle(1, array('name' => 'Times New Roman', 'size' => 18, 'bold' => true)); + $section = $phpWord->addSection(); + $section->addTitle('Text Title', 1); + $section->addText('Text following Text Title'); + $textRun = new \PhpOffice\PhpWord\Element\TextRun(); + $textRun->addText('Text Run'); + $textRun->addText(' Title'); + $section->addTitle($textRun, 1); + $section->addText('Text following Text Run Title'); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $p2t = '/office:document-content/office:body/office:text/text:section'; + + $element = "$p2t/text:h[1]"; + $this->assertEquals('HE1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('1', $doc->getElementAttribute($element, 'text:outline-level')); + $span = "$element/text:span"; + $this->assertEquals('Text Title', $doc->getElement($span)->textContent); + $this->assertEquals('Heading_1', $doc->getElementAttribute($span, 'text:style-name')); + $element = "$p2t/text:p[2]/text:span"; + $this->assertEquals('Text following Text Title', $doc->getElement($element)->nodeValue); + + $element = "$p2t/text:h[2]"; + $this->assertEquals('HD1', $doc->getElementAttribute($element, 'text:style-name')); + $this->assertEquals('1', $doc->getElementAttribute($element, 'text:outline-level')); + $span = "$element/text:span"; + $this->assertEquals('Text Run', $doc->getElement("$span/text:span[1]")->textContent); + $this->assertTrue($doc->elementExists("$span/text:span[2]/text:s")); + $this->assertEquals('Title', $doc->getElement("$span/text:span[2]")->textContent); + $this->assertEquals('Heading_1', $doc->getElementAttribute($span, 'text:style-name')); + $element = "$p2t/text:p[3]/text:span"; + $this->assertEquals('Text following Text Run Title', $doc->getElement($element)->nodeValue); + } + /** * Test correct writing of text with ampersand in it */ @@ -225,4 +267,62 @@ class ElementTest extends \PHPUnit\Framework\TestCase self::assertTrue($doc->elementExists($element, 'content.xml')); self::assertEquals('PB', $doc->getElementAttribute($element, 'text:style-name', 'content.xml')); } + + /** + * Test tracked changes + */ + public function testTrackedChanges() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + + // New portrait section + $section = $phpWord->addSection(); + $textRun = $section->addTextRun(); + + $text = $textRun->addText('Hello World! Time to '); + + $text = $textRun->addText('wake ', array('bold' => true)); + $text->setChangeInfo(TrackChange::INSERTED, 'Fred', time() - 1800); + + $text = $textRun->addText('up'); + $text->setTrackChange(new TrackChange(TrackChange::INSERTED, 'Fred')); + + $text = $textRun->addText('go to sleep'); + $text->setChangeInfo(TrackChange::DELETED, 'Barney', new \DateTime('@' . (time() - 3600))); + + $doc = TestHelperDOCX::getDocument($phpWord, 'ODText'); + + $tcs = '/office:document-content/office:body/office:text/text:tracked-changes'; + $tc1 = "$tcs/text:changed-region[1]"; + $tc1id = $doc->getElementAttribute($tc1, 'text:id'); + $element = "$tc1/text:insertion"; + self::assertTrue($doc->elementExists($element)); + $element .= '/office:change-info'; + self::AssertEquals('Fred', $doc->getElement("$element/dc:creator")->nodeValue); + self::assertTrue($doc->elementExists("$element/dc:date")); + + $tc2 = "$tcs/text:changed-region[2]"; + $tc2id = $doc->getElementAttribute($tc2, 'text:id'); + $element = "$tc2/text:insertion"; + self::assertTrue($doc->elementExists($element)); + $element .= '/office:change-info'; + self::AssertEquals('Fred', $doc->getElement("$element/dc:creator")->nodeValue); + //self::assertTrue($doc->elementExists("$element/dc:date")); + + $tc3 = "$tcs/text:changed-region[3]"; + $tc3id = $doc->getElementAttribute($tc3, 'text:id'); + $element = "$tc3/text:deletion"; + self::assertTrue($doc->elementExists($element)); + $element .= '/office:change-info'; + self::AssertEquals('Barney', $doc->getElement("$element/dc:creator")->nodeValue); + self::assertTrue($doc->elementExists("$element/dc:date")); + + $p2t = '/office:document-content/office:body/office:text/text:section/text:p[2]'; + $element = "$p2t/text:span[2]/text:change-start"; + self::AssertEquals($tc1id, $doc->getElementAttribute($element, 'text:change-id')); + $element = "$p2t/text:span[3]/text:change-start"; + self::AssertEquals($tc2id, $doc->getElementAttribute($element, 'text:change-id')); + $element = "$p2t/text:change"; + self::AssertEquals($tc3id, $doc->getElementAttribute($element, 'text:change-id')); + } } From 3738a6806eb4ee1b333b285d8b5c08be7af41ab5 Mon Sep 17 00:00:00 2001 From: Owen Leibman Date: Mon, 27 Apr 2020 21:29:27 -0700 Subject: [PATCH 039/112] Improve Word2007 Test Coverage After this change, Writer/Word2007 is 100% covered. One source change is required. Writer/Word2007/Style/AbstractStyle has incorrectly searched Measurement Array using in_array (which searches values) rather than array_key_exists (keys). There was no test for this, and now there is. 3 changes in tests/PhpWord/_includes are borrowed from "ODT Changes" (pull request 1796, not yet merged) and "Fix PHPUnit Tests" (pull request 1771, merged after work on this change was started). Writer/Word2007/ElementTest was becoming too unwieldy. Tests for Chart and FormFields were moved to their own members. --- .../Writer/Word2007/Style/AbstractStyle.php | 2 +- .../Writer/Word2007/Element/ChartTest.php | 241 ++++++++++++++++++ .../Writer/Word2007/Element/FormFieldTest.php | 70 +++++ tests/PhpWord/Writer/Word2007/ElementTest.php | 50 +--- .../Writer/Word2007/Part/SettingsTest.php | 27 ++ .../Writer/Word2007/Style/ImageTest.php | 36 +++ .../Writer/Word2007/Style/SectionTest.php | 57 +++++ .../AbstractWebServerEmbeddedTest.php | 21 +- tests/PhpWord/_includes/TestHelperDOCX.php | 7 +- tests/PhpWord/_includes/XmlDocument.php | 62 ++++- 10 files changed, 516 insertions(+), 57 deletions(-) create mode 100644 tests/PhpWord/Writer/Word2007/Element/ChartTest.php create mode 100644 tests/PhpWord/Writer/Word2007/Element/FormFieldTest.php create mode 100644 tests/PhpWord/Writer/Word2007/Style/SectionTest.php diff --git a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php index fcd4aeb6..877ff1db 100644 --- a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php @@ -96,7 +96,7 @@ abstract class AbstractStyle ); $unit = Settings::getMeasurementUnit(); $factor = 1; - if (in_array($unit, $factors) && $value != $default) { + if (array_key_exists($unit, $factors) && $value != $default) { $factor = $factors[$unit]; } diff --git a/tests/PhpWord/Writer/Word2007/Element/ChartTest.php b/tests/PhpWord/Writer/Word2007/Element/ChartTest.php new file mode 100644 index 00000000..432d8db8 --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Element/ChartTest.php @@ -0,0 +1,241 @@ +outputEscapingEnabled = Settings::isOutputEscapingEnabled(); + } + + /** + * Executed after each method of the class + */ + public function tearDown() + { + Settings::setOutputEscapingEnabled($this->outputEscapingEnabled); + TestHelperDOCX::clear(); + } + + /** + * Test chart elements + */ + public function testChartElements() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $style = array( + 'width' => 5000000, + 'height' => 5000000, + 'showAxisLabels' => true, + 'showGridX' => true, + 'showGridY' => true, + 'showLegend' => false, + ); + + $chartTypes = array('pie', 'doughnut', 'bar', 'line', 'area', 'scatter', 'radar'); + $categories = array('A', 'B', 'C', 'D', 'E'); + $series1 = array(1, 3, 2, 5, 4); + foreach ($chartTypes as $chartType) { + $section->addChart($chartType, $categories, $series1, $style); + } + $colorArray = array('FFFFFF', '000000', 'FF0000', '00FF00', '0000FF'); + $numColor = count($colorArray); + $chart = $section->addChart('pie', $categories, $series1, $style); + $chart->getStyle()->setColors($colorArray)->setTitle('3d chart')->set3d(true); + $chart = $section->addChart('stacked_bar', $categories, $series1, $style); + $chart->getStyle()->setColors($colorArray)->setShowLegend(true); + $chart = $section->addChart('scatter', $categories, $series1, $style); + $chart->getStyle()->setMajorTickPosition('cross'); + $section->addChart('scatter', $categories, $series1, $style, 'seriesname'); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $index = 0; + foreach ($chartTypes as $chartType) { + ++$index; + $file = "word/charts/chart{$index}.xml"; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; + self::assertTrue($doc->elementExists($path, $file), "chart type $chartType"); + } + + $index = 11; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'scatter'; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; + self::assertEquals('seriesname', $doc->getElement($path . '/c:ser/c:tx/c:strRef/c:strCache/c:pt/c:v')->nodeValue); + + $index = 8; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'pie3D'; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; + for ($idx = 0; $idx < $numColor; ++$idx) { + $idxp1 = $idx + 1; + $element = $path . "/c:ser/c:dPt[$idxp1]/c:spPr/a:solidFill/a:srgbClr"; + self::assertEquals($colorArray[$idx], $doc->getElementAttribute($element, 'val'), "pie3d chart idx=$idx"); + } + + $index = 9; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'bar'; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; + for ($idxp1 = 1; $idxp1 < $numColor; ++$idxp1) { + $idx = $idxp1; // stacked bar chart is shifted + $element = $path . "/c:ser/c:dPt[$idxp1]/c:spPr/a:solidFill/a:srgbClr"; + self::assertEquals($colorArray[$idx], $doc->getElementAttribute($element, 'val'), "bar chart idx=$idx"); + } + } + + public function testChartEscapingEnabled() + { + Settings::setOutputEscapingEnabled(true); + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $style = array( + 'width' => 5000000, + 'height' => 5000000, + 'showAxisLabels' => true, + 'showGridX' => true, + 'showGridY' => true, + 'showLegend' => false, + 'valueAxisTitle' => 'Values', + ); + $categories = array('A&B', 'C', 'E', 'F', 'G'); + $series1 = array(1, 3, 2, 5, 4); + $section->addChart('bar', $categories, $series1, $style); + $doc = TestHelperDOCX::getDocument($phpWord); + + $index = 1; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'bar'; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart/c:ser/c:cat/c:strLit"; + $element = "$path/c:pt[1]/c:v"; + self::assertEquals('A&B', $doc->getElement($element)->nodeValue); + $element = "$path/c:pt[2]/c:v"; + self::assertEquals('C', $doc->getElement($element)->nodeValue); + } + + public function testChartEscapingDisabled() + { + Settings::setOutputEscapingEnabled(false); + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $style = array( + 'width' => 5000000, + 'height' => 5000000, + 'showAxisLabels' => true, + 'showGridX' => true, + 'showGridY' => true, + 'showLegend' => false, + 'valueAxisTitle' => 'Values', + ); + $categories = array('A&B', 'C<D>', 'E', 'F', 'G'); + $series1 = array(1, 3, 2, 5, 4); + $section->addChart('bar', $categories, $series1, $style); + $doc = TestHelperDOCX::getDocument($phpWord); + + $index = 1; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'bar'; + $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart/c:ser/c:cat/c:strLit"; + $element = "$path/c:pt[1]/c:v"; + self::assertEquals('A&B', $doc->getElement($element)->nodeValue); + $element = "$path/c:pt[2]/c:v"; + self::assertEquals('C', $doc->getElement($element)->nodeValue); + } + + public function testValueAxisTitle() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $style = array( + 'width' => 5000000, + 'height' => 5000000, + 'showAxisLabels' => true, + 'showGridX' => true, + 'showGridY' => true, + 'showLegend' => false, + 'valueAxisTitle' => 'Values', + ); + $chartType = 'line'; + $categories = array('A', 'B', 'C', 'D', 'E'); + $series1 = array(1, 3, 2, 5, 4); + $section->addChart($chartType, $categories, $series1, $style); + $doc = TestHelperDOCX::getDocument($phpWord); + + $index = 1; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'line'; + $path = '/c:chartSpace/c:chart/c:plotArea'; + $element = "$path/c:{$chartType}Chart"; + self::assertTrue($doc->elementExists($path)); + $element = "$path/c:valAx"; + self::assertTrue($doc->elementExists($element)); + $element .= '/c:title/c:tx/c:rich/a:p/a:r/a:t'; + self::assertEquals('Values', $doc->getElement($element)->nodeValue); + } + + public function testNoAxisLabels() + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $style = array( + 'width' => 5000000, + 'height' => 5000000, + 'showAxisLabels' => false, + 'showGridX' => true, + 'showGridY' => true, + 'showLegend' => false, + 'valueAxisTitle' => 'Values', + ); + $chartType = 'line'; + $categories = array('A', 'B', 'C', 'D', 'E'); + $series1 = array(1, 3, 2, 5, 4); + $section->addChart($chartType, $categories, $series1, $style); + $doc = TestHelperDOCX::getDocument($phpWord); + + $index = 1; + $file = "word/charts/chart{$index}.xml"; + $doc->setDefaultFile($file); + $chartType = 'line'; + $path = '/c:chartSpace/c:chart/c:plotArea'; + $element = "$path/c:{$chartType}Chart"; + $element = "$path/c:valAx"; + $element .= '/c:tickLblPos'; + self::assertEquals('none', $doc->getElementAttribute($element, 'val')); + } +} diff --git a/tests/PhpWord/Writer/Word2007/Element/FormFieldTest.php b/tests/PhpWord/Writer/Word2007/Element/FormFieldTest.php new file mode 100644 index 00000000..f3ee1790 --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Element/FormFieldTest.php @@ -0,0 +1,70 @@ +addSection(); + + $section->addFormField('textinput')->setName('MyTextBox'); + $section->addFormField('checkbox')->setDefault(true)->setValue('Your name'); + $section->addFormField('checkbox')->setDefault(true); + $section->addFormField('dropdown')->setEntries(array('Choice 1', 'Choice 2', 'Choice 3', '')); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $path = '/w:document/w:body/w:p[1]/w:r/w:fldChar/w:ffData'; + $this->assertTrue($doc->elementExists("$path/w:textInput")); + $this->assertEquals('MyTextBox', $doc->getElementAttribute("$path/w:name", 'w:val')); + + $path = '/w:document/w:body/w:p[2]/w:r/w:fldChar/w:ffData'; + $this->assertTrue($doc->elementExists("$path/w:checkBox")); + $path = '/w:document/w:body/w:p[2]/w:r[4]/w:t'; + $this->assertEquals('Your name', $doc->getElement($path)->textContent); + + $path = '/w:document/w:body/w:p[3]/w:r/w:fldChar/w:ffData'; + $this->assertTrue($doc->elementExists("$path/w:checkBox")); + + $path = '/w:document/w:body/w:p[4]/w:r/w:fldChar/w:ffData/w:ddList'; + $this->assertTrue($doc->elementExists($path)); + $this->assertEquals('Choice 1', $doc->getElementAttribute("$path/w:listEntry[1]", 'w:val')); + $this->assertEquals('Choice 2', $doc->getElementAttribute("$path/w:listEntry[2]", 'w:val')); + $this->assertEquals('Choice 3', $doc->getElementAttribute("$path/w:listEntry[3]", 'w:val')); + $this->assertEquals('', trim($doc->getElementAttribute("$path/w:listEntry[4]", 'w:val'), ' ')); + } +} diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php index 6a295965..183ef553 100644 --- a/tests/PhpWord/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Writer/Word2007/ElementTest.php @@ -249,33 +249,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase } } - /** - * Test shape elements - */ - public function testChartElements() - { - $phpWord = new PhpWord(); - $section = $phpWord->addSection(); - $style = array('width' => 1000000, 'height' => 1000000, 'showAxisLabels' => true, 'showGridX' => true, 'showGridY' => true); - - $chartTypes = array('pie', 'doughnut', 'bar', 'line', 'area', 'scatter', 'radar'); - $categories = array('A', 'B', 'C', 'D', 'E'); - $series1 = array(1, 3, 2, 5, 4); - foreach ($chartTypes as $chartType) { - $section->addChart($chartType, $categories, $series1, $style); - } - $section->addChart('pie', $categories, $series1, array('3d' => true)); - - $doc = TestHelperDOCX::getDocument($phpWord); - - $index = 0; - foreach ($chartTypes as $chartType) { - ++$index; - $file = "word/charts/chart{$index}.xml"; - $path = "/c:chartSpace/c:chart/c:plotArea/c:{$chartType}Chart"; - $this->assertTrue($doc->elementExists($path, $file)); - } - } + // testChartElements moved to Writer/Word2007/Element/ChartTest public function testFieldElement() { @@ -354,27 +328,7 @@ class ElementTest extends \PHPUnit\Framework\TestCase $this->assertEquals(' MACROBUTTON Zoom100 double click to zoom ', $doc->getElement($element)->textContent); } - /** - * Test form fields - */ - public function testFormFieldElements() - { - $phpWord = new PhpWord(); - $section = $phpWord->addSection(); - - $section->addFormField('textinput')->setName('MyTextBox'); - $section->addFormField('checkbox')->setDefault(true)->setValue('Your name'); - $section->addFormField('checkbox')->setDefault(true); - $section->addFormField('dropdown')->setEntries(array('Choice 1', 'Choice 2', 'Choice 3')); - - $doc = TestHelperDOCX::getDocument($phpWord); - - $path = '/w:document/w:body/w:p[%d]/w:r/w:fldChar/w:ffData'; - $this->assertTrue($doc->elementExists(sprintf($path, 1) . '/w:textInput')); - $this->assertTrue($doc->elementExists(sprintf($path, 2) . '/w:checkBox')); - $this->assertTrue($doc->elementExists(sprintf($path, 3) . '/w:checkBox')); - $this->assertTrue($doc->elementExists(sprintf($path, 4) . '/w:ddList')); - } + // testFormFieldElements moved to Writer/Word2007/Element/FormFieldTest /** * Test SDT elements diff --git a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php index 8a21e827..fcf5cabc 100644 --- a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php @@ -67,6 +67,8 @@ class SettingsTest extends \PHPUnit\Framework\TestCase $phpWord->getSettings()->getDocumentProtection()->setSalt(base64_decode('uq81pJRRGFIY5U+E9gt8tA==')); $phpWord->getSettings()->getDocumentProtection()->setAlgorithm(PasswordEncoder::ALGORITHM_MD2); $phpWord->getSettings()->getDocumentProtection()->setSpinCount(10); + $sect = $phpWord->addSection(); + $sect->addText('This is a protected document'); $doc = TestHelperDOCX::getDocument($phpWord); @@ -79,6 +81,31 @@ class SettingsTest extends \PHPUnit\Framework\TestCase $this->assertEquals('10', $doc->getElement($path, $file)->getAttribute('w:cryptSpinCount')); } + /** + * Test document protection with password without setting salt + */ + public function testDocumentProtectionWithPasswordNoSalt() + { + $phpWord = new PhpWord(); + $phpWord->getSettings()->getDocumentProtection()->setEditing('readOnly'); + $phpWord->getSettings()->getDocumentProtection()->setPassword('testÄö@€!$&'); + //$phpWord->getSettings()->getDocumentProtection()->setSalt(base64_decode('uq81pJRRGFIY5U+E9gt8tA==')); + $phpWord->getSettings()->getDocumentProtection()->setAlgorithm(PasswordEncoder::ALGORITHM_MD2); + $phpWord->getSettings()->getDocumentProtection()->setSpinCount(10); + $sect = $phpWord->addSection(); + $sect->addText('This is a protected document'); + + $doc = TestHelperDOCX::getDocument($phpWord); + + $file = 'word/settings.xml'; + + $path = '/w:settings/w:documentProtection'; + $this->assertTrue($doc->elementExists($path, $file)); + //$this->assertEquals('rUuJbk6LuN2/qFyp7IUPQA==', $doc->getElement($path, $file)->getAttribute('w:hash')); + $this->assertEquals('1', $doc->getElement($path, $file)->getAttribute('w:cryptAlgorithmSid')); + $this->assertEquals('10', $doc->getElement($path, $file)->getAttribute('w:cryptSpinCount')); + } + /** * Test compatibility */ diff --git a/tests/PhpWord/Writer/Word2007/Style/ImageTest.php b/tests/PhpWord/Writer/Word2007/Style/ImageTest.php index efa0a105..c2cbfcae 100644 --- a/tests/PhpWord/Writer/Word2007/Style/ImageTest.php +++ b/tests/PhpWord/Writer/Word2007/Style/ImageTest.php @@ -54,6 +54,42 @@ class ImageTest extends \PHPUnit\Framework\TestCase $section->addImage(__DIR__ . '/../../../_files/images/earth.jpg', $styles); $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $path = '/w:document/w:body/w:p[1]/w:r/w:rPr/w:position'; + $this->assertFalse($doc->elementExists($path)); + $path = '/w:document/w:body/w:p[1]/w:r/w:pict/v:shape'; + $this->assertTrue($doc->elementExists($path . '/w10:wrap')); + $this->assertEquals('inline', $doc->getElementAttribute($path . '/w10:wrap', 'type')); + + $this->assertTrue($doc->elementExists($path)); + $style = $doc->getElement($path)->getAttribute('style'); + $this->assertNotNull($style); + $this->assertContains('mso-wrap-distance-left:10pt;', $style); + $this->assertContains('mso-wrap-distance-right:20pt;', $style); + $this->assertContains('mso-wrap-distance-top:30pt;', $style); + $this->assertContains('mso-wrap-distance-bottom:40pt;', $style); + } + + /** + * Test writing image wrapping + */ + public function testWrappingWithPosition() + { + $styles = array( + 'wrap' => Image::WRAP_INLINE, + 'wrapDistanceLeft' => 10, + 'wrapDistanceRight' => 20, + 'wrapDistanceTop' => 30, + 'wrapDistanceBottom' => 40, + 'position' => 10, + ); + + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + $section->addImage(__DIR__ . '/../../../_files/images/earth.jpg', $styles); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + $path = '/w:document/w:body/w:p[1]/w:r/w:rPr/w:position'; + $this->assertEquals('10', $doc->getElement($path)->getAttribute('w:val')); $path = '/w:document/w:body/w:p[1]/w:r/w:pict/v:shape'; $this->assertTrue($doc->elementExists($path . '/w10:wrap')); $this->assertEquals('inline', $doc->getElementAttribute($path . '/w10:wrap', 'type')); diff --git a/tests/PhpWord/Writer/Word2007/Style/SectionTest.php b/tests/PhpWord/Writer/Word2007/Style/SectionTest.php new file mode 100644 index 00000000..74e1eadd --- /dev/null +++ b/tests/PhpWord/Writer/Word2007/Style/SectionTest.php @@ -0,0 +1,57 @@ +addSection(); + $section->getStyle()->setMarginTop(0.1)->setMarginBottom(0.4)->setMarginLeft(0.2)->setMarginRight(0.3); + $section->addText('test'); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + Settings::setMeasurementUnit($unit); + + $path = '/w:document/w:body/w:sectPr/w:pgMar'; + $this->assertEquals('144', $doc->getElementAttribute($path, 'w:top')); + $this->assertEquals('432', $doc->getElementAttribute($path, 'w:right')); + $this->assertEquals('576', $doc->getElementAttribute($path, 'w:bottom')); + $this->assertEquals('288', $doc->getElementAttribute($path, 'w:left')); + } +} diff --git a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php index 9316a9fe..25fe836a 100644 --- a/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php +++ b/tests/PhpWord/_includes/AbstractWebServerEmbeddedTest.php @@ -26,7 +26,26 @@ abstract class AbstractWebServerEmbeddedTest extends \PHPUnit\Framework\TestCase public static function setUpBeforeClass() { if (self::isBuiltinServerSupported()) { - self::$httpServer = new Process('php -S localhost:8080 -t tests/PhpWord/_files'); + $commandLine = 'php -S localhost:8080 -t tests/PhpWord/_files'; + + /* + * Make sure to invoke \Symfony\Component\Process\Process correctly + * regardless of PHP version used. + * + * In Process version >= 5 / PHP >= 7.2.5, the constructor requires + * an array, while in version < 3.3 / PHP < 5.5.9 it requires a string. + * In between, it can accept both. + * + * Process::fromShellCommandLine() was introduced in version 4.2.0, + * to enable recent versions of Process to parse a command string, + * so if it is not available it means it is still possible to pass + * a string to the constructor. + */ + if (method_exists('Symfony\Component\Process\Process', 'fromShellCommandLine')) { + self::$httpServer = Process::fromShellCommandline($commandLine); + } else { + self::$httpServer = new Process($commandLine); + } self::$httpServer->start(); while (!self::$httpServer->isRunning()) { usleep(1000); diff --git a/tests/PhpWord/_includes/TestHelperDOCX.php b/tests/PhpWord/_includes/TestHelperDOCX.php index 02fa7d78..d35f0e3f 100644 --- a/tests/PhpWord/_includes/TestHelperDOCX.php +++ b/tests/PhpWord/_includes/TestHelperDOCX.php @@ -63,7 +63,12 @@ class TestHelperDOCX $zip->close(); } - return new XmlDocument(Settings::getTempDir() . '/PhpWord_Unit_Test/'); + $doc = new XmlDocument(Settings::getTempDir() . '/PhpWord_Unit_Test/'); + if ($writerName === 'ODText') { + $doc->setDefaultFile('content.xml'); + } + + return $doc; } /** diff --git a/tests/PhpWord/_includes/XmlDocument.php b/tests/PhpWord/_includes/XmlDocument.php index 3a7869bc..41a9d9db 100644 --- a/tests/PhpWord/_includes/XmlDocument.php +++ b/tests/PhpWord/_includes/XmlDocument.php @@ -50,6 +50,37 @@ class XmlDocument */ private $file; + /** + * Default file name + * + * @var string + */ + private $defaultFile = 'word/document.xml'; + + /** + * Get default file + * + * @return string + */ + public function getDefaultFile() + { + return $this->defaultFile; + } + + /** + * Set default file + * + * @param string $file + * @return string + */ + public function setDefaultFile($file) + { + $temp = $this->defaultFile; + $this->defaultFile = $file; + + return $temp; + } + /** * Create new instance * @@ -66,8 +97,11 @@ class XmlDocument * @param string $file * @return \DOMDocument */ - public function getFileDom($file = 'word/document.xml') + public function getFileDom($file = '') { + if (!$file) { + $file = $this->defaultFile; + } if (null !== $this->dom && $file === $this->file) { return $this->dom; } @@ -91,8 +125,11 @@ class XmlDocument * @param string $file * @return \DOMNodeList */ - public function getNodeList($path, $file = 'word/document.xml') + public function getNodeList($path, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } if (null === $this->dom || $file !== $this->file) { $this->getFileDom($file); } @@ -112,8 +149,11 @@ class XmlDocument * @param string $file * @return \DOMElement */ - public function getElement($path, $file = 'word/document.xml') + public function getElement($path, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } $elements = $this->getNodeList($path, $file); return $elements->item(0); @@ -147,8 +187,12 @@ class XmlDocument * @param string $file * @return string */ - public function getElementAttribute($path, $attribute, $file = 'word/document.xml') + public function getElementAttribute($path, $attribute, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } + return $this->getElement($path, $file)->getAttribute($attribute); } @@ -159,8 +203,11 @@ class XmlDocument * @param string $file * @return string */ - public function elementExists($path, $file = 'word/document.xml') + public function elementExists($path, $file = '') { + if (!$file) { + $file = $this->defaultFile; + } $nodeList = $this->getNodeList($path, $file); return $nodeList->length != 0; @@ -173,8 +220,11 @@ class XmlDocument * @param string $file * @return string */ - public function printXml($path = '/', $file = 'word/document.xml') + public function printXml($path = '/', $file = '') { + if (!$file) { + $file = $this->defaultFile; + } $element = $this->getElement($path, $file); if ($element instanceof \DOMDocument) { $element->formatOutput = true; From 2c7a30696107571a6b75a3d95307afccb49d8acc Mon Sep 17 00:00:00 2001 From: Sakis bal <43223812+ThanasisMpalatsoukas@users.noreply.github.com> Date: Mon, 1 Jun 2020 12:28:11 +0300 Subject: [PATCH 040/112] Unused variables $rows, $cols Columns are hardcoded in the basic table loop instead of using the predefined $rows and $cols variables --- samples/Sample_09_Tables.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Sample_09_Tables.php b/samples/Sample_09_Tables.php index 32d89573..31155438 100644 --- a/samples/Sample_09_Tables.php +++ b/samples/Sample_09_Tables.php @@ -17,9 +17,9 @@ $cols = 5; $section->addText('Basic table', $header); $table = $section->addTable(); -for ($r = 1; $r <= 8; $r++) { +for ($r = 1; $r <= $rows; $r++) { $table->addRow(); - for ($c = 1; $c <= 5; $c++) { + for ($c = 1; $c <= $cols; $c++) { $table->addCell(1750)->addText("Row {$r}, Cell {$c}"); } } From 701f770ab781d92723ca2e7ae1471466d349040d Mon Sep 17 00:00:00 2001 From: lubosdz Date: Fri, 10 Jul 2020 12:43:19 +0200 Subject: [PATCH 041/112] Html parser (addHtml) - support width in tables & cells --- src/PhpWord/Shared/Html.php | 19 ++++++++- tests/PhpWord/Shared/HtmlTest.php | 70 +++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 54e9509e..cb09af62 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -106,6 +106,19 @@ class Html case 'lang': $styles['lang'] = $attribute->value; break; + case 'width': + // tables, cells + $val = trim($attribute->value); + if(false !== strpos($val, '%')){ + // e.g. or
+ $styles['width'] = intval($val) * 50; + $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT; + }else{ + // e.g. addCell(null, $cellStyles); + + // set cell width to control column widths + $width = isset($cellStyles['width']) ? $cellStyles['width'] : null; + unset($cellStyles['width']); // would not apply + $cell = $element->addCell($width, $cellStyles); if (self::shouldAddTextRun($node)) { return $cell->addTextRun(self::parseInlineStyle($node, $styles['paragraph'])); diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 5bc9e241..5474eb0d 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -632,4 +632,74 @@ class HtmlTest extends AbstractWebServerEmbeddedTest $this->assertTrue($doc->elementExists('/w:document/w:body/w:p/w:r/w:rPr/w:spacing')); $this->assertEquals(150 * 15, $doc->getElement('/w:document/w:body/w:p/w:r/w:rPr/w:spacing')->getAttribute('w:val')); } + + /** + * Parse widths in tables and cells, which also allows for controlling column width + */ + public function testParseTableAndCellWidth() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection([ + 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE, + ]); + + // borders & backgrounds are here just for better visual comparison + $html = << + + + + +
25% + + + + + + + + + + + + + +
400px
T2.R2.C150ptT2.R2.C3
300pxT2.R3.C3
+
+HTML; + + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + // outer table grid + $xpath = '/w:document/w:body/w:tbl/w:tblGrid/w:gridCol'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals(25 * 50, $doc->getElement($xpath)->getAttribute('w:w')); + $this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); + + //
assertTrue($doc->elementExists($xpath)); + $this->assertEquals(6000, $doc->getElement($xpath)->getAttribute('w:w')); + $this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); + + // assertTrue($doc->elementExists($xpath)); + $this->assertEquals(4500, $doc->getElement($xpath)->getAttribute('w:w')); + $this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); + } + } From e180cfe456ee81c210fa0034c740655f25c91144 Mon Sep 17 00:00:00 2001 From: lubosdz Date: Sat, 11 Jul 2020 00:24:08 +0200 Subject: [PATCH 042/112] Html parser (addHtml) - support cellspacing, bgColor --- src/PhpWord/Shared/Html.php | 14 +++++++++-- tests/PhpWord/Shared/HtmlTest.php | 42 +++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index cb09af62..08004b7a 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -96,7 +96,7 @@ class Html $attributes = $node->attributes; // get all the attributes(eg: id, class) foreach ($attributes as $attribute) { - switch ($attribute->name) { + switch (strtolower($attribute->name)) { case 'style': $styles = self::parseStyle($attribute, $styles); break; @@ -119,6 +119,15 @@ class Html $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::TWIP; } break; + case 'cellspacing': + // tables e.g. , where "2" = 2px (always pixels) + $val = intval($attribute->value).'px'; + $styles['cellSpacing'] = Converter::cssToTwip($val); + break; + case 'bgcolor': + // tables, rows, cells e.g. + $styles['bgColor'] = trim($attribute->value, '# '); + break; } } } @@ -519,7 +528,8 @@ class Html foreach ($properties as $property) { list($cKey, $cValue) = array_pad(explode(':', $property, 2), 2, null); $cValue = trim($cValue); - switch (trim($cKey)) { + $cKey = strtolower(trim($cKey)); + switch ($cKey) { case 'text-decoration': switch ($cValue) { case 'underline': diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 5474eb0d..61ebd535 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -702,4 +702,46 @@ HTML; $this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); } + public function testParseCellspacingRowBgColor() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection([ + 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE, + ]); + + // borders & backgrounds are here just for better visual comparison + $html = << + + + + + + + + +
AB
CD
+HTML; + + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + // uncomment to see results + file_put_contents('./table_src.html', $html); + file_put_contents('./table_result_'.time().'.docx', file_get_contents( TestHelperDOCX::getFile() ) ); + + $xpath = '/w:document/w:body/w:tbl/w:tblPr/w:tblCellSpacing'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals(3 * 15, $doc->getElement($xpath)->getAttribute('w:w')); + $this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); + + $xpath = '/w:document/w:body/w:tbl/w:tr[1]/w:tc[1]/w:tcPr/w:shd'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('lightgreen', $doc->getElement($xpath)->getAttribute('w:fill')); + + $xpath = '/w:document/w:body/w:tbl/w:tr[2]/w:tc[1]/w:tcPr/w:shd'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('FF0000', $doc->getElement($xpath)->getAttribute('w:fill')); + } + } From ca5f08130230066ffc9ac2feee797f08b6bc2faa Mon Sep 17 00:00:00 2001 From: lubosdz Date: Sat, 11 Jul 2020 15:42:28 +0200 Subject: [PATCH 043/112] Html parser (addHtml) - support horizontal rule
--- src/PhpWord/Shared/Html.php | 61 +++++++++++++++++++++++++++++-- tests/PhpWord/Shared/HtmlTest.php | 48 ++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 8 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 08004b7a..9e5d84b1 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -183,6 +183,7 @@ class Html 'img' => array('Image', $node, $element, $styles, null, null, null), 'br' => array('LineBreak', null, $element, $styles, null, null, null), 'a' => array('Link', $node, $element, $styles, null, null, null), + 'hr' => array('HorizRule', $node, $element, $styles, null, null, null), ); $newElement = null; @@ -630,10 +631,27 @@ class Html } break; case 'border': - if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+)\s+([a-z]+)/', $cValue, $matches)) { - $styles['borderSize'] = Converter::cssToPoint($matches[1]); - $styles['borderColor'] = trim($matches[2], '#'); - $styles['borderStyle'] = self::mapBorderStyle($matches[3]); + case 'border-top': + case 'border-bottom': + case 'border-right': + case 'border-left': + // must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid" + // Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC + if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $cValue, $matches)) { + if(false !== strpos($cKey, '-')){ + $which = explode('-', $cKey)[1]; + $which = ucfirst($which); // e.g. bottom -> Bottom + }else{ + $which = ''; + } + // normalization: in HTML 1px means tinest possible line width, so we cannot convert 1px -> 15 twips, coz line'd be bold, we use smallest twip instead + $size = strtolower(trim($matches[1])); + // (!) BC change: up to ver. 0.17.0 Converter was incorrectly converting to points - Converter::cssToPoint($matches[1]) + $size = ($size == '1px') ? 1 : Converter::cssToTwip($size); + // valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc .. + $styles["border{$which}Size"] = $size; // twips + $styles["border{$which}Color"] = trim($matches[2], '#'); + $styles["border{$which}Style"] = self::mapBorderStyle($matches[3]); } break; } @@ -835,4 +853,39 @@ class Html return $element->addLink($target, $node->textContent, $styles['font'], $styles['paragraph']); } + + /** + * Render horizontal rule + * Note: Word rule is not the same as HTML's
since it does not support width and thus neither alignment + * + * @param \DOMNode $node + * @param \PhpOffice\PhpWord\Element\AbstractContainer $element + */ + protected static function parseHorizRule($node, $element) + { + $styles = self::parseInlineStyle($node); + + //
is implemented as an empty paragraph - extending 100% inside the section + // Some properties may be controlled, e.g.
+ + $fontStyle = $styles + ['size' => 3]; + + $paragraphStyle = $styles + [ + 'lineHeight' => 0.25, // multiply default line height - e.g. 1, 1.5 etc + 'spacing' => 0, // twip + 'spaceBefore' => 120, // twip, 240/2 (default line height) + 'spaceAfter' => 120, // twip + 'borderBottomSize' => empty($styles['line-height']) ? 1 : $styles['line-height'], + 'borderBottomColor' => empty($styles['color']) ? '000000' : $styles['color'], + 'borderBottomStyle' => 'single', // same as "solid" + ]; + + $element->addText("", $fontStyle, $paragraphStyle); + + // Notes:
cannot be: + // - table - throws error "cannot be inside textruns", e.g. lists + // - line - that is a shape, has different behaviour + // - repeated text, e.g. underline "_", because of unpredictable line wrapping + } + } diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 61ebd535..52d641af 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -702,6 +702,9 @@ HTML; $this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type')); } + /** + * Test parsing background color for table rows and table cellspacing + */ public function testParseCellspacingRowBgColor() { $phpWord = new \PhpOffice\PhpWord\PhpWord(); @@ -726,10 +729,6 @@ HTML; Html::addHtml($section, $html); $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); - // uncomment to see results - file_put_contents('./table_src.html', $html); - file_put_contents('./table_result_'.time().'.docx', file_get_contents( TestHelperDOCX::getFile() ) ); - $xpath = '/w:document/w:body/w:tbl/w:tblPr/w:tblCellSpacing'; $this->assertTrue($doc->elementExists($xpath)); $this->assertEquals(3 * 15, $doc->getElement($xpath)->getAttribute('w:w')); @@ -744,4 +743,45 @@ HTML; $this->assertEquals('FF0000', $doc->getElement($xpath)->getAttribute('w:fill')); } + /** + * Parse horizontal rule + */ + public function testParseHorizRule() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + + // borders & backgrounds are here just for better visual comparison + $html = <<Simple default rule:

+
+

Custom style rule:

+
+

END

+HTML; + + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + // default rule + $xpath = '/w:document/w:body/w:p[2]/w:pPr/w:pBdr/w:bottom'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('single', $doc->getElement($xpath)->getAttribute('w:val')); // solid + $this->assertEquals('1', $doc->getElement($xpath)->getAttribute('w:sz')); // 1 twip + $this->assertEquals('000000', $doc->getElement($xpath)->getAttribute('w:color')); // black + + // custom style rule + $xpath = '/w:document/w:body/w:p[4]/w:pPr/w:pBdr/w:bottom'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('single', $doc->getElement($xpath)->getAttribute('w:val')); + $this->assertEquals(5 * 15, $doc->getElement($xpath)->getAttribute('w:sz')); + $this->assertEquals('lightblue', $doc->getElement($xpath)->getAttribute('w:color')); + + $xpath = '/w:document/w:body/w:p[4]/w:pPr/w:spacing'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals(22.5, $doc->getElement($xpath)->getAttribute('w:before')); + $this->assertEquals(0, $doc->getElement($xpath)->getAttribute('w:after')); + $this->assertEquals(240, $doc->getElement($xpath)->getAttribute('w:line')); + } + } From 108c1cdc558dc3e53f01526b7cd540e78d26a4b1 Mon Sep 17 00:00:00 2001 From: lubosdz Date: Sat, 11 Jul 2020 17:20:36 +0200 Subject: [PATCH 044/112] Html parser (addHtml) - support attributes start, type in ordered list
    --- src/PhpWord/Shared/Html.php | 48 ++++++++++++++++++++++++- tests/PhpWord/Shared/HtmlTest.php | 60 +++++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 9e5d84b1..7e0848b1 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -447,7 +447,31 @@ class Html } else { $data['listdepth'] = 0; $styles['list'] = 'listStyle_' . self::$listIndex++; - $element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList)); + $style = $element->getPhpWord()->addNumberingStyle($styles['list'], self::getListStyle($isOrderedList)); + + // extract attributes start & type e.g.
      + $start = 0; + $type = ''; + foreach ($node->attributes as $attribute) { + switch ($attribute->name) { + case 'start': + $start = (int) $attribute->value; + break; + case 'type': + $type = $attribute->value; + break; + } + } + + $levels = $style->getLevels(); + /** @var \PhpOffice\PhpWord\Style\NumberingLevel */ + $level = $levels[0]; + if($start > 0){ + $level->setStart($start); + } + if($type && !!($type = self::mapListType($type))){ + $level->setFormat($type); + } } if ($node->parentNode->nodeName === 'li') { return $element->getParent(); @@ -818,6 +842,28 @@ class Html } } + /** + * Map list style for ordered list + * + * @param string $cssListType + */ + protected static function mapListType($cssListType) + { + switch ($cssListType) { + case 'a': + return NumberFormat::LOWER_LETTER; // a, b, c, .. + case 'A': + return NumberFormat::UPPER_LETTER; // A, B, C, .. + case 'i': + return NumberFormat::LOWER_ROMAN; // i, ii, iii, iv, .. + case 'I': + return NumberFormat::UPPER_ROMAN; // I, II, III, IV, .. + case '1': + default: + return NumberFormat::DECIMAL; // 1, 2, 3, .. + } + } + /** * Parse line break * diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 52d641af..2c1e2d07 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -784,4 +784,64 @@ HTML; $this->assertEquals(240, $doc->getElement($xpath)->getAttribute('w:line')); } + /** + * Parse ordered list start & numbering style + */ + public function testParseOrderedList() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + + // borders & backgrounds are here just for better visual comparison + $html = << +
    1. standard ordered list line 1
    2. +
    3. standard ordered list line 2
    4. +
    + +
      +
    1. ordered list alphabetical, line 5 => E
    2. +
    3. ordered list alphabetical, line 6 => F
    4. +
    + +
      +
    1. ordered list roman lower, line 3 => iii
    2. +
    3. ordered list roman lower, line 4 => iv
    4. +
    + +HTML; + + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + // compare numbering file + $xmlFile = 'word/numbering.xml'; + + // default - decimal start = 1 + $xpath = '/w:numbering/w:abstractNum[1]/w:lvl[1]/w:start'; + $this->assertTrue($doc->elementExists($xpath, $xmlFile)); + $this->assertEquals('1', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); + + $xpath = '/w:numbering/w:abstractNum[1]/w:lvl[1]/w:numFmt'; + $this->assertTrue($doc->elementExists($xpath, $xmlFile)); + $this->assertEquals('decimal', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); + + // second list - start = 5, type A = upperLetter + $xpath = '/w:numbering/w:abstractNum[2]/w:lvl[1]/w:start'; + $this->assertTrue($doc->elementExists($xpath, $xmlFile)); + $this->assertEquals('5', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); + + $xpath = '/w:numbering/w:abstractNum[2]/w:lvl[1]/w:numFmt'; + $this->assertTrue($doc->elementExists($xpath, $xmlFile)); + $this->assertEquals('upperLetter', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); + + // third list - start = 3, type i = lowerRoman + $xpath = '/w:numbering/w:abstractNum[3]/w:lvl[1]/w:start'; + $this->assertTrue($doc->elementExists($xpath, $xmlFile)); + $this->assertEquals('3', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); + + $xpath = '/w:numbering/w:abstractNum[3]/w:lvl[1]/w:numFmt'; + $this->assertTrue($doc->elementExists($xpath, $xmlFile)); + $this->assertEquals('lowerRoman', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); + } } From 3066d47003e18461001828f513db3778a652a76f Mon Sep 17 00:00:00 2001 From: lubosdz Date: Sat, 11 Jul 2020 22:47:40 +0200 Subject: [PATCH 045/112] Html parser (addHtml) - support vertical-align, valign --- src/PhpWord/Shared/Html.php | 46 ++++++++++++++++++++++++++++--- tests/PhpWord/Shared/HtmlTest.php | 44 +++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 7e0848b1..bf1a688b 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -96,19 +96,19 @@ class Html $attributes = $node->attributes; // get all the attributes(eg: id, class) foreach ($attributes as $attribute) { + $val = trim($attribute->value); switch (strtolower($attribute->name)) { case 'style': $styles = self::parseStyle($attribute, $styles); break; case 'align': - $styles['alignment'] = self::mapAlign($attribute->value); + $styles['alignment'] = self::mapAlign($val); break; case 'lang': - $styles['lang'] = $attribute->value; + $styles['lang'] = $val; break; case 'width': // tables, cells - $val = trim($attribute->value); if(false !== strpos($val, '%')){ // e.g. or - $styles['bgColor'] = trim($attribute->value, '# '); + $styles['bgColor'] = trim($val, '# '); + break; + case 'valign': + // cells e.g. + + + + + + +
    $styles['width'] = intval($val) * 50; @@ -126,7 +126,13 @@ class Html break; case 'bgcolor': // tables, rows, cells e.g.
    + if (preg_match('#(?:top|bottom|middle|baseline)#i', $val, $matches)) { + $styles['valign'] = self::mapAlignVertical($matches[0]); + } break; } } @@ -678,6 +684,12 @@ class Html $styles["border{$which}Style"] = self::mapBorderStyle($matches[3]); } break; + case 'vertical-align': + // https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align + if (preg_match('#(?:top|bottom|middle|sub|baseline)#i', $cValue, $matches)) { + $styles['valign'] = self::mapAlignVertical($matches[0]); + } + break; } } @@ -842,6 +854,32 @@ class Html } } + /** + * Transforms a HTML/CSS alignment into a \PhpOffice\PhpWord\SimpleType\Jc + * + * @param string $cssAlignment + * @return string|null + */ + protected static function mapAlignVertical($alignment) + { + $alignment = strtolower($alignment); + switch ($alignment) { + case 'top': + case 'baseline': + case 'bottom': + return $alignment; + case 'middle': + return 'center'; + case 'sub': + return 'bottom'; + case 'text-top': + case 'baseline': + return 'top'; + default: + return ''; + } + } + /** * Map list style for ordered list * diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 2c1e2d07..67c9fcaa 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -844,4 +844,48 @@ HTML; $this->assertTrue($doc->elementExists($xpath, $xmlFile)); $this->assertEquals('lowerRoman', $doc->getElement($xpath, $xmlFile)->getAttribute('w:val')); } + + /** + * Parse ordered list start & numbering style + */ + public function testParseVerticalAlign() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + + // borders & backgrounds are here just for better visual comparison + $html = << +
    defaulttopmiddlebottom






    +HTML; + + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + + // uncomment to see results + file_put_contents('./table_src.html', $html); + file_put_contents('./table_result_'.time().'.docx', file_get_contents( TestHelperDOCX::getFile() ) ); + + $xpath = '/w:document/w:body/w:tbl/w:tr/w:tc[1]/w:tcPr/w:vAlign'; + $this->assertFalse($doc->elementExists($xpath)); + + $xpath = '/w:document/w:body/w:tbl/w:tr/w:tc[2]/w:tcPr/w:vAlign'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('top', $doc->getElement($xpath)->getAttribute('w:val')); + + $xpath = '/w:document/w:body/w:tbl/w:tr/w:tc[3]/w:tcPr/w:vAlign'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('center', $doc->getElement($xpath)->getAttribute('w:val')); + + $xpath = '/w:document/w:body/w:tbl/w:tr/w:tc[4]/w:tcPr/w:vAlign'; + $this->assertTrue($doc->elementExists($xpath)); + $this->assertEquals('bottom', $doc->getElement($xpath)->getAttribute('w:val')); + } } From 889f4e338138c1ba4279e7fc92d925cb4469a670 Mon Sep 17 00:00:00 2001 From: lubosdz Date: Sat, 11 Jul 2020 23:03:51 +0200 Subject: [PATCH 046/112] fix converting margin to incorrect unit (points instead of twips) fix image alignment on float - relative to inner margin instead of page margin --- src/PhpWord/Shared/Html.php | 17 ++++++++++++----- tests/PhpWord/Shared/HtmlTest.php | 6 +----- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index bf1a688b..332dd6f8 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -633,11 +633,18 @@ class Html } $styles['italic'] = $tValue; break; + case 'margin': + $cValue = Converter::cssToTwip($cValue); + $styles['spaceBefore'] = $cValue; + $styles['spaceAfter'] = $cValue; + break; case 'margin-top': - $styles['spaceBefore'] = Converter::cssToPoint($cValue); + // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue) + $styles['spaceBefore'] = Converter::cssToTwip($cValue); break; case 'margin-bottom': - $styles['spaceAfter'] = Converter::cssToPoint($cValue); + // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue) + $styles['spaceAfter'] = Converter::cssToTwip($cValue); break; case 'border-color': self::mapBorderColor($styles, $cValue); @@ -676,7 +683,7 @@ class Html } // normalization: in HTML 1px means tinest possible line width, so we cannot convert 1px -> 15 twips, coz line'd be bold, we use smallest twip instead $size = strtolower(trim($matches[1])); - // (!) BC change: up to ver. 0.17.0 Converter was incorrectly converting to points - Converter::cssToPoint($matches[1]) + // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($size) $size = ($size == '1px') ? 1 : Converter::cssToTwip($size); // valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc .. $styles["border{$which}Size"] = $size; // twips @@ -732,14 +739,14 @@ class Html case 'float': if (trim($v) == 'right') { $style['hPos'] = \PhpOffice\PhpWord\Style\Image::POS_RIGHT; - $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE; + $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_MARGIN; // inner section area $style['pos'] = \PhpOffice\PhpWord\Style\Image::POS_RELATIVE; $style['wrap'] = \PhpOffice\PhpWord\Style\Image::WRAP_TIGHT; $style['overlap'] = true; } if (trim($v) == 'left') { $style['hPos'] = \PhpOffice\PhpWord\Style\Image::POS_LEFT; - $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_PAGE; + $style['hPosRelTo'] = \PhpOffice\PhpWord\Style\Image::POS_RELTO_MARGIN; // inner section area $style['pos'] = \PhpOffice\PhpWord\Style\Image::POS_RELATIVE; $style['wrap'] = \PhpOffice\PhpWord\Style\Image::WRAP_TIGHT; $style['overlap'] = true; diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 67c9fcaa..010d1918 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -779,7 +779,7 @@ HTML; $xpath = '/w:document/w:body/w:p[4]/w:pPr/w:spacing'; $this->assertTrue($doc->elementExists($xpath)); - $this->assertEquals(22.5, $doc->getElement($xpath)->getAttribute('w:before')); + $this->assertEquals(450, $doc->getElement($xpath)->getAttribute('w:before')); $this->assertEquals(0, $doc->getElement($xpath)->getAttribute('w:after')); $this->assertEquals(240, $doc->getElement($xpath)->getAttribute('w:line')); } @@ -869,10 +869,6 @@ HTML; Html::addHtml($section, $html); $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); - // uncomment to see results - file_put_contents('./table_src.html', $html); - file_put_contents('./table_result_'.time().'.docx', file_get_contents( TestHelperDOCX::getFile() ) ); - $xpath = '/w:document/w:body/w:tbl/w:tr/w:tc[1]/w:tcPr/w:vAlign'; $this->assertFalse($doc->elementExists($xpath)); From 38788e0c7e1e5b68cd86b3db29a6f8389a44328f Mon Sep 17 00:00:00 2001 From: lubosdz Date: Mon, 13 Jul 2020 18:48:27 +0200 Subject: [PATCH 047/112] Code style --- src/PhpWord/Shared/Html.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 332dd6f8..7d45b4ba 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -109,11 +109,11 @@ class Html break; case 'width': // tables, cells - if(false !== strpos($val, '%')){ - // e.g. or
    + if (false !== strpos($val, '%')) { + // e.g. or
    $styles['width'] = intval($val) * 50; $styles['unit'] = \PhpOffice\PhpWord\SimpleType\TblWidth::PERCENT; - }else{ + } else { // e.g. getLevels(); /** @var \PhpOffice\PhpWord\Style\NumberingLevel */ $level = $levels[0]; - if($start > 0){ + if ($start > 0) { $level->setStart($start); } - if($type && !!($type = self::mapListType($type))){ + if ($type && !!($type = self::mapListType($type))) { $level->setFormat($type); } } @@ -675,10 +675,10 @@ class Html // must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid" // Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $cValue, $matches)) { - if(false !== strpos($cKey, '-')){ + if (false !== strpos($cKey, '-')) { $which = explode('-', $cKey)[1]; $which = ucfirst($which); // e.g. bottom -> Bottom - }else{ + } else { $which = ''; } // normalization: in HTML 1px means tinest possible line width, so we cannot convert 1px -> 15 twips, coz line'd be bold, we use smallest twip instead @@ -883,6 +883,10 @@ class Html case 'baseline': return 'top'; default: + // @discuss - which one should apply: + // - Word uses default vert. alignment: top + // - all browsers use default vert. alignment: middle + // Returning empty string means attribute wont be set so use Word default (top). return ''; } } From 70ad01550bab55c6f1f6558a36945777a18ffb53 Mon Sep 17 00:00:00 2001 From: lubosdz Date: Mon, 13 Jul 2020 19:16:38 +0200 Subject: [PATCH 048/112] Make scrunitizer happier --- src/PhpWord/Shared/Html.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 7d45b4ba..edd418bf 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -96,13 +96,13 @@ class Html $attributes = $node->attributes; // get all the attributes(eg: id, class) foreach ($attributes as $attribute) { - $val = trim($attribute->value); + $val = $attribute->value; switch (strtolower($attribute->name)) { case 'style': $styles = self::parseStyle($attribute, $styles); break; case 'align': - $styles['alignment'] = self::mapAlign($val); + $styles['alignment'] = self::mapAlign(trim($val)); break; case 'lang': $styles['lang'] = $val; @@ -121,7 +121,7 @@ class Html break; case 'cellspacing': // tables e.g.
    , where "2" = 2px (always pixels) - $val = intval($attribute->value).'px'; + $val = intval($val).'px'; $styles['cellSpacing'] = Converter::cssToTwip($val); break; case 'bgcolor': @@ -475,7 +475,8 @@ class Html if ($start > 0) { $level->setStart($start); } - if ($type && !!($type = self::mapListType($type))) { + $type = $type ? self::mapListType($type) : null; + if ($type) { $level->setFormat($type); } } From 4448bda7215c7389bec955988004c4d13a5ac482 Mon Sep 17 00:00:00 2001 From: lubosdz Date: Tue, 14 Jul 2020 01:31:16 +0200 Subject: [PATCH 049/112] Better normalization for width of borders --- src/PhpWord/Shared/Html.php | 20 ++++++++++++-------- tests/PhpWord/Shared/HtmlTest.php | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index edd418bf..c8a7fa69 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -682,10 +682,14 @@ class Html } else { $which = ''; } - // normalization: in HTML 1px means tinest possible line width, so we cannot convert 1px -> 15 twips, coz line'd be bold, we use smallest twip instead - $size = strtolower(trim($matches[1])); - // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($size) - $size = ($size == '1px') ? 1 : Converter::cssToTwip($size); + // Note - border width normalization: + // Width of border in Word is calculated differently than HTML borders, usually showing up too bold. + // Smallest 1px (or 1pt) appears in Word like 2-3px/pt in HTML once converted to twips. + // Therefore we need to normalize converted twip value to cca 1/2 of value. + // This may be adjusted, if better ratio or formula found. + // BC change: up to ver. 0.17.0 was $size converted to points - Converter::cssToPoint($size) + $size = Converter::cssToTwip($matches[1]); + $size = intval($size / 2); // valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc .. $styles["border{$which}Size"] = $size; // twips $styles["border{$which}Color"] = trim($matches[2], '#'); @@ -884,10 +888,10 @@ class Html case 'baseline': return 'top'; default: - // @discuss - which one should apply: - // - Word uses default vert. alignment: top - // - all browsers use default vert. alignment: middle - // Returning empty string means attribute wont be set so use Word default (top). + // @discuss - which one should apply: + // - Word uses default vert. alignment: top + // - all browsers use default vert. alignment: middle + // Returning empty string means attribute wont be set so use Word default (top). return ''; } } diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 010d1918..93df9337 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -774,7 +774,7 @@ HTML; $xpath = '/w:document/w:body/w:p[4]/w:pPr/w:pBdr/w:bottom'; $this->assertTrue($doc->elementExists($xpath)); $this->assertEquals('single', $doc->getElement($xpath)->getAttribute('w:val')); - $this->assertEquals(5 * 15, $doc->getElement($xpath)->getAttribute('w:sz')); + $this->assertEquals(intval(5 * 15 / 2), $doc->getElement($xpath)->getAttribute('w:sz')); $this->assertEquals('lightblue', $doc->getElement($xpath)->getAttribute('w:color')); $xpath = '/w:document/w:body/w:p[4]/w:pPr/w:spacing'; From f69885e7b92e4e149c7a535aaa6dabf5c4d57117 Mon Sep 17 00:00:00 2001 From: lubosdz Date: Wed, 22 Jul 2020 10:04:12 +0200 Subject: [PATCH 050/112] fix bug - don't decode double quotes inside double quoted string --- src/PhpWord/Shared/Html.php | 4 ++-- tests/PhpWord/Shared/HtmlTest.php | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index c8a7fa69..d3e452e4 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -62,10 +62,10 @@ class Html // Preprocess: remove all line ends, decode HTML entity, // fix ampersand and angle brackets and add body tag for HTML fragments $html = str_replace(array("\n", "\r"), '', $html); - $html = str_replace(array('<', '>', '&'), array('_lt_', '_gt_', '_amp_'), $html); + $html = str_replace(array('<', '>', '&', '"'), array('_lt_', '_gt_', '_amp_', '_quot_'), $html); $html = html_entity_decode($html, ENT_QUOTES, 'UTF-8'); $html = str_replace('&', '&', $html); - $html = str_replace(array('_lt_', '_gt_', '_amp_'), array('<', '>', '&'), $html); + $html = str_replace(array('_lt_', '_gt_', '_amp_', '_quot_'), array('<', '>', '&', '"'), $html); if (false === $fullHTML) { $html = '' . $html . ''; diff --git a/tests/PhpWord/Shared/HtmlTest.php b/tests/PhpWord/Shared/HtmlTest.php index 93df9337..7a806c26 100644 --- a/tests/PhpWord/Shared/HtmlTest.php +++ b/tests/PhpWord/Shared/HtmlTest.php @@ -884,4 +884,22 @@ HTML; $this->assertTrue($doc->elementExists($xpath)); $this->assertEquals('bottom', $doc->getElement($xpath)->getAttribute('w:val')); } + + /** + * Fix bug - don't decode double quotes inside double quoted string + */ + public function testDontDecodeAlreadyEncodedDoubleQuotes() + { + $phpWord = new \PhpOffice\PhpWord\PhpWord(); + $section = $phpWord->addSection(); + + // borders & backgrounds are here just for better visual comparison + $html = <<This would crash if inline quotes also decoded at loading XML into DOMDocument! +HTML; + + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord, 'Word2007'); + $this->assertTrue(is_object($doc)); + } } From 69632b3f0333cbdbd51f4c71cdbce67240144b2f Mon Sep 17 00:00:00 2001 From: lubosdz Date: Wed, 22 Jul 2020 10:46:23 +0200 Subject: [PATCH 051/112] remove extra line at the end, which possibly causes CI job fail --- src/PhpWord/Shared/Html.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index d3e452e4..04200b31 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -987,5 +987,4 @@ class Html // - line - that is a shape, has different behaviour // - repeated text, e.g. underline "_", because of unpredictable line wrapping } - } From d6260988bd6df5f20bf662f0467f6d1a10bea1cf Mon Sep 17 00:00:00 2001 From: Maxim Date: Thu, 23 Jul 2020 18:49:25 +0300 Subject: [PATCH 052/112] Allow to redefine TCPDF object Sometimes we need create TCPDF object with modified initial arguments. This change allow rewrite only createExternalWriterInstance() in user defined TCPDF class. --- src/PhpWord/Writer/PDF/TCPDF.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/PhpWord/Writer/PDF/TCPDF.php b/src/PhpWord/Writer/PDF/TCPDF.php index badab046..2558c29e 100644 --- a/src/PhpWord/Writer/PDF/TCPDF.php +++ b/src/PhpWord/Writer/PDF/TCPDF.php @@ -36,6 +36,20 @@ class TCPDF extends AbstractRenderer implements WriterInterface */ protected $includeFile = 'tcpdf.php'; + /** + * Gets the implementation of external PDF library that should be used. + * + * @param string $orientation Page orientation + * @param string $unit Unit measure + * @param string $paperSize Paper size + * + * @return \TCPDF implementation + */ + protected function createExternalWriterInstance($orientation, $unit, $paperSize) + { + return new \TCPDF($orientation, $unit, $paperSize); + } + /** * Save PhpWord to file. * @@ -50,7 +64,7 @@ class TCPDF extends AbstractRenderer implements WriterInterface $orientation = 'P'; // Create PDF - $pdf = new \TCPDF($orientation, 'pt', $paperSize); + $pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize); $pdf->setFontSubsetting(false); $pdf->setPrintHeader(false); $pdf->setPrintFooter(false); From 3a7dd774a2fa723a05b6c3345997f41f838a4b6c Mon Sep 17 00:00:00 2001 From: Sven Ahrens Date: Tue, 1 Sep 2020 18:13:25 +0200 Subject: [PATCH 053/112] Add support for mc:AlternateContent --- .gitignore | 3 +++ src/PhpWord/Reader/Word2007/AbstractPart.php | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/.gitignore b/.gitignore index dd858cea..da661ee3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,6 @@ phpword.ini /.project /nbproject /.php_cs.cache +docker-compose.yml +/phpdocker +/public diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index 06dfe37b..17dbceb4 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -292,6 +292,19 @@ abstract class AbstractPart $parent->addTextBreak(); } elseif ($node->nodeName == 'w:tab') { $parent->addText("\t"); + } elseif ($node->nodeName == 'mc:AlternateContent') { + $hasChildren = $node->childNodes->length > 0; + + if ($hasChildren) { + $origin = $node->childNodes->item($node->childNodes->length - 1); + + if ($origin->nodeValue) { + // TextRun + $textContent = htmlspecialchars($origin->nodeValue, ENT_QUOTES, 'UTF-8'); + + $parent->addText($textContent, $fontStyle, $paragraphStyle); + } + } } elseif ($node->nodeName == 'w:t' || $node->nodeName == 'w:delText') { // TextRun $textContent = htmlspecialchars($xmlReader->getValue('.', $node), ENT_QUOTES, 'UTF-8'); From 63d1ffceb8f5aa627d706fc02d82b6ccd37a7691 Mon Sep 17 00:00:00 2001 From: Sven Ahrens Date: Tue, 1 Sep 2020 18:14:27 +0200 Subject: [PATCH 054/112] Restore gitignore --- .gitignore | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitignore b/.gitignore index da661ee3..dd858cea 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,3 @@ phpword.ini /.project /nbproject /.php_cs.cache -docker-compose.yml -/phpdocker -/public From 58e0200fbdb5634becb4c777f9624998935a38ff Mon Sep 17 00:00:00 2001 From: Sven Ahrens Date: Wed, 2 Sep 2020 12:12:48 +0200 Subject: [PATCH 055/112] Get fallback value instead of taking the last element --- src/PhpWord/Reader/Word2007/AbstractPart.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index 17dbceb4..2bc71b3a 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -293,14 +293,14 @@ abstract class AbstractPart } elseif ($node->nodeName == 'w:tab') { $parent->addText("\t"); } elseif ($node->nodeName == 'mc:AlternateContent') { - $hasChildren = $node->childNodes->length > 0; - - if ($hasChildren) { - $origin = $node->childNodes->item($node->childNodes->length - 1); + if ($node->hasChildNodes()) { + // Get fallback instead of mc:Choice to make sure it is compatible + $fallbackElements = $node->getElementsByTagName('Fallback'); - if ($origin->nodeValue) { + if ($fallbackElements->length) { + $fallback = $fallbackElements->item(0); // TextRun - $textContent = htmlspecialchars($origin->nodeValue, ENT_QUOTES, 'UTF-8'); + $textContent = htmlspecialchars($fallback->nodeValue, ENT_QUOTES, 'UTF-8'); $parent->addText($textContent, $fontStyle, $paragraphStyle); } From ae34ae9518efc2bb6bdfc150e03b24fd72848702 Mon Sep 17 00:00:00 2001 From: Sven Ahrens Date: Wed, 2 Sep 2020 13:03:29 +0200 Subject: [PATCH 056/112] Add testcase --- composer.json | 2 +- tests/PhpWord/Reader/Word2007/ElementTest.php | 43 +++++++++++++++++++ .../PhpWord/_includes/AbstractTestReader.php | 2 +- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index f5f751ec..e8720b7b 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ ], "scripts": { "test": [ - "phpunit --color=always" + "phpunit --color=always --filter testReadAlternateContent" ], "test-no-coverage": [ "phpunit --color=always --no-coverage" diff --git a/tests/PhpWord/Reader/Word2007/ElementTest.php b/tests/PhpWord/Reader/Word2007/ElementTest.php index cb72ef9f..a0875a67 100644 --- a/tests/PhpWord/Reader/Word2007/ElementTest.php +++ b/tests/PhpWord/Reader/Word2007/ElementTest.php @@ -25,6 +25,49 @@ use PhpOffice\PhpWord\Element\TrackChange; */ class ElementTest extends AbstractTestReader { + /** + * Test reading of alternate content value + */ + public function testReadAlternateContent() + { + $documentXml = ' + + + + + + + + + + + + + + Test node value + + + + + + + + + + '; + + $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); + + $elements = $phpWord->getSection(0)->getElements(); + + $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); + $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $elements[0]->getElement(0)); + + $text = $elements[0]; + + $this->assertEquals('Test node value', trim($text->getElement(0)->getText())); + } + /** * Test reading of textbreak */ diff --git a/tests/PhpWord/_includes/AbstractTestReader.php b/tests/PhpWord/_includes/AbstractTestReader.php index d9097d71..12bd437a 100644 --- a/tests/PhpWord/_includes/AbstractTestReader.php +++ b/tests/PhpWord/_includes/AbstractTestReader.php @@ -24,7 +24,7 @@ abstract class AbstractTestReader extends \PHPUnit\Framework\TestCase { private $parts = array( 'styles' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Styles', 'xml' => '{toReplace}'), - 'document' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Document', 'xml' => '{toReplace}'), + 'document' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Document', 'xml' => '{toReplace}'), 'footnotes' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Footnotes', 'xml' => '{toReplace}'), 'endnotes' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Endnotes', 'xml' => '{toReplace}'), 'settings' => array('class' => 'PhpOffice\PhpWord\Reader\Word2007\Settings', 'xml' => '{toReplace}'), From 51034af207cd5f39636a541b7705e33edb09a69c Mon Sep 17 00:00:00 2001 From: Sven Ahrens Date: Wed, 2 Sep 2020 13:03:49 +0200 Subject: [PATCH 057/112] Remove filter option --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e8720b7b..f5f751ec 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ ], "scripts": { "test": [ - "phpunit --color=always --filter testReadAlternateContent" + "phpunit --color=always" ], "test-no-coverage": [ "phpunit --color=always --no-coverage" From 166a136f221b9d2c8dcfd0f740925492ce29d262 Mon Sep 17 00:00:00 2001 From: Sven Ahrens Date: Wed, 2 Sep 2020 14:36:08 +0200 Subject: [PATCH 058/112] Fix spacing --- tests/PhpWord/Reader/Word2007/ElementTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/PhpWord/Reader/Word2007/ElementTest.php b/tests/PhpWord/Reader/Word2007/ElementTest.php index a0875a67..d5db6be8 100644 --- a/tests/PhpWord/Reader/Word2007/ElementTest.php +++ b/tests/PhpWord/Reader/Word2007/ElementTest.php @@ -59,12 +59,9 @@ class ElementTest extends AbstractTestReader $phpWord = $this->getDocumentFromString(array('document' => $documentXml)); $elements = $phpWord->getSection(0)->getElements(); - $this->assertInstanceOf('PhpOffice\PhpWord\Element\TextRun', $elements[0]); $this->assertInstanceOf('PhpOffice\PhpWord\Element\Text', $elements[0]->getElement(0)); - $text = $elements[0]; - $this->assertEquals('Test node value', trim($text->getElement(0)->getText())); } From aa7c1d0fe87c27f49fcb15ce2895301307773707 Mon Sep 17 00:00:00 2001 From: Yannik Firre <3316758+YannikFirre@users.noreply.github.com> Date: Tue, 22 Sep 2020 16:28:52 +0200 Subject: [PATCH 059/112] FIX - When setComplexValue is not found --- src/PhpWord/TemplateProcessor.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7efc0f1a..7f50e3fb 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -274,6 +274,11 @@ class TemplateProcessor $elementWriter->write(); $where = $this->findContainingXmlBlockForMacro($search, 'w:r'); + + if($where === false) { + return ; + } + $block = $this->getSlice($where['start'], $where['end']); $textParts = $this->splitTextIntoTexts($block); $this->replaceXmlBlock($search, $textParts, 'w:r'); From adb917273c3f0504516a9e281ea61e6e97ed1382 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 12:56:06 +0200 Subject: [PATCH 060/112] allow PHP 7.4, 8.0 --- .travis.yml | 11 +++++++---- composer.json | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index acdf95cc..421e772c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,8 @@ php: - 7.1 - 7.2 - 7.3 - - 7.4snapshot + - 7.4 + - nightly matrix: include: @@ -26,14 +27,16 @@ matrix: env: COVERAGE=1 - php: 7.3 env: DEPENDENCIES="--ignore-platform-reqs" + - php: 7.4 + env: DEPENDENCIES="--ignore-platform-reqs" + - php: nightly + env: DEPENDENCIES="--ignore-platform-reqs" exclude: - php: 5.3 - php: 5.4 - php: 5.5 - - php: 7.0 - - php: 7.3 allow_failures: - - php: 7.4snapshot + - php: nightly cache: directories: diff --git a/composer.json b/composer.json index f5f751ec..43738937 100644 --- a/composer.json +++ b/composer.json @@ -58,7 +58,7 @@ "fix": "Fixes issues found by PHP-CS" }, "require": { - "php": "^5.3.3 || ^7.0", + "php": "^5.3.3 || ^7.0 || ^8.0", "ext-xml": "*", "zendframework/zend-escaper": "^2.2", "phpoffice/common": "^0.2.9" @@ -67,13 +67,13 @@ "ext-zip": "*", "ext-gd": "*", "phpunit/phpunit": "^4.8.36 || ^7.0", - "squizlabs/php_codesniffer": "^2.9", + "squizlabs/php_codesniffer": "^2.9 || ^3.5", "friendsofphp/php-cs-fixer": "^2.2", "phpmd/phpmd": "2.*", - "phploc/phploc": "2.* || 3.* || 4.*", + "phploc/phploc": "2.* || 3.* || 4.* || 5.* || 6.* || 7.*", "dompdf/dompdf":"0.8.*", "tecnickcom/tcpdf": "6.*", - "mpdf/mpdf": "5.7.4 || 6.* || 7.*", + "mpdf/mpdf": "5.7.4 || 6.* || 7.* || 8.*", "php-coveralls/php-coveralls": "1.1.0 || ^2.0" }, "suggest": { From e334ecf059aa19b7ba82fa8600501efcb0b45ff4 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 13:39:22 +0200 Subject: [PATCH 061/112] compatibility with phpmd 2.9 --- phpmd.xml.dist | 2 ++ src/PhpWord/Reader/MsDoc.php | 16 ++++++++-------- tests/PhpWord/Element/TableTest.php | 6 +++--- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/phpmd.xml.dist b/phpmd.xml.dist index 2077e02b..cfb9353b 100644 --- a/phpmd.xml.dist +++ b/phpmd.xml.dist @@ -5,6 +5,8 @@ xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd" xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd"> + + diff --git a/src/PhpWord/Reader/MsDoc.php b/src/PhpWord/Reader/MsDoc.php index 1a9e4988..eb64e00a 100644 --- a/src/PhpWord/Reader/MsDoc.php +++ b/src/PhpWord/Reader/MsDoc.php @@ -1581,7 +1581,7 @@ class MsDoc extends AbstractReader implements ReaderInterface // Variables $sprmCPicLocation = null; $sprmCFData = null; - $sprmCFSpec = null; + //$sprmCFSpec = null; do { // Variables @@ -1830,7 +1830,7 @@ class MsDoc extends AbstractReader implements ReaderInterface break; // sprmCFSpec case 0x55: - $sprmCFSpec = $operand; + //$sprmCFSpec = $operand; break; // sprmCFtcBi case 0x5E: @@ -2094,11 +2094,11 @@ class MsDoc extends AbstractReader implements ReaderInterface $sprmCPicLocation += 1; // stPicName - $stPicName = ''; + //$stPicName = ''; for ($inc = 0; $inc <= $cchPicName; $inc++) { - $chr = self::getInt1d($this->dataData, $sprmCPicLocation); + //$chr = self::getInt1d($this->dataData, $sprmCPicLocation); $sprmCPicLocation += 1; - $stPicName .= chr($chr); + //$stPicName .= chr($chr); } } @@ -2143,11 +2143,11 @@ class MsDoc extends AbstractReader implements ReaderInterface $sprmCPicLocation += 1; // nameData if ($cbName > 0) { - $nameData = ''; + //$nameData = ''; for ($inc = 0; $inc <= ($cbName / 2); $inc++) { - $chr = self::getInt2d($this->dataData, $sprmCPicLocation); + //$chr = self::getInt2d($this->dataData, $sprmCPicLocation); $sprmCPicLocation += 2; - $nameData .= chr($chr); + //$nameData .= chr($chr); } } // embeddedBlip diff --git a/tests/PhpWord/Element/TableTest.php b/tests/PhpWord/Element/TableTest.php index 8ae5306c..17aa13a3 100644 --- a/tests/PhpWord/Element/TableTest.php +++ b/tests/PhpWord/Element/TableTest.php @@ -99,10 +99,10 @@ class TableTest extends \PHPUnit\Framework\TestCase { $oTable = new Table(); $oTable->addRow(); - $element = $oTable->addCell(); + $oTable->addCell(); $this->assertEquals($oTable->countColumns(), 1); - $element = $oTable->addCell(); - $element = $oTable->addCell(); + $oTable->addCell(); + $oTable->addCell(); $this->assertEquals($oTable->countColumns(), 3); } } From 9dad1d20e8ca3375fd7e8413ba3722232e758236 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 14:05:52 +0200 Subject: [PATCH 062/112] PHP 7.4 - fixed invalid value in hexdec avoid notice: "Invalid characters passed for attempted conversion, these have been ignored" --- src/PhpWord/Shared/Converter.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php index 39e80545..05755ef7 100644 --- a/src/PhpWord/Shared/Converter.php +++ b/src/PhpWord/Shared/Converter.php @@ -338,9 +338,9 @@ class Converter return false; } - $red = hexdec($red); - $green = hexdec($green); - $blue = hexdec($blue); + $red = ctype_xdigit($red) ? hexdec($red) : 0; + $green = ctype_xdigit($green) ? hexdec($green) : 0; + $blue = ctype_xdigit($blue) ? hexdec($blue) : 0; return array($red, $green, $blue); } From 7fd4b64e8ad678d9017c08f89077344f2f024bee Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 14:26:19 +0200 Subject: [PATCH 063/112] - composer in php 5.x require increase memory limit - in PHP 7.3, 7.4 not necessary remove ignore platforms --- .travis.yml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 421e772c..421df777 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,17 +18,12 @@ matrix: include: - php: 5.3 dist: precise - env: COMPOSER_MEMORY_LIMIT=3G - php: 5.4 dist: trusty - php: 5.5 dist: trusty - php: 7.0 env: COVERAGE=1 - - php: 7.3 - env: DEPENDENCIES="--ignore-platform-reqs" - - php: 7.4 - env: DEPENDENCIES="--ignore-platform-reqs" - php: nightly env: DEPENDENCIES="--ignore-platform-reqs" exclude: @@ -58,6 +53,8 @@ before_script: - if [ -z "$COVERAGE" ]; then phpenv config-rm xdebug.ini || echo "xdebug not available" ; fi ## Composer - composer self-update + ## Composer in PHP versions 5.x requires 3 GB memory + - if [ ${TRAVIS_PHP_VERSION:0:2} == "5." ]; then export COMPOSER_MEMORY_LIMIT=3G ; fi - travis_wait composer install --prefer-source $(if [ -n "$DEPENDENCIES" ]; then echo $DEPENDENCIES; fi) ## PHPDocumentor ##- mkdir -p build/docs From 93978211a13dbccd197e47090085a6565a6a2504 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 15:48:39 +0200 Subject: [PATCH 064/112] PHP 8.0 - depracated libxml_disable_entity_loader, is disabled by default --- src/PhpWord/Shared/Html.php | 8 ++++++-- src/PhpWord/TemplateProcessor.php | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index 54e9509e..be1ef742 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -72,7 +72,9 @@ class Html } // Load DOM - $orignalLibEntityLoader = libxml_disable_entity_loader(true); + if (\PHP_VERSION_ID < 80000) { + $orignalLibEntityLoader = libxml_disable_entity_loader(true); + } $dom = new \DOMDocument(); $dom->preserveWhiteSpace = $preserveWhiteSpace; $dom->loadXML($html); @@ -80,7 +82,9 @@ class Html $node = $dom->getElementsByTagName('body'); self::parseNode($node->item(0), $element); - libxml_disable_entity_loader($orignalLibEntityLoader); + if (\PHP_VERSION_ID < 80000) { + libxml_disable_entity_loader($orignalLibEntityLoader); + } } /** diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7efc0f1a..8aadb8c5 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -170,7 +170,9 @@ class TemplateProcessor */ protected function transformSingleXml($xml, $xsltProcessor) { - $orignalLibEntityLoader = libxml_disable_entity_loader(true); + if (\PHP_VERSION_ID < 80000) { + $orignalLibEntityLoader = libxml_disable_entity_loader(true); + } $domDocument = new \DOMDocument(); if (false === $domDocument->loadXML($xml)) { throw new Exception('Could not load the given XML document.'); @@ -180,7 +182,9 @@ class TemplateProcessor if (false === $transformedXml) { throw new Exception('Could not transform the given XML document.'); } - libxml_disable_entity_loader($orignalLibEntityLoader); + if (\PHP_VERSION_ID < 80000) { + libxml_disable_entity_loader($orignalLibEntityLoader); + } return $transformedXml; } From 9c6cf6fdb622db07332d0c23f1ba47b123005670 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 15:56:40 +0200 Subject: [PATCH 065/112] PHP 8.0 fix - ValueError: file_exists(): Argument #1 ($filename) must not contain any null bytes --- src/PhpWord/Element/Image.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index bae87ff5..c6dd0e58 100644 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -454,7 +454,7 @@ class Image extends AbstractElement } else { $this->sourceType = self::SOURCE_GD; } - } elseif (@file_exists($this->source)) { + } elseif (is_string($this->source) && @file_exists($this->source)) { $this->memoryImage = false; $this->sourceType = self::SOURCE_LOCAL; } else { From 93579e90efed9836d247c412715c1daf95906587 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 16:05:30 +0200 Subject: [PATCH 066/112] forgotten condition for libxml_disable_entity_loader --- tests/PhpWord/_includes/XmlDocument.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/PhpWord/_includes/XmlDocument.php b/tests/PhpWord/_includes/XmlDocument.php index 3a7869bc..14735511 100644 --- a/tests/PhpWord/_includes/XmlDocument.php +++ b/tests/PhpWord/_includes/XmlDocument.php @@ -76,10 +76,14 @@ class XmlDocument $this->file = $file; $file = $this->path . '/' . $file; - $orignalLibEntityLoader = libxml_disable_entity_loader(false); + if (\PHP_VERSION_ID < 80000) { + $orignalLibEntityLoader = libxml_disable_entity_loader(false); + } $this->dom = new \DOMDocument(); $this->dom->load($file); - libxml_disable_entity_loader($orignalLibEntityLoader); + if (\PHP_VERSION_ID < 80000) { + libxml_disable_entity_loader($orignalLibEntityLoader); + } return $this->dom; } From a36a42912880d77fe6caabb0689b5c294fb3793d Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 17:08:23 +0200 Subject: [PATCH 067/112] rewrite check to local file --- src/PhpWord/Element/Image.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index c6dd0e58..081e3b5d 100644 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -454,7 +454,7 @@ class Image extends AbstractElement } else { $this->sourceType = self::SOURCE_GD; } - } elseif (is_string($this->source) && @file_exists($this->source)) { + } elseif ($this->isFile($this->source)) { $this->memoryImage = false; $this->sourceType = self::SOURCE_LOCAL; } else { @@ -463,6 +463,18 @@ class Image extends AbstractElement } } + /** + * @param string $filename + * @return bool + */ + private function isFile($filename) { + try { + return @file_exists($filename); + } catch(\Exception $ex) { + return false; + } + } + /** * Get image size from archive * From bf1bb2b7ca5262312b17f8f7ebcb3f1121ff523d Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 17:17:46 +0200 Subject: [PATCH 068/112] PHP 8.0 - fixed Error: Unknown named parameter $styles --- src/PhpWord/Shared/Html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php index be1ef742..252665e7 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -182,7 +182,7 @@ class Html } } $method = "parse{$method}"; - $newElement = call_user_func_array(array('PhpOffice\PhpWord\Shared\Html', $method), $arguments); + $newElement = call_user_func_array(array('PhpOffice\PhpWord\Shared\Html', $method), array_values($arguments)); // Retrieve back variables from arguments foreach ($keys as $key) { From 2845284284260844169951689aa744646d1ca1c0 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 17:25:02 +0200 Subject: [PATCH 069/112] revert from develop branch --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 421df777..c7add667 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,7 @@ matrix: - php: 5.3 - php: 5.4 - php: 5.5 + - php: 7.0 allow_failures: - php: nightly From 236c7c44998c64e7603574bbf084303a64a04ec9 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 17 Oct 2020 17:34:51 +0200 Subject: [PATCH 070/112] fix code format --- src/PhpWord/Element/Image.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index 081e3b5d..0662bd4c 100644 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -467,10 +467,11 @@ class Image extends AbstractElement * @param string $filename * @return bool */ - private function isFile($filename) { + private function isFile($filename) + { try { return @file_exists($filename); - } catch(\Exception $ex) { + } catch (\Exception $ex) { return false; } } From 0edcab0da6a6075f6fee4e3bc0851c5cff08e885 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 07:26:06 +0200 Subject: [PATCH 071/112] allow php 5.3, 5.4, 5.5 except xenial os --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index c7add667..1b7b71da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,8 +28,11 @@ matrix: env: DEPENDENCIES="--ignore-platform-reqs" exclude: - php: 5.3 + dist: xenial - php: 5.4 + dist: xenial - php: 5.5 + dist: xenial - php: 7.0 allow_failures: - php: nightly From eaf2212aa8d2e5cc85757fb6f76428ab72748b5a Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 07:45:57 +0200 Subject: [PATCH 072/112] fix format for coverage --- .travis.yml | 1 - src/PhpWord/Element/AbstractContainer.php | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1b7b71da..05d21d82 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,7 +33,6 @@ matrix: dist: xenial - php: 5.5 dist: xenial - - php: 7.0 allow_failures: - php: nightly diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php index 0c773dbe..6a1ed930 100644 --- a/src/PhpWord/Element/AbstractContainer.php +++ b/src/PhpWord/Element/AbstractContainer.php @@ -109,6 +109,7 @@ abstract class AbstractContainer extends AbstractElement } else { // All other elements array_unshift($args, $element); // Prepend element name to the beginning of args array + return call_user_func_array(array($this, 'addElement'), $args); } } From 6d49b28678466a63d09530b637e3ae4fdc96627b Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 07:47:25 +0200 Subject: [PATCH 073/112] skip TCPDF test in PHP 5.3, because version 6.3.5 doesn't support PHP 5.3, fixed via https://github.com/tecnickcom/TCPDF/pull/197, pending new release. --- tests/PhpWord/Writer/PDF/TCPDFTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/PhpWord/Writer/PDF/TCPDFTest.php b/tests/PhpWord/Writer/PDF/TCPDFTest.php index 6dc8f24c..c8f5d222 100644 --- a/tests/PhpWord/Writer/PDF/TCPDFTest.php +++ b/tests/PhpWord/Writer/PDF/TCPDFTest.php @@ -33,6 +33,12 @@ class TCPDFTest extends \PHPUnit\Framework\TestCase */ public function testConstruct() { + // TCPDF version 6.3.5 doesn't support PHP 5.3, fixed via https://github.com/tecnickcom/TCPDF/pull/197, + // pending new release. + if (version_compare(PHP_VERSION, '5.4.0', '<')) { + return; + } + $file = __DIR__ . '/../../_files/tcpdf.pdf'; $phpWord = new PhpWord(); From b7dc71ab1b64e0b42d0497c61d29f4732549a358 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 08:18:39 +0200 Subject: [PATCH 074/112] scrutinizer fix, allow zip extension --- .scrutinizer.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 2b395afd..d1fe5961 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -3,6 +3,12 @@ build: analysis: tests: override: [php-scrutinizer-run] + environment: + php: + version: '7.4' + pecl_extensions: + - zip + filter: excluded_paths: [ 'vendor/*', 'tests/*', 'samples/*', 'src/PhpWord/Shared/PCLZip/*' ] From b8ffe0477be771e9c09ce9edf28e6ea6b61bf13a Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 08:54:12 +0200 Subject: [PATCH 075/112] fix cs issue: "Arguments with default values must be at the end of the argument list" --- src/PhpWord/Reader/Word2007/AbstractPart.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index 06dfe37b..eab659fa 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -573,11 +573,11 @@ abstract class AbstractPart * Returns the first child element found * * @param XMLReader $xmlReader - * @param \DOMElement $parentNode - * @param string|array $elements + * @param \DOMElement|null $parentNode + * @param string|array|null $elements * @return string|null */ - private function findPossibleElement(XMLReader $xmlReader, \DOMElement $parentNode = null, $elements) + private function findPossibleElement(XMLReader $xmlReader, \DOMElement $parentNode = null, $elements = null) { if (is_array($elements)) { //if element is an array, we take the first element that exists in the XML From 4e4282a76e7269f8e9586efd22f1364110e6e0d6 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 10:56:24 +0200 Subject: [PATCH 076/112] refixed "must not contain any null bytes" --- src/PhpWord/Element/Image.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/PhpWord/Element/Image.php b/src/PhpWord/Element/Image.php index 0662bd4c..2e25fd18 100644 --- a/src/PhpWord/Element/Image.php +++ b/src/PhpWord/Element/Image.php @@ -454,7 +454,7 @@ class Image extends AbstractElement } else { $this->sourceType = self::SOURCE_GD; } - } elseif ($this->isFile($this->source)) { + } elseif ((strpos($this->source, chr(0)) === false) && @file_exists($this->source)) { $this->memoryImage = false; $this->sourceType = self::SOURCE_LOCAL; } else { @@ -463,19 +463,6 @@ class Image extends AbstractElement } } - /** - * @param string $filename - * @return bool - */ - private function isFile($filename) - { - try { - return @file_exists($filename); - } catch (\Exception $ex) { - return false; - } - } - /** * Get image size from archive * From f2516b08b146da9b2be66326bb36d6f54fe56781 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 18 Oct 2020 13:16:43 +0200 Subject: [PATCH 077/112] migrate from abandoned `Zend\Escaper` to `Laminas Escaper` --- README.md | 3 +-- composer.json | 5 ++++- docs/installing.rst | 4 +--- src/PhpWord/Writer/HTML/Element/AbstractElement.php | 4 ++-- src/PhpWord/Writer/HTML/Part/AbstractPart.php | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b15f83d7..30b008a2 100644 --- a/README.md +++ b/README.md @@ -62,8 +62,7 @@ PHPWord requires the following: - PHP 5.3.3+ - [XML Parser extension](http://www.php.net/manual/en/xml.installation.php) -- [Zend\Escaper component](http://framework.zend.com/manual/current/en/modules/zend.escaper.introduction.html) -- [Zend\Stdlib component](http://framework.zend.com/manual/current/en/modules/zend.stdlib.hydrator.html) +- [Laminas Escaper component](https://docs.laminas.dev/laminas-escaper/intro/) - [Zip extension](http://php.net/manual/en/book.zip.php) (optional, used to write OOXML and ODF) - [GD extension](http://php.net/manual/en/book.image.php) (optional, used to add images) - [XMLWriter extension](http://php.net/manual/en/book.xmlwriter.php) (optional, used to write OOXML and ODF) diff --git a/composer.json b/composer.json index 43738937..19997215 100644 --- a/composer.json +++ b/composer.json @@ -60,7 +60,7 @@ "require": { "php": "^5.3.3 || ^7.0 || ^8.0", "ext-xml": "*", - "zendframework/zend-escaper": "^2.2", + "laminas/laminas-escaper": "^2.2", "phpoffice/common": "^0.2.9" }, "require-dev": { @@ -76,6 +76,9 @@ "mpdf/mpdf": "5.7.4 || 6.* || 7.* || 8.*", "php-coveralls/php-coveralls": "1.1.0 || ^2.0" }, + "replace": { + "laminas/laminas-zendframework-bridge": "*" + }, "suggest": { "ext-zip": "Allows writing OOXML and ODF", "ext-gd2": "Allows adding images", diff --git a/docs/installing.rst b/docs/installing.rst index 2a9582d0..baf966bd 100644 --- a/docs/installing.rst +++ b/docs/installing.rst @@ -10,9 +10,7 @@ Mandatory: - PHP 5.3.3+ - `XML Parser `__ extension -- `Zend\\Escaper `__ component -- Zend\\Stdlib component -- `Zend\\Validator `__ component +- `Laminas Escaper `__ component Optional: diff --git a/src/PhpWord/Writer/HTML/Element/AbstractElement.php b/src/PhpWord/Writer/HTML/Element/AbstractElement.php index dc5ccfaa..30d1ccaa 100644 --- a/src/PhpWord/Writer/HTML/Element/AbstractElement.php +++ b/src/PhpWord/Writer/HTML/Element/AbstractElement.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\HTML\Element; +use Laminas\Escaper\Escaper; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Writer\AbstractWriter; -use Zend\Escaper\Escaper; /** * Abstract HTML element writer @@ -50,7 +50,7 @@ abstract class AbstractElement protected $withoutP = false; /** - * @var \Zend\Escaper\Escaper|\PhpOffice\PhpWord\Escaper\AbstractEscaper + * @var \Laminas\Escaper\Escaper|\PhpOffice\PhpWord\Escaper\AbstractEscaper */ protected $escaper; diff --git a/src/PhpWord/Writer/HTML/Part/AbstractPart.php b/src/PhpWord/Writer/HTML/Part/AbstractPart.php index 2d86f399..97c76375 100644 --- a/src/PhpWord/Writer/HTML/Part/AbstractPart.php +++ b/src/PhpWord/Writer/HTML/Part/AbstractPart.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\HTML\Part; +use Laminas\Escaper\Escaper; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Writer\AbstractWriter; -use Zend\Escaper\Escaper; /** * @since 0.11.0 @@ -32,7 +32,7 @@ abstract class AbstractPart private $parentWriter; /** - * @var \Zend\Escaper\Escaper + * @var \Laminas\Escaper\Escaper */ protected $escaper; From 4cc2cc1aeb54c29521fcc6e5a00e9016b4e604f3 Mon Sep 17 00:00:00 2001 From: Libor M Date: Mon, 19 Oct 2020 11:24:25 +0200 Subject: [PATCH 078/112] travis upgrade to PHPUnit 8 for PHP 8 --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 05d21d82..3c3e1819 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,6 +59,12 @@ before_script: ## Composer in PHP versions 5.x requires 3 GB memory - if [ ${TRAVIS_PHP_VERSION:0:2} == "5." ]; then export COMPOSER_MEMORY_LIMIT=3G ; fi - travis_wait composer install --prefer-source $(if [ -n "$DEPENDENCIES" ]; then echo $DEPENDENCIES; fi) + ## PHP 8 require PHPUnit 8 + - | + if [[ ${TRAVIS_PHP_VERSION:0:2} == "8." ]] || [[ $TRAVIS_PHP_VERSION == "nightly" ]]; then + travis_wait composer remove phpunit/phpunit --dev --no-interaction --ignore-platform-reqs + travis_wait composer require phpunit/phpunit ^8.0 --dev --ignore-platform-reqs + fi ## PHPDocumentor ##- mkdir -p build/docs - mkdir -p build/coverage From 6b999233630eb99016906a46f34d77eb13ff1777 Mon Sep 17 00:00:00 2001 From: Libor M Date: Mon, 19 Oct 2020 14:44:28 +0200 Subject: [PATCH 079/112] travis php 8 - convert tests from phpunit 7 format to phpunit 8 format --- .travis.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3c3e1819..ef850687 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,11 +59,23 @@ before_script: ## Composer in PHP versions 5.x requires 3 GB memory - if [ ${TRAVIS_PHP_VERSION:0:2} == "5." ]; then export COMPOSER_MEMORY_LIMIT=3G ; fi - travis_wait composer install --prefer-source $(if [ -n "$DEPENDENCIES" ]; then echo $DEPENDENCIES; fi) - ## PHP 8 require PHPUnit 8 + ## PHP 8 require PHPUnit 8 (ugly hack for support PHPUnit 7 and 8 together) - | if [[ ${TRAVIS_PHP_VERSION:0:2} == "8." ]] || [[ $TRAVIS_PHP_VERSION == "nightly" ]]; then travis_wait composer remove phpunit/phpunit --dev --no-interaction --ignore-platform-reqs travis_wait composer require phpunit/phpunit ^8.0 --dev --ignore-platform-reqs + + find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function setUpBeforeClass()$/function setUpBeforeClass(): void/' {} \; + find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function tearDownAfterClass()$/function tearDownAfterClass(): void/' {} \; + find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function setUp()$/function setUp(): void/' {} \; + find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function tearDown()$/function tearDown(): void/' {} \; + + find ./tests/ -name "*.php" -type f -exec sed -i -e 's/->assertContains(/->assertStringContainsString(/' {} \; + find ./tests/ -name "*.php" -type f -exec sed -i -e 's/->assertNotContains(/->assertStringNotContainsString(/' {} \; + find ./tests/ -name "*.php" -type f -exec sed -i -e "s/->assertInternalType('array', /->assertIsArray(/" {} \; + + sed -i "s/\$this->addWarning('The @expectedException,/\/\/\$this->addWarning('The @expectedException,/" ./vendor/phpunit/phpunit/src/Framework/TestCase.php + sed -i "s/self::createWarning('The optional \$delta/\/\/self::createWarning('The optional \$delta/" ./vendor/phpunit/phpunit/src/Framework/Assert.php fi ## PHPDocumentor ##- mkdir -p build/docs From ecc13ff62a87322f1f71f4a9b4dee76a1f98dc84 Mon Sep 17 00:00:00 2001 From: Nicholas Moore Date: Thu, 22 Oct 2020 13:53:28 +0300 Subject: [PATCH 080/112] Update templates-processing.rst fix typo in doc --- docs/templates-processing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index 5bc11454..bac421c3 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -190,7 +190,7 @@ Finds a row in a table row identified by `$search` param and clones it as many t ['userId' => 1, 'userName' => 'Batman', 'userAddress' => 'Gotham City'], ['userId' => 2, 'userName' => 'Superman', 'userAddress' => 'Metropolis'], ]; - $templateProcessor->cloneRowAndSetValues('userId', ); + $templateProcessor->cloneRowAndSetValues('userId', $values); Will result in From f7242e12501829d26574bd10b2649fde0dbfd013 Mon Sep 17 00:00:00 2001 From: Johannes Sochor Date: Wed, 11 Nov 2020 17:18:52 +0100 Subject: [PATCH 081/112] fix image limit --- src/PhpWord/TemplateProcessor.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7efc0f1a..5973dd88 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -577,6 +577,7 @@ class TemplateProcessor // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) $imgTpl = ''; + $i = 0; foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); @@ -609,6 +610,10 @@ class TemplateProcessor // replace on each iteration, because in one tag we can have 2+ inline variables => before proceed next variable we need to change $partContent $partContent = $this->setValueForPart($wholeTag, $replaceXml, $partContent, $limit); } + $i++; + if($i >= $limit) { + break; + } } } } From a9e012530d9bdbdf426077917ff7974d2a6a5afb Mon Sep 17 00:00:00 2001 From: Libor M Date: Wed, 9 Dec 2020 13:03:49 +0100 Subject: [PATCH 082/112] travis support latest php as 8.0 now --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef850687..2d68941a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ php: - 7.2 - 7.3 - 7.4 - - nightly + - 8.0 matrix: include: @@ -24,7 +24,7 @@ matrix: dist: trusty - php: 7.0 env: COVERAGE=1 - - php: nightly + - php: 8.0 env: DEPENDENCIES="--ignore-platform-reqs" exclude: - php: 5.3 @@ -34,7 +34,7 @@ matrix: - php: 5.5 dist: xenial allow_failures: - - php: nightly + - php: 8.0 cache: directories: From 4b4dfb4ccd392219a12579316e88fb477809e727 Mon Sep 17 00:00:00 2001 From: Libor M Date: Thu, 10 Dec 2020 15:27:31 +0100 Subject: [PATCH 083/112] update ugly hack for PHPUnit 8 support --- .travis.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2d68941a..aca5dbf8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,13 +58,17 @@ before_script: - composer self-update ## Composer in PHP versions 5.x requires 3 GB memory - if [ ${TRAVIS_PHP_VERSION:0:2} == "5." ]; then export COMPOSER_MEMORY_LIMIT=3G ; fi + ## PHP 8 require PHPUnit 8 (ugly hack for support PHPUnit 7 and 8 together) + - | + if [[ ${TRAVIS_PHP_VERSION:0:2} == "8." ]] || [[ $TRAVIS_PHP_VERSION == "nightly" ]]; then + travis_wait composer remove phpunit/phpunit --dev --no-update --no-interaction + travis_wait composer require phpunit/phpunit ^8.0 --dev --no-update + fi + ## Install composer packages - travis_wait composer install --prefer-source $(if [ -n "$DEPENDENCIES" ]; then echo $DEPENDENCIES; fi) ## PHP 8 require PHPUnit 8 (ugly hack for support PHPUnit 7 and 8 together) - | if [[ ${TRAVIS_PHP_VERSION:0:2} == "8." ]] || [[ $TRAVIS_PHP_VERSION == "nightly" ]]; then - travis_wait composer remove phpunit/phpunit --dev --no-interaction --ignore-platform-reqs - travis_wait composer require phpunit/phpunit ^8.0 --dev --ignore-platform-reqs - find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function setUpBeforeClass()$/function setUpBeforeClass(): void/' {} \; find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function tearDownAfterClass()$/function tearDownAfterClass(): void/' {} \; find ./tests/ -name "*.php" -type f -exec sed -i -e 's/function setUp()$/function setUp(): void/' {} \; From 36b63a107c51db41f1bf042c2365888dff338f89 Mon Sep 17 00:00:00 2001 From: Libor M Date: Fri, 11 Dec 2020 07:53:33 +0100 Subject: [PATCH 084/112] skip test, because php 8.0 contain internally validation --- tests/PhpWord/TemplateProcessorTest.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 4caca77a..5b922f71 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -140,6 +140,11 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase */ final public function testXslStyleSheetCanNotBeAppliedOnFailureOfSettingParameterValue() { + // Test is not needed for PHP 8.0, because internally validation throws TypeError exception. + if (\PHP_VERSION_ID >= 80000) { + $this->markTestSkipped('not needed for PHP 8.0'); + } + $templateProcessor = new TemplateProcessor(__DIR__ . '/_files/templates/blank.docx'); $xslDomDocument = new \DOMDocument(); From c8de86a7ec9e368e5e7cb48e03d51e20e979a6c0 Mon Sep 17 00:00:00 2001 From: SailorMax Date: Wed, 16 Dec 2020 17:24:42 +0300 Subject: [PATCH 085/112] allow to use customized pdf library --- src/PhpWord/Element/AbstractContainer.php | 1 + src/PhpWord/Writer/PDF/DomPDF.php | 12 +++++++++++- src/PhpWord/Writer/PDF/MPDF.php | 15 +++++++++++++-- src/PhpWord/Writer/PDF/TCPDF.php | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 4 deletions(-) diff --git a/src/PhpWord/Element/AbstractContainer.php b/src/PhpWord/Element/AbstractContainer.php index 0c773dbe..6a1ed930 100644 --- a/src/PhpWord/Element/AbstractContainer.php +++ b/src/PhpWord/Element/AbstractContainer.php @@ -109,6 +109,7 @@ abstract class AbstractContainer extends AbstractElement } else { // All other elements array_unshift($args, $element); // Prepend element name to the beginning of args array + return call_user_func_array(array($this, 'addElement'), $args); } } diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php index 5fa8f75d..53eea815 100644 --- a/src/PhpWord/Writer/PDF/DomPDF.php +++ b/src/PhpWord/Writer/PDF/DomPDF.php @@ -35,6 +35,16 @@ class DomPDF extends AbstractRenderer implements WriterInterface */ protected $includeFile = null; + /** + * Gets the implementation of external PDF library that should be used. + * + * @return Dompdf implementation + */ + protected function createExternalWriterInstance() + { + return new DompdfLib(); + } + /** * Save PhpWord to file. * @@ -49,7 +59,7 @@ class DomPDF extends AbstractRenderer implements WriterInterface $orientation = 'portrait'; // Create PDF - $pdf = new DompdfLib(); + $pdf = $this->createExternalWriterInstance(); $pdf->setPaper(strtolower($paperSize), $orientation); $pdf->loadHtml(str_replace(PHP_EOL, '', $this->getContent())); $pdf->render(); diff --git a/src/PhpWord/Writer/PDF/MPDF.php b/src/PhpWord/Writer/PDF/MPDF.php index e63f5dfd..464f156c 100644 --- a/src/PhpWord/Writer/PDF/MPDF.php +++ b/src/PhpWord/Writer/PDF/MPDF.php @@ -44,6 +44,18 @@ class MPDF extends AbstractRenderer implements WriterInterface parent::__construct($phpWord); } + /** + * Gets the implementation of external PDF library that should be used. + * + * @return Mpdf implementation + */ + protected function createExternalWriterInstance() + { + $mPdfClass = $this->getMPdfClassName(); + + return new $mPdfClass(); + } + /** * Save PhpWord to file. * @@ -58,8 +70,7 @@ class MPDF extends AbstractRenderer implements WriterInterface $orientation = strtoupper('portrait'); // Create PDF - $mPdfClass = $this->getMPdfClassName(); - $pdf = new $mPdfClass(); + $pdf = $this->createExternalWriterInstance(); $pdf->_setPageSize($paperSize, $orientation); $pdf->addPage($orientation); diff --git a/src/PhpWord/Writer/PDF/TCPDF.php b/src/PhpWord/Writer/PDF/TCPDF.php index badab046..3abbbf06 100644 --- a/src/PhpWord/Writer/PDF/TCPDF.php +++ b/src/PhpWord/Writer/PDF/TCPDF.php @@ -36,6 +36,20 @@ class TCPDF extends AbstractRenderer implements WriterInterface */ protected $includeFile = 'tcpdf.php'; + /** + * Gets the implementation of external PDF library that should be used. + * + * @param string $orientation Page orientation + * @param string $unit Unit measure + * @param string $paperSize Paper size + * + * @return \TCPDF implementation + */ + protected function createExternalWriterInstance($orientation, $unit, $paperSize) + { + return new \TCPDF($orientation, $unit, $paperSize); + } + /** * Save PhpWord to file. * @@ -50,7 +64,7 @@ class TCPDF extends AbstractRenderer implements WriterInterface $orientation = 'P'; // Create PDF - $pdf = new \TCPDF($orientation, 'pt', $paperSize); + $pdf = $this->createExternalWriterInstance($orientation, 'pt', $paperSize); $pdf->setFontSubsetting(false); $pdf->setPrintHeader(false); $pdf->setPrintFooter(false); From 1f67c993e286b642f8d5600f302d6351d8565ef0 Mon Sep 17 00:00:00 2001 From: Ciki Date: Tue, 22 Dec 2020 12:12:18 +0100 Subject: [PATCH 086/112] Fix deprecated warning for non-hexadecimal number I've come across this deprecated warning when trying to save docx to pdf.. if cellBgColor is set to `auto` it should be converted back to `null` which has in practice the same functionality. --- src/PhpWord/Writer/HTML/Element/Table.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php index a6f14792..f19ad683 100644 --- a/src/PhpWord/Writer/HTML/Element/Table.php +++ b/src/PhpWord/Writer/HTML/Element/Table.php @@ -52,6 +52,7 @@ class Table extends AbstractElement for ($j = 0; $j < $rowCellCount; $j++) { $cellStyle = $rowCells[$j]->getStyle(); $cellBgColor = $cellStyle->getBgColor(); + $cellBgColor === 'auto' && $cellBgColor = null; // Fix deprecated warning for non-hexadecimal number $cellFgColor = null; if ($cellBgColor) { $red = hexdec(substr($cellBgColor, 0, 2)); From 18e41a1c4e0a9afcc1317dffb35c48318a260b7e Mon Sep 17 00:00:00 2001 From: Ergashev Adizbek Date: Wed, 23 Dec 2020 16:00:06 +0500 Subject: [PATCH 087/112] Update TemplateProcessor.php --- src/PhpWord/TemplateProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7efc0f1a..c8fc0b8a 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -575,7 +575,7 @@ class TemplateProcessor // define templates // result can be verified via "Open XML SDK 2.5 Productivity Tool" (http://www.microsoft.com/en-us/download/details.aspx?id=30425) - $imgTpl = ''; + $imgTpl = ''; foreach ($searchParts as $partFileName => &$partContent) { $partVariables = $this->getVariablesForPart($partContent); From 15ee5dee05067dd7217a4a22e92f0780c1a3e5ac Mon Sep 17 00:00:00 2001 From: Yannik Firre <3316758+YannikFirre@users.noreply.github.com> Date: Wed, 30 Dec 2020 14:02:29 +0100 Subject: [PATCH 088/112] Fix missing space after if keyword --- src/PhpWord/TemplateProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7f50e3fb..ece29c35 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -275,7 +275,7 @@ class TemplateProcessor $where = $this->findContainingXmlBlockForMacro($search, 'w:r'); - if($where === false) { + if ($where === false) { return ; } From 834a95c503361b8e15353e2ac98295561aea8d55 Mon Sep 17 00:00:00 2001 From: Antoine de Troostembergh Date: Wed, 30 Dec 2020 20:29:43 +0100 Subject: [PATCH 089/112] fix formatting --- src/PhpWord/Writer/PDF/TCPDF.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Writer/PDF/TCPDF.php b/src/PhpWord/Writer/PDF/TCPDF.php index f31ca1b1..9cd01ffd 100644 --- a/src/PhpWord/Writer/PDF/TCPDF.php +++ b/src/PhpWord/Writer/PDF/TCPDF.php @@ -36,7 +36,7 @@ class TCPDF extends AbstractRenderer implements WriterInterface */ protected $includeFile = 'tcpdf.php'; - /** + /** * Gets the implementation of external PDF library that should be used. * * @param string $orientation Page orientation From cf808cb3fcc436f344eef5b4b3e60b75e515d240 Mon Sep 17 00:00:00 2001 From: Antoine de Troostembergh Date: Thu, 31 Dec 2020 21:56:45 +0100 Subject: [PATCH 090/112] Fix merge issue --- src/PhpWord/Writer/PDF/TCPDF.php | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/src/PhpWord/Writer/PDF/TCPDF.php b/src/PhpWord/Writer/PDF/TCPDF.php index 9cd01ffd..3abbbf06 100644 --- a/src/PhpWord/Writer/PDF/TCPDF.php +++ b/src/PhpWord/Writer/PDF/TCPDF.php @@ -50,20 +50,6 @@ class TCPDF extends AbstractRenderer implements WriterInterface return new \TCPDF($orientation, $unit, $paperSize); } - /** - * Gets the implementation of external PDF library that should be used. - * - * @param string $orientation Page orientation - * @param string $unit Unit measure - * @param string $paperSize Paper size - * - * @return \TCPDF implementation - */ - protected function createExternalWriterInstance($orientation, $unit, $paperSize) - { - return new \TCPDF($orientation, $unit, $paperSize); - } - /** * Save PhpWord to file. * From c52686c2436eed853ec2a993811a12c3d5829dd8 Mon Sep 17 00:00:00 2001 From: Libor M Date: Fri, 1 Jan 2021 16:09:16 +0100 Subject: [PATCH 091/112] \PhpOffice\Common\Text -> \PhpOffice\PhpWord\Shared\Text --- src/PhpWord/Element/Bookmark.php | 4 +- src/PhpWord/Element/CheckBox.php | 4 +- src/PhpWord/Element/Link.php | 6 +- src/PhpWord/Element/ListItem.php | 4 +- src/PhpWord/Element/PreserveText.php | 4 +- src/PhpWord/Element/Text.php | 4 +- src/PhpWord/Element/Title.php | 4 +- src/PhpWord/Shared/Text.php | 235 ++++++++++++++++++ src/PhpWord/Style/AbstractStyle.php | 2 +- src/PhpWord/Style/Paragraph.php | 2 +- src/PhpWord/TemplateProcessor.php | 2 +- .../Writer/RTF/Element/AbstractElement.php | 4 +- .../Word2007/Element/AbstractElement.php | 4 +- tests/PhpWord/Shared/TextTest.php | 88 +++++++ 14 files changed, 345 insertions(+), 22 deletions(-) create mode 100644 src/PhpWord/Shared/Text.php create mode 100644 tests/PhpWord/Shared/TextTest.php diff --git a/src/PhpWord/Element/Bookmark.php b/src/PhpWord/Element/Bookmark.php index 16b020d7..856f6860 100644 --- a/src/PhpWord/Element/Bookmark.php +++ b/src/PhpWord/Element/Bookmark.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; /** * Bookmark element @@ -45,7 +45,7 @@ class Bookmark extends AbstractElement */ public function __construct($name = '') { - $this->name = CommonText::toUTF8($name); + $this->name = SharedText::toUTF8($name); } /** diff --git a/src/PhpWord/Element/CheckBox.php b/src/PhpWord/Element/CheckBox.php index f3e87176..beabf8a0 100644 --- a/src/PhpWord/Element/CheckBox.php +++ b/src/PhpWord/Element/CheckBox.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; /** * Check box element @@ -55,7 +55,7 @@ class CheckBox extends Text */ public function setName($name) { - $this->name = CommonText::toUTF8($name); + $this->name = SharedText::toUTF8($name); return $this; } diff --git a/src/PhpWord/Element/Link.php b/src/PhpWord/Element/Link.php index 2bec32dd..25a87fee 100644 --- a/src/PhpWord/Element/Link.php +++ b/src/PhpWord/Element/Link.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; @@ -79,8 +79,8 @@ class Link extends AbstractElement */ public function __construct($source, $text = null, $fontStyle = null, $paragraphStyle = null, $internal = false) { - $this->source = CommonText::toUTF8($source); - $this->text = is_null($text) ? $this->source : CommonText::toUTF8($text); + $this->source = SharedText::toUTF8($source); + $this->text = is_null($text) ? $this->source : SharedText::toUTF8($text); $this->fontStyle = $this->setNewStyle(new Font('text'), $fontStyle); $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle); $this->internal = $internal; diff --git a/src/PhpWord/Element/ListItem.php b/src/PhpWord/Element/ListItem.php index 8b064c47..40381de0 100644 --- a/src/PhpWord/Element/ListItem.php +++ b/src/PhpWord/Element/ListItem.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; use PhpOffice\PhpWord\Style\ListItem as ListItemStyle; /** @@ -57,7 +57,7 @@ class ListItem extends AbstractElement */ public function __construct($text, $depth = 0, $fontStyle = null, $listStyle = null, $paragraphStyle = null) { - $this->textObject = new Text(CommonText::toUTF8($text), $fontStyle, $paragraphStyle); + $this->textObject = new Text(SharedText::toUTF8($text), $fontStyle, $paragraphStyle); $this->depth = $depth; // Version >= 0.10.0 will pass numbering style name. Older version will use old method diff --git a/src/PhpWord/Element/PreserveText.php b/src/PhpWord/Element/PreserveText.php index 374f1a99..c0e64268 100644 --- a/src/PhpWord/Element/PreserveText.php +++ b/src/PhpWord/Element/PreserveText.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; @@ -59,7 +59,7 @@ class PreserveText extends AbstractElement $this->fontStyle = $this->setNewStyle(new Font('text'), $fontStyle); $this->paragraphStyle = $this->setNewStyle(new Paragraph(), $paragraphStyle); - $this->text = CommonText::toUTF8($text); + $this->text = SharedText::toUTF8($text); $matches = preg_split('/({.*?})/', $this->text, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); if (isset($matches[0])) { $this->text = $matches; diff --git a/src/PhpWord/Element/Text.php b/src/PhpWord/Element/Text.php index f4d7f081..1ad497b0 100644 --- a/src/PhpWord/Element/Text.php +++ b/src/PhpWord/Element/Text.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; @@ -136,7 +136,7 @@ class Text extends AbstractElement */ public function setText($text) { - $this->text = CommonText::toUTF8($text); + $this->text = SharedText::toUTF8($text); return $this; } diff --git a/src/PhpWord/Element/Title.php b/src/PhpWord/Element/Title.php index d01f7f33..f061b3d5 100644 --- a/src/PhpWord/Element/Title.php +++ b/src/PhpWord/Element/Title.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Element; -use PhpOffice\Common\Text as CommonText; +use PhpOffice\PhpWord\Shared\Text as SharedText; use PhpOffice\PhpWord\Style; /** @@ -62,7 +62,7 @@ class Title extends AbstractElement public function __construct($text, $depth = 1) { if (is_string($text)) { - $this->text = CommonText::toUTF8($text); + $this->text = SharedText::toUTF8($text); } elseif ($text instanceof TextRun) { $this->text = $text; } else { diff --git a/src/PhpWord/Shared/Text.php b/src/PhpWord/Shared/Text.php new file mode 100644 index 00000000..b9bea3ad --- /dev/null +++ b/src/PhpWord/Shared/Text.php @@ -0,0 +1,235 @@ +) + * element or in the shared string element. + * + * @param string $value Value to escape + * @return string + */ + public static function controlCharacterPHP2OOXML($value = '') + { + if (empty(self::$controlCharacters)) { + self::buildControlCharacters(); + } + + return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $value); + } + + /** + * Return a number formatted for being integrated in xml files + * @param float $number + * @param integer $decimals + * @return string + */ + public static function numberFormat($number, $decimals) + { + return number_format($number, $decimals, '.', ''); + } + + /** + * @param int $dec + * @link http://stackoverflow.com/a/7153133/2235790 + * @author velcrow + * @return string + */ + public static function chr($dec) + { + if ($dec<=0x7F) { + return chr($dec); + } + if ($dec<=0x7FF) { + return chr(($dec>>6)+192).chr(($dec&63)+128); + } + if ($dec<=0xFFFF) { + return chr(($dec>>12)+224).chr((($dec>>6)&63)+128).chr(($dec&63)+128); + } + if ($dec<=0x1FFFFF) { + return chr(($dec>>18)+240).chr((($dec>>12)&63)+128).chr((($dec>>6)&63)+128).chr(($dec&63)+128); + } + return ''; + } + + /** + * Convert from OpenXML escaped control character to PHP control character + * + * @param string $value Value to unescape + * @return string + */ + public static function controlCharacterOOXML2PHP($value = '') + { + if (empty(self::$controlCharacters)) { + self::buildControlCharacters(); + } + + return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $value); + } + + /** + * Check if a string contains UTF-8 data + * + * @param string $value + * @return boolean + */ + public static function isUTF8($value = '') + { + return is_string($value) && ($value === '' || preg_match('/^./su', $value) == 1); + } + + /** + * Return UTF8 encoded value + * + * @param string $value + * @return string + */ + public static function toUTF8($value = '') + { + if (!is_null($value) && !self::isUTF8($value)) { + $value = utf8_encode($value); + } + + return $value; + } + + /** + * Returns unicode from UTF8 text + * + * The function is splitted to reduce cyclomatic complexity + * + * @param string $text UTF8 text + * @return string Unicode text + * @since 0.11.0 + */ + public static function toUnicode($text) + { + return self::unicodeToEntities(self::utf8ToUnicode($text)); + } + + /** + * Returns unicode array from UTF8 text + * + * @param string $text UTF8 text + * @return array + * @since 0.11.0 + * @link http://www.randomchaos.com/documents/?source=php_and_unicode + */ + public static function utf8ToUnicode($text) + { + $unicode = array(); + $values = array(); + $lookingFor = 1; + + // Gets unicode for each character + for ($i = 0; $i < strlen($text); $i++) { + $thisValue = ord($text[$i]); + if ($thisValue < 128) { + $unicode[] = $thisValue; + } else { + if (count($values) == 0) { + $lookingFor = $thisValue < 224 ? 2 : 3; + } + $values[] = $thisValue; + if (count($values) == $lookingFor) { + if ($lookingFor == 3) { + $number = (($values[0] % 16) * 4096) + (($values[1] % 64) * 64) + ($values[2] % 64); + } else { + $number = (($values[0] % 32) * 64) + ($values[1] % 64); + } + $unicode[] = $number; + $values = array(); + $lookingFor = 1; + } + } + } + + return $unicode; + } + + /** + * Returns entites from unicode array + * + * @param array $unicode + * @return string + * @since 0.11.0 + * @link http://www.randomchaos.com/documents/?source=php_and_unicode + */ + private static function unicodeToEntities($unicode) + { + $entities = ''; + + foreach ($unicode as $value) { + if ($value != 65279) { + $entities .= $value > 127 ? '\uc0{\u' . $value . '}' : chr($value); + } + } + + return $entities; + } + + /** + * 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; + } +} diff --git a/src/PhpWord/Style/AbstractStyle.php b/src/PhpWord/Style/AbstractStyle.php index 8edbe80b..aca6635f 100644 --- a/src/PhpWord/Style/AbstractStyle.php +++ b/src/PhpWord/Style/AbstractStyle.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Style; -use PhpOffice\Common\Text; +use PhpOffice\PhpWord\Shared\Text; /** * Abstract style class diff --git a/src/PhpWord/Style/Paragraph.php b/src/PhpWord/Style/Paragraph.php index 580ef54a..522dea9b 100644 --- a/src/PhpWord/Style/Paragraph.php +++ b/src/PhpWord/Style/Paragraph.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Style; -use PhpOffice\Common\Text; use PhpOffice\PhpWord\Exception\InvalidStyleException; +use PhpOffice\PhpWord\Shared\Text; use PhpOffice\PhpWord\SimpleType\Jc; use PhpOffice\PhpWord\SimpleType\TextAlignment; diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 08d328b1..7806530b 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -17,13 +17,13 @@ namespace PhpOffice\PhpWord; -use PhpOffice\Common\Text; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Escaper\RegExp; use PhpOffice\PhpWord\Escaper\Xml; use PhpOffice\PhpWord\Exception\CopyFileException; use PhpOffice\PhpWord\Exception\CreateTemporaryFileException; use PhpOffice\PhpWord\Exception\Exception; +use PhpOffice\PhpWord\Shared\Text; use PhpOffice\PhpWord\Shared\ZipArchive; class TemplateProcessor diff --git a/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/src/PhpWord/Writer/RTF/Element/AbstractElement.php index 132890e6..fa1058bd 100644 --- a/src/PhpWord/Writer/RTF/Element/AbstractElement.php +++ b/src/PhpWord/Writer/RTF/Element/AbstractElement.php @@ -17,10 +17,10 @@ namespace PhpOffice\PhpWord\Writer\RTF\Element; -use PhpOffice\Common\Text as CommonText; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Escaper\Rtf; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Shared\Text as SharedText; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font as FontStyle; use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle; @@ -126,7 +126,7 @@ abstract class AbstractElement extends HTMLAbstractElement return $this->escaper->escape($text); } - return CommonText::toUnicode($text); // todo: replace with `return $text;` later. + return SharedText::toUnicode($text); // todo: replace with `return $text;` later. } /** diff --git a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php index 63f45a76..6f83df67 100644 --- a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php +++ b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php @@ -17,10 +17,10 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\Text as CommonText; use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Shared\Text as SharedText; /** * Abstract element writer @@ -207,7 +207,7 @@ abstract class AbstractElement */ protected function getText($text) { - return CommonText::controlCharacterPHP2OOXML($text); + return SharedText::controlCharacterPHP2OOXML($text); } /** diff --git a/tests/PhpWord/Shared/TextTest.php b/tests/PhpWord/Shared/TextTest.php new file mode 100644 index 00000000..ded00e96 --- /dev/null +++ b/tests/PhpWord/Shared/TextTest.php @@ -0,0 +1,88 @@ +assertEquals('', Text::controlCharacterPHP2OOXML()); + $this->assertEquals('aeiou', Text::controlCharacterPHP2OOXML('aeiou')); + $this->assertEquals('àéîöù', Text::controlCharacterPHP2OOXML('àéîöù')); + + $value = rand(0, 8); + $this->assertEquals('_x'.sprintf('%04s', strtoupper(dechex($value))).'_', Text::controlCharacterPHP2OOXML(chr($value))); + + $this->assertEquals('', Text::controlCharacterOOXML2PHP('')); + $this->assertEquals(chr(0x08), Text::controlCharacterOOXML2PHP('_x0008_')); + } + + public function testNumberFormat() + { + $this->assertEquals('2.1', Text::numberFormat('2.06', 1)); + $this->assertEquals('2.1', Text::numberFormat('2.12', 1)); + $this->assertEquals('1234.0', Text::numberFormat(1234, 1)); + } + + public function testChr() + { + $this->assertEquals('A', Text::chr(65)); + $this->assertEquals('A', Text::chr(0x41)); + $this->assertEquals('é', Text::chr(233)); + $this->assertEquals('é', Text::chr(0xE9)); + $this->assertEquals('⼳', Text::chr(12083)); + $this->assertEquals('⼳', Text::chr(0x2F33)); + $this->assertEquals('🌃', Text::chr(127747)); + $this->assertEquals('🌃', Text::chr(0x1F303)); + $this->assertEquals('', Text::chr(2097152)); + } + /** + * Is UTF8 + */ + public function testIsUTF8() + { + $this->assertTrue(Text::isUTF8('')); + $this->assertTrue(Text::isUTF8('éééé')); + $this->assertFalse(Text::isUTF8(utf8_decode('éééé'))); + } + + /** + * Test unicode conversion + */ + public function testToUnicode() + { + $this->assertEquals('a', Text::toUnicode('a')); + $this->assertEquals('\uc0{\u8364}', Text::toUnicode('€')); + $this->assertEquals('\uc0{\u233}', Text::toUnicode('é')); + } + + /** + * Test remove underscore prefix + */ + public function testRemoveUnderscorePrefix() + { + $this->assertEquals('item', Text::removeUnderscorePrefix('_item')); + } +} From b656720619768c5a423b7e26a1c0423b74c6ef4d Mon Sep 17 00:00:00 2001 From: Libor M Date: Fri, 1 Jan 2021 16:15:24 +0100 Subject: [PATCH 092/112] \PhpOffice\Common\Drawing -> \PhpOffice\PhpWord\Shared\Drawing --- src/PhpWord/Reader/MsDoc.php | 2 +- src/PhpWord/Shared/Drawing.php | 238 +++++++++++++++++++++++++++ tests/PhpWord/Shared/DrawingTest.php | 124 ++++++++++++++ 3 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 src/PhpWord/Shared/Drawing.php create mode 100644 tests/PhpWord/Shared/DrawingTest.php diff --git a/src/PhpWord/Reader/MsDoc.php b/src/PhpWord/Reader/MsDoc.php index eb64e00a..b4c194ca 100644 --- a/src/PhpWord/Reader/MsDoc.php +++ b/src/PhpWord/Reader/MsDoc.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader; -use PhpOffice\Common\Drawing; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\Drawing; use PhpOffice\PhpWord\Shared\OLERead; use PhpOffice\PhpWord\Style; diff --git a/src/PhpWord/Shared/Drawing.php b/src/PhpWord/Shared/Drawing.php new file mode 100644 index 00000000..25110582 --- /dev/null +++ b/src/PhpWord/Shared/Drawing.php @@ -0,0 +1,238 @@ +assertEquals(0, Drawing::degreesToAngle()); + $this->assertEquals((int) round($value * 60000), Drawing::degreesToAngle($value)); + $this->assertEquals(0, Drawing::angleToDegrees()); + $this->assertEquals(round($value / 60000), Drawing::angleToDegrees($value)); + } + + /** + */ + public function testPixelsCentimeters() + { + $value = rand(1, 100); + + $this->assertEquals(0, Drawing::pixelsToCentimeters()); + $this->assertEquals($value / Drawing::DPI_96 * 2.54, Drawing::pixelsToCentimeters($value)); + $this->assertEquals(0, Drawing::centimetersToPixels()); + $this->assertEquals($value / 2.54 * Drawing::DPI_96, Drawing::centimetersToPixels($value)); + } + + /** + */ + public function testPixelsEMU() + { + $value = rand(1, 100); + + $this->assertEquals(0, Drawing::pixelsToEmu()); + $this->assertEquals(round($value*9525), Drawing::pixelsToEmu($value)); + $this->assertEquals(0, Drawing::emuToPixels()); + $this->assertEquals(round($value/9525), Drawing::emuToPixels($value)); + } + + /** + */ + public function testPixelsPoints() + { + $value = rand(1, 100); + + $this->assertEquals(0, Drawing::pixelsToPoints()); + $this->assertEquals($value*0.67777777, Drawing::pixelsToPoints($value)); + $this->assertEquals(0, Drawing::pointsToPixels()); + $this->assertEquals($value* 1.333333333, Drawing::pointsToPixels($value)); + } + + /** + */ + public function testPointsCentimeters() + { + $value = rand(1, 100); + + $this->assertEquals(0, Drawing::pointsToCentimeters()); + $this->assertEquals($value * 1.333333333 / Drawing::DPI_96 * 2.54, Drawing::pointsToCentimeters($value)); + } + + /** + */ + public function testTwips() + { + $value = rand(1, 100); + + // Centimeters + $this->assertEquals(0, Drawing::centimetersToTwips()); + $this->assertEquals($value * 566.928, Drawing::centimetersToTwips($value)); + + $this->assertEquals(0, Drawing::twipsToCentimeters()); + $this->assertEquals($value / 566.928, Drawing::twipsToCentimeters($value)); + + // Inches + $this->assertEquals(0, Drawing::inchesToTwips()); + $this->assertEquals($value * 1440, Drawing::inchesToTwips($value)); + + $this->assertEquals(0, Drawing::twipsToInches()); + $this->assertEquals($value / 1440, Drawing::twipsToInches($value)); + + // Pixels + $this->assertEquals(0, Drawing::twipsToPixels()); + $this->assertEquals(round($value / 15.873984), Drawing::twipsToPixels($value)); + } + + public function testHTML() + { + $this->assertFalse(Drawing::htmlToRGB('0')); + $this->assertFalse(Drawing::htmlToRGB('00')); + $this->assertFalse(Drawing::htmlToRGB('0000')); + $this->assertFalse(Drawing::htmlToRGB('00000')); + + $this->assertInternalType('array', Drawing::htmlToRGB('ABCDEF')); + $this->assertCount(3, Drawing::htmlToRGB('ABCDEF')); + $this->assertEquals(array(0xAB, 0xCD, 0xEF), Drawing::htmlToRGB('ABCDEF')); + $this->assertEquals(array(0xAB, 0xCD, 0xEF), Drawing::htmlToRGB('#ABCDEF')); + $this->assertEquals(array(0xAA, 0xBB, 0xCC), Drawing::htmlToRGB('ABC')); + $this->assertEquals(array(0xAA, 0xBB, 0xCC), Drawing::htmlToRGB('#ABC')); + } +} From 9a26ad9189f9cf29aad5e429f62fdff81aa1dd30 Mon Sep 17 00:00:00 2001 From: Libor M Date: Fri, 1 Jan 2021 16:23:58 +0100 Subject: [PATCH 093/112] \PhpOffice\Common\Microsoft\PasswordEncoder -> \PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder --- src/PhpWord/Metadata/Protection.php | 2 +- .../Shared/Microsoft/PasswordEncoder.php | 235 ++++++++++++++++++ src/PhpWord/Writer/Word2007/Part/Settings.php | 2 +- .../Shared/Microsoft/PasswordEncoderTest.php | 88 +++++++ 4 files changed, 325 insertions(+), 2 deletions(-) create mode 100644 src/PhpWord/Shared/Microsoft/PasswordEncoder.php create mode 100644 tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php diff --git a/src/PhpWord/Metadata/Protection.php b/src/PhpWord/Metadata/Protection.php index 15aa3ff1..a46d43b9 100644 --- a/src/PhpWord/Metadata/Protection.php +++ b/src/PhpWord/Metadata/Protection.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Metadata; -use PhpOffice\Common\Microsoft\PasswordEncoder; +use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder; use PhpOffice\PhpWord\SimpleType\DocProtect; /** diff --git a/src/PhpWord/Shared/Microsoft/PasswordEncoder.php b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php new file mode 100644 index 00000000..fc0c7ecd --- /dev/null +++ b/src/PhpWord/Shared/Microsoft/PasswordEncoder.php @@ -0,0 +1,235 @@ + array(1, 'md2'), + self::ALGORITHM_MD4 => array(2, 'md4'), + self::ALGORITHM_MD5 => array(3, 'md5'), + self::ALGORITHM_SHA_1 => array(4, 'sha1'), + self::ALGORITHM_MAC => array(5, ''), // 'mac' -> not possible with hash() + self::ALGORITHM_RIPEMD => array(6, 'ripemd'), + self::ALGORITHM_RIPEMD_160 => array(7, 'ripemd160'), + self::ALGORITHM_HMAC => array(9, ''), //'hmac' -> not possible with hash() + self::ALGORITHM_SHA_256 => array(12, 'sha256'), + self::ALGORITHM_SHA_384 => array(13, 'sha384'), + self::ALGORITHM_SHA_512 => array(14, 'sha512'), + ); + + private static $initialCodeArray = array( + 0xE1F0, + 0x1D0F, + 0xCC9C, + 0x84C0, + 0x110C, + 0x0E10, + 0xF1CE, + 0x313E, + 0x1872, + 0xE139, + 0xD40F, + 0x84F9, + 0x280C, + 0xA96A, + 0x4EC3, + ); + + private static $encryptionMatrix = array( + array(0xAEFC, 0x4DD9, 0x9BB2, 0x2745, 0x4E8A, 0x9D14, 0x2A09), + array(0x7B61, 0xF6C2, 0xFDA5, 0xEB6B, 0xC6F7, 0x9DCF, 0x2BBF), + array(0x4563, 0x8AC6, 0x05AD, 0x0B5A, 0x16B4, 0x2D68, 0x5AD0), + array(0x0375, 0x06EA, 0x0DD4, 0x1BA8, 0x3750, 0x6EA0, 0xDD40), + array(0xD849, 0xA0B3, 0x5147, 0xA28E, 0x553D, 0xAA7A, 0x44D5), + array(0x6F45, 0xDE8A, 0xAD35, 0x4A4B, 0x9496, 0x390D, 0x721A), + array(0xEB23, 0xC667, 0x9CEF, 0x29FF, 0x53FE, 0xA7FC, 0x5FD9), + array(0x47D3, 0x8FA6, 0x0F6D, 0x1EDA, 0x3DB4, 0x7B68, 0xF6D0), + array(0xB861, 0x60E3, 0xC1C6, 0x93AD, 0x377B, 0x6EF6, 0xDDEC), + array(0x45A0, 0x8B40, 0x06A1, 0x0D42, 0x1A84, 0x3508, 0x6A10), + array(0xAA51, 0x4483, 0x8906, 0x022D, 0x045A, 0x08B4, 0x1168), + array(0x76B4, 0xED68, 0xCAF1, 0x85C3, 0x1BA7, 0x374E, 0x6E9C), + array(0x3730, 0x6E60, 0xDCC0, 0xA9A1, 0x4363, 0x86C6, 0x1DAD), + array(0x3331, 0x6662, 0xCCC4, 0x89A9, 0x0373, 0x06E6, 0x0DCC), + array(0x1021, 0x2042, 0x4084, 0x8108, 0x1231, 0x2462, 0x48C4), + ); + + private static $passwordMaxLength = 15; + + /** + * Create a hashed password that MS Word will be able to work with + * @see https://blogs.msdn.microsoft.com/vsod/2010/04/05/how-to-set-the-editing-restrictions-in-word-using-open-xml-sdk-2-0/ + * + * @param string $password + * @param string $algorithmName + * @param string $salt + * @param int $spinCount + * @return string + */ + public static function hashPassword($password, $algorithmName = self::ALGORITHM_SHA_1, $salt = null, $spinCount = 10000) + { + $origEncoding = mb_internal_encoding(); + mb_internal_encoding('UTF-8'); + + $password = mb_substr($password, 0, min(self::$passwordMaxLength, mb_strlen($password))); + + // Get the single-byte values by iterating through the Unicode characters of the truncated password. + // For each character, if the low byte is not equal to 0, take it. Otherwise, take the high byte. + $passUtf8 = mb_convert_encoding($password, 'UCS-2LE', 'UTF-8'); + $byteChars = array(); + + for ($i = 0; $i < mb_strlen($password); $i++) { + $byteChars[$i] = ord(substr($passUtf8, $i * 2, 1)); + + if ($byteChars[$i] == 0) { + $byteChars[$i] = ord(substr($passUtf8, $i * 2 + 1, 1)); + } + } + + // build low-order word and hig-order word and combine them + $combinedKey = self::buildCombinedKey($byteChars); + // build reversed hexadecimal string + $hex = str_pad(strtoupper(dechex($combinedKey & 0xFFFFFFFF)), 8, '0', \STR_PAD_LEFT); + $reversedHex = $hex[6] . $hex[7] . $hex[4] . $hex[5] . $hex[2] . $hex[3] . $hex[0] . $hex[1]; + + $generatedKey = mb_convert_encoding($reversedHex, 'UCS-2LE', 'UTF-8'); + + // Implementation Notes List: + // Word requires that the initial hash of the password with the salt not be considered in the count. + // The initial hash of salt + key is not included in the iteration count. + $algorithm = self::getAlgorithm($algorithmName); + $generatedKey = hash($algorithm, $salt . $generatedKey, true); + + for ($i = 0; $i < $spinCount; $i++) { + $generatedKey = hash($algorithm, $generatedKey . pack('CCCC', $i, $i >> 8, $i >> 16, $i >> 24), true); + } + $generatedKey = base64_encode($generatedKey); + + mb_internal_encoding($origEncoding); + + return $generatedKey; + } + + /** + * Get algorithm from self::$algorithmMapping + * + * @param string $algorithmName + * @return string + */ + private static function getAlgorithm($algorithmName) + { + $algorithm = self::$algorithmMapping[$algorithmName][1]; + if ($algorithm == '') { + $algorithm = 'sha1'; + } + + return $algorithm; + } + + /** + * Returns the algorithm ID + * + * @param string $algorithmName + * @return int + */ + public static function getAlgorithmId($algorithmName) + { + return self::$algorithmMapping[$algorithmName][0]; + } + + /** + * Build combined key from low-order word and high-order word + * + * @param array $byteChars byte array representation of password + * @return int + */ + private static function buildCombinedKey($byteChars) + { + $byteCharsLength = count($byteChars); + // Compute the high-order word + // Initialize from the initial code array (see above), depending on the passwords length. + $highOrderWord = self::$initialCodeArray[$byteCharsLength - 1]; + + // For each character in the password: + // For every bit in the character, starting with the least significant and progressing to (but excluding) + // the most significant, if the bit is set, XOR the key’s high-order word with the corresponding word from + // the Encryption Matrix + for ($i = 0; $i < $byteCharsLength; $i++) { + $tmp = self::$passwordMaxLength - $byteCharsLength + $i; + $matrixRow = self::$encryptionMatrix[$tmp]; + for ($intBit = 0; $intBit < 7; $intBit++) { + if (($byteChars[$i] & (0x0001 << $intBit)) != 0) { + $highOrderWord = ($highOrderWord ^ $matrixRow[$intBit]); + } + } + } + + // Compute low-order word + // Initialize with 0 + $lowOrderWord = 0; + // For each character in the password, going backwards + for ($i = $byteCharsLength - 1; $i >= 0; $i--) { + // low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR character + $lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteChars[$i]); + } + // Lastly, low-order word = (((low-order word SHR 14) AND 0x0001) OR (low-order word SHL 1) AND 0x7FFF)) XOR strPassword length XOR 0xCE4B. + $lowOrderWord = (((($lowOrderWord >> 14) & 0x0001) | (($lowOrderWord << 1) & 0x7FFF)) ^ $byteCharsLength ^ 0xCE4B); + + // Combine the Low and High Order Word + return self::int32(($highOrderWord << 16) + $lowOrderWord); + } + + /** + * Simulate behaviour of (signed) int32 + * + * @codeCoverageIgnore + * @param int $value + * @return int + */ + private static function int32($value) + { + $value = ($value & 0xFFFFFFFF); + + if ($value & 0x80000000) { + $value = -((~$value & 0xFFFFFFFF) + 1); + } + + return $value; + } +} diff --git a/src/PhpWord/Writer/Word2007/Part/Settings.php b/src/PhpWord/Writer/Word2007/Part/Settings.php index b764642a..42d3a5d5 100644 --- a/src/PhpWord/Writer/Word2007/Part/Settings.php +++ b/src/PhpWord/Writer/Word2007/Part/Settings.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\Microsoft\PasswordEncoder; use PhpOffice\PhpWord\ComplexType\ProofState; use PhpOffice\PhpWord\ComplexType\TrackChangesView; +use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder; use PhpOffice\PhpWord\Style\Language; /** diff --git a/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php b/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php new file mode 100644 index 00000000..47fb5481 --- /dev/null +++ b/tests/PhpWord/Shared/Microsoft/PasswordEncoderTest.php @@ -0,0 +1,88 @@ +assertEquals('M795/MAlmGU8RIsY9Q9uDLHC7bk=', $hashPassword); + } + + /** + * Test that a password can be hashed with a custom salt + */ + public function testEncodePasswordWithSalt() + { + //given + $password = 'test'; + $salt = base64_decode('uq81pJRRGFIY5U+E9gt8tA=='); + + //when + $hashPassword = PasswordEncoder::hashPassword($password, PasswordEncoder::ALGORITHM_SHA_1, $salt); + + //then + $this->assertEquals('QiDOcpia1YzSVJPiKPwWebl9p/0=', $hashPassword); + } + + /** + * Test that the encoder falls back on SHA-1 if a non supported algorithm is given + */ + public function testDefaultsToSha1IfUnsupportedAlgorithm() + { + //given + $password = 'test'; + $salt = base64_decode('uq81pJRRGFIY5U+E9gt8tA=='); + + //when + $hashPassword = PasswordEncoder::hashPassword($password, PasswordEncoder::ALGORITHM_MAC, $salt); + + //then + $this->assertEquals('QiDOcpia1YzSVJPiKPwWebl9p/0=', $hashPassword); + } + + /** + * Test that the encoder falls back on SHA-1 if a non supported algorithm is given + */ + public function testEncodePasswordWithNullAsciiCodeInPassword() + { + //given + $password = 'test' . chr(0); + $salt = base64_decode('uq81pJRRGFIY5U+E9gt8tA=='); + + //when + $hashPassword = PasswordEncoder::hashPassword($password, PasswordEncoder::ALGORITHM_MAC, $salt, 1); + + //then + $this->assertEquals('rDV9sgdDsztoCQlvRCb1lF2wxNg=', $hashPassword); + } +} From 0cca050bcd21f161062410f4f7fca0faa072755f Mon Sep 17 00:00:00 2001 From: Libor M Date: Fri, 1 Jan 2021 16:33:27 +0100 Subject: [PATCH 094/112] \PhpOffice\Common\XMLReader -> \PhpOffice\PhpWord\Shared\XMLReader --- src/PhpWord/Reader/ODText.php | 2 +- src/PhpWord/Reader/ODText/Content.php | 2 +- src/PhpWord/Reader/ODText/Meta.php | 2 +- src/PhpWord/Reader/Word2007.php | 2 +- src/PhpWord/Reader/Word2007/AbstractPart.php | 22 +- src/PhpWord/Reader/Word2007/DocPropsCore.php | 2 +- .../Reader/Word2007/DocPropsCustom.php | 2 +- src/PhpWord/Reader/Word2007/Document.php | 8 +- src/PhpWord/Reader/Word2007/Footnotes.php | 2 +- src/PhpWord/Reader/Word2007/Numbering.php | 4 +- src/PhpWord/Reader/Word2007/Settings.php | 2 +- src/PhpWord/Reader/Word2007/Styles.php | 2 +- src/PhpWord/Shared/XMLReader.php | 211 ++++++++++++++++++ tests/PhpWord/Shared/XMLReaderTest.php | 134 +++++++++++ tests/PhpWord/_files/xml/reader.zip | Bin 0 -> 272 bytes 15 files changed, 371 insertions(+), 26 deletions(-) create mode 100644 src/PhpWord/Shared/XMLReader.php create mode 100644 tests/PhpWord/Shared/XMLReaderTest.php create mode 100644 tests/PhpWord/_files/xml/reader.zip diff --git a/src/PhpWord/Reader/ODText.php b/src/PhpWord/Reader/ODText.php index 0b58dc50..d0aa9138 100644 --- a/src/PhpWord/Reader/ODText.php +++ b/src/PhpWord/Reader/ODText.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Reader for ODText diff --git a/src/PhpWord/Reader/ODText/Content.php b/src/PhpWord/Reader/ODText/Content.php index 9dfd6453..cec06418 100644 --- a/src/PhpWord/Reader/ODText/Content.php +++ b/src/PhpWord/Reader/ODText/Content.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Reader\ODText; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Content reader diff --git a/src/PhpWord/Reader/ODText/Meta.php b/src/PhpWord/Reader/ODText/Meta.php index 8801a543..9a3d8341 100644 --- a/src/PhpWord/Reader/ODText/Meta.php +++ b/src/PhpWord/Reader/ODText/Meta.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader\ODText; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Meta reader diff --git a/src/PhpWord/Reader/Word2007.php b/src/PhpWord/Reader/Word2007.php index 52030ef8..699a4ead 100644 --- a/src/PhpWord/Reader/Word2007.php +++ b/src/PhpWord/Reader/Word2007.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; use PhpOffice\PhpWord\Shared\ZipArchive; /** diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index eab659fa..083161d0 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -17,12 +17,12 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\ComplexType\TblWidth as TblWidthComplexType; use PhpOffice\PhpWord\Element\AbstractContainer; use PhpOffice\PhpWord\Element\TextRun; use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Abstract part reader @@ -95,7 +95,7 @@ abstract class AbstractPart /** * Read w:p. * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @param \PhpOffice\PhpWord\Element\AbstractContainer $parent * @param string $docPart @@ -202,7 +202,7 @@ abstract class AbstractPart /** * Read w:r. * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @param \PhpOffice\PhpWord\Element\AbstractContainer $parent * @param string $docPart @@ -320,7 +320,7 @@ abstract class AbstractPart /** * Read w:tbl. * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @param mixed $parent * @param string $docPart @@ -378,7 +378,7 @@ abstract class AbstractPart /** * Read w:pPr. * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @return array|null */ @@ -413,7 +413,7 @@ abstract class AbstractPart /** * Read w:rPr * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @return array|null */ @@ -459,7 +459,7 @@ abstract class AbstractPart /** * Read w:tblPr * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @return string|array|null * @todo Capture w:tblStylePr w:type="firstRow" @@ -509,7 +509,7 @@ abstract class AbstractPart /** * Read w:tblpPr * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @return array */ @@ -534,7 +534,7 @@ abstract class AbstractPart /** * Read w:tblInd * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @return TblWidthComplexType */ @@ -552,7 +552,7 @@ abstract class AbstractPart /** * Read w:tcPr * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @return array */ @@ -620,7 +620,7 @@ abstract class AbstractPart /** * Read style definition * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $parentNode * @param array $styleDefs * @ignoreScrutinizerPatch diff --git a/src/PhpWord/Reader/Word2007/DocPropsCore.php b/src/PhpWord/Reader/Word2007/DocPropsCore.php index 36eecebe..d241df18 100644 --- a/src/PhpWord/Reader/Word2007/DocPropsCore.php +++ b/src/PhpWord/Reader/Word2007/DocPropsCore.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Core properties reader diff --git a/src/PhpWord/Reader/Word2007/DocPropsCustom.php b/src/PhpWord/Reader/Word2007/DocPropsCustom.php index a6835aac..feb41006 100644 --- a/src/PhpWord/Reader/Word2007/DocPropsCustom.php +++ b/src/PhpWord/Reader/Word2007/DocPropsCustom.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\Metadata\DocInfo; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Custom properties reader diff --git a/src/PhpWord/Reader/Word2007/Document.php b/src/PhpWord/Reader/Word2007/Document.php index f0d1194a..13a92e47 100644 --- a/src/PhpWord/Reader/Word2007/Document.php +++ b/src/PhpWord/Reader/Word2007/Document.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Document reader @@ -97,7 +97,7 @@ class Document extends AbstractPart /** * Read w:sectPr * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $domNode * @ignoreScrutinizerPatch * @return array @@ -141,7 +141,7 @@ class Document extends AbstractPart /** * Read w:p node. * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $node * @param \PhpOffice\PhpWord\Element\Section &$section * @@ -170,7 +170,7 @@ class Document extends AbstractPart /** * Read w:sectPr node. * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $node * @param \PhpOffice\PhpWord\Element\Section &$section */ diff --git a/src/PhpWord/Reader/Word2007/Footnotes.php b/src/PhpWord/Reader/Word2007/Footnotes.php index 634f4739..a8829d0b 100644 --- a/src/PhpWord/Reader/Word2007/Footnotes.php +++ b/src/PhpWord/Reader/Word2007/Footnotes.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Footnotes reader diff --git a/src/PhpWord/Reader/Word2007/Numbering.php b/src/PhpWord/Reader/Word2007/Numbering.php index 3f57cbf8..dea8f3ee 100644 --- a/src/PhpWord/Reader/Word2007/Numbering.php +++ b/src/PhpWord/Reader/Word2007/Numbering.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; /** * Numbering reader @@ -89,7 +89,7 @@ class Numbering extends AbstractPart /** * Read numbering level definition from w:abstractNum and w:num * - * @param \PhpOffice\Common\XMLReader $xmlReader + * @param \PhpOffice\PhpWord\Shared\XMLReader $xmlReader * @param \DOMElement $subnode * @param int $levelId * @return array diff --git a/src/PhpWord/Reader/Word2007/Settings.php b/src/PhpWord/Reader/Word2007/Settings.php index 3084943b..0a59e045 100644 --- a/src/PhpWord/Reader/Word2007/Settings.php +++ b/src/PhpWord/Reader/Word2007/Settings.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\ComplexType\TrackChangesView; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; use PhpOffice\PhpWord\Style\Language; /** diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php index 97f29b43..37ce4909 100644 --- a/src/PhpWord/Reader/Word2007/Styles.php +++ b/src/PhpWord/Reader/Word2007/Styles.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Reader\Word2007; -use PhpOffice\Common\XMLReader; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLReader; use PhpOffice\PhpWord\Style\Language; /** diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php new file mode 100644 index 00000000..77013f6c --- /dev/null +++ b/src/PhpWord/Shared/XMLReader.php @@ -0,0 +1,211 @@ +open($zipFile); + $content = $zip->getFromName($xmlFile); + $zip->close(); + + if ($content === false) { + return false; + } + + return $this->getDomFromString($content); + } + + /** + * Get DOMDocument from content string + * + * @param string $content + * @return \DOMDocument + */ + public function getDomFromString($content) + { + $originalLibXMLEntityValue = libxml_disable_entity_loader(true); + $this->dom = new \DOMDocument(); + $this->dom->loadXML($content); + libxml_disable_entity_loader($originalLibXMLEntityValue); + + return $this->dom; + } + + /** + * Get elements + * + * @param string $path + * @param \DOMElement $contextNode + * @return \DOMNodeList + */ + public function getElements($path, \DOMElement $contextNode = null) + { + if ($this->dom === null) { + return array(); + } + if ($this->xpath === null) { + $this->xpath = new \DOMXpath($this->dom); + } + + if (is_null($contextNode)) { + return $this->xpath->query($path); + } + + return $this->xpath->query($path, $contextNode); + } + + /** + * Registers the namespace with the DOMXPath object + * + * @param string $prefix The prefix + * @param string $namespaceURI The URI of the namespace + * @return bool true on success or false on failure + * @throws \InvalidArgumentException If called before having loaded the DOM document + */ + public function registerNamespace($prefix, $namespaceURI) + { + if ($this->dom === null) { + throw new \InvalidArgumentException('Dom needs to be loaded before registering a namespace'); + } + if ($this->xpath === null) { + $this->xpath = new \DOMXpath($this->dom); + } + return $this->xpath->registerNamespace($prefix, $namespaceURI); + } + + /** + * Get element + * + * @param string $path + * @param \DOMElement $contextNode + * @return \DOMElement|null + */ + public function getElement($path, \DOMElement $contextNode = null) + { + $elements = $this->getElements($path, $contextNode); + if ($elements->length > 0) { + return $elements->item(0); + } + + return null; + } + + /** + * Get element attribute + * + * @param string $attribute + * @param \DOMElement $contextNode + * @param string $path + * @return string|null + */ + public function getAttribute($attribute, \DOMElement $contextNode = null, $path = null) + { + $return = null; + if ($path !== null) { + $elements = $this->getElements($path, $contextNode); + if ($elements->length > 0) { + /** @var \DOMElement $node Type hint */ + $node = $elements->item(0); + $return = $node->getAttribute($attribute); + } + } else { + if ($contextNode !== null) { + $return = $contextNode->getAttribute($attribute); + } + } + + return ($return == '') ? null : $return; + } + + /** + * Get element value + * + * @param string $path + * @param \DOMElement $contextNode + * @return string|null + */ + public function getValue($path, \DOMElement $contextNode = null) + { + $elements = $this->getElements($path, $contextNode); + if ($elements->length > 0) { + return $elements->item(0)->nodeValue; + } + + return null; + } + + /** + * Count elements + * + * @param string $path + * @param \DOMElement $contextNode + * @return integer + */ + public function countElements($path, \DOMElement $contextNode = null) + { + $elements = $this->getElements($path, $contextNode); + + return $elements->length; + } + + /** + * Element exists + * + * @param string $path + * @param \DOMElement $contextNode + * @return boolean + */ + public function elementExists($path, \DOMElement $contextNode = null) + { + return $this->getElements($path, $contextNode)->length > 0; + } +} diff --git a/tests/PhpWord/Shared/XMLReaderTest.php b/tests/PhpWord/Shared/XMLReaderTest.php new file mode 100644 index 00000000..a34d650a --- /dev/null +++ b/tests/PhpWord/Shared/XMLReaderTest.php @@ -0,0 +1,134 @@ +getDomFromString('AAA'); + + $this->assertTrue($reader->elementExists('/element/child')); + $this->assertEquals('AAA', $reader->getElement('/element/child')->textContent); + $this->assertEquals('AAA', $reader->getValue('/element/child')); + $this->assertEquals('test', $reader->getAttribute('attr', $reader->getElement('/element'))); + $this->assertEquals('subtest', $reader->getAttribute('attr', $reader->getElement('/element'), 'child')); + } + + /** + * Test reading XML from zip + */ + public function testDomFromZip() + { + $archiveFile = __DIR__ . '/../_files/xml/reader.zip'; + + $reader = new XMLReader(); + $reader->getDomFromZip($archiveFile, 'test.xml'); + + $this->assertTrue($reader->elementExists('/element/child')); + + $this->assertFalse($reader->getDomFromZip($archiveFile, 'non_existing_xml_file.xml')); + } + + /** + * Test that read from non existing archive throws exception + * + * @expectedException Exception + */ + public function testThrowsExceptionOnNonExistingArchive() + { + $archiveFile = __DIR__ . '/../_files/xml/readers.zip'; + + $reader = new XMLReader(); + $reader->getDomFromZip($archiveFile, 'test.xml'); + } + + /** + * Test elements count + */ + public function testCountElements() + { + $reader = new XMLReader(); + $reader->getDomFromString('AAABBB'); + + $this->assertEquals(2, $reader->countElements('/element/child')); + } + + /** + * Test read non existing elements + */ + public function testReturnNullOnNonExistingNode() + { + $reader = new XMLReader(); + $this->assertEmpty($reader->getElements('/element/children')); + $reader->getDomFromString('AAA'); + + $this->assertNull($reader->getElement('/element/children')); + $this->assertNull($reader->getValue('/element/children')); + } + + /** + * Test that xpath fails if custom namespace is not registered + */ + public function testShouldThrowExceptionIfNamespaceIsNotKnown() + { + try { + $reader = new XMLReader(); + $reader->getDomFromString('AAA'); + + $this->assertTrue($reader->elementExists('/element/test:child')); + $this->assertEquals('AAA', $reader->getElement('/element/test:child')->textContent); + $this->fail(); + } catch (\Exception $e) { + $this->assertTrue(true); + } + } + + /** + * Test reading XML with manually registered namespace + */ + public function testShouldParseXmlWithCustomNamespace() + { + $reader = new XMLReader(); + $reader->getDomFromString('AAA'); + $reader->registerNamespace('test', 'http://phpword.com/my/custom/namespace'); + + $this->assertTrue($reader->elementExists('/element/test:child')); + $this->assertEquals('AAA', $reader->getElement('/element/test:child')->textContent); + } + + /** + * Test that xpath fails if custom namespace is not registered + * + * @expectedException InvalidArgumentException + */ + public function testShouldThowExceptionIfTryingToRegisterNamespaceBeforeReadingDoc() + { + $reader = new XMLReader(); + $reader->registerNamespace('test', 'http://phpword.com/my/custom/namespace'); + } +} diff --git a/tests/PhpWord/_files/xml/reader.zip b/tests/PhpWord/_files/xml/reader.zip new file mode 100644 index 0000000000000000000000000000000000000000..dbe69cbbc03f9ab229f52b6ccdce012abfa8fae5 GIT binary patch literal 272 zcmWIWW@Zs#U|`^2*i|{f`-6HZV-}Ft1{RTFC`m0Y(W}VK2@T<7U{=sFjZy^S(h6<{ zMwYLP3=CkMu4f#%nhgXTE|zldb)K>J*1H%Vo|C>!GjBP}X^?cyU!S13`+Rhb$?rK= z+`Kzyn%3Mh*LY;ueqwT4=ssWmRUOYC1gBjp2w*hcGtZ+lEh#WA<)T6ei(cJ>AKjWf z*P`CMTk~Sh>W;hr3%+s&cr!A|G2?Qr1klY43_w>gENKL>5N>CMxE;;i0p6@^Aa#sD L=m(_NfH({Qga}Y@ literal 0 HcmV?d00001 From 357e905e8b897b0b5b521a8de50a97093106d5e3 Mon Sep 17 00:00:00 2001 From: Libor M Date: Fri, 1 Jan 2021 16:52:15 +0100 Subject: [PATCH 095/112] fix PasswordEncoder namespace --- tests/PhpWord/Writer/Word2007/Part/SettingsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php index fcf5cabc..d3c1c1dd 100644 --- a/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php +++ b/tests/PhpWord/Writer/Word2007/Part/SettingsTest.php @@ -17,10 +17,10 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\Microsoft\PasswordEncoder; use PhpOffice\PhpWord\ComplexType\ProofState; use PhpOffice\PhpWord\ComplexType\TrackChangesView; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\Microsoft\PasswordEncoder; use PhpOffice\PhpWord\SimpleType\Zoom; use PhpOffice\PhpWord\Style\Language; use PhpOffice\PhpWord\TestHelperDOCX; From a2c8d8c2d50a5ac35f6fa792b833f7f4e01f7267 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 2 Jan 2021 08:26:46 +0100 Subject: [PATCH 096/112] \PhpOffice\Common\XMLWriter -> \PhpOffice\PhpWord\Shared\XMLWriter --- src/PhpWord/Shared/XMLWriter.php | 184 ++++++++++++++++++ src/PhpWord/TemplateProcessor.php | 2 +- src/PhpWord/Writer/ODText/Element/Table.php | 6 +- .../Writer/ODText/Part/AbstractPart.php | 6 +- src/PhpWord/Writer/ODText/Part/Content.php | 6 +- src/PhpWord/Writer/ODText/Part/Meta.php | 4 +- src/PhpWord/Writer/ODText/Part/Styles.php | 12 +- .../Word2007/Element/AbstractElement.php | 8 +- .../Writer/Word2007/Element/Container.php | 4 +- .../Writer/Word2007/Element/FormField.php | 8 +- src/PhpWord/Writer/Word2007/Element/Image.php | 2 +- src/PhpWord/Writer/Word2007/Element/SDT.php | 10 +- src/PhpWord/Writer/Word2007/Element/Shape.php | 12 +- src/PhpWord/Writer/Word2007/Element/TOC.php | 8 +- src/PhpWord/Writer/Word2007/Element/Table.php | 8 +- .../Writer/Word2007/Part/AbstractPart.php | 4 +- src/PhpWord/Writer/Word2007/Part/Chart.php | 14 +- src/PhpWord/Writer/Word2007/Part/Comments.php | 4 +- .../Writer/Word2007/Part/ContentTypes.php | 4 +- src/PhpWord/Writer/Word2007/Part/Document.php | 6 +- .../Writer/Word2007/Part/Footnotes.php | 4 +- .../Writer/Word2007/Part/Numbering.php | 8 +- src/PhpWord/Writer/Word2007/Part/Rels.php | 8 +- src/PhpWord/Writer/Word2007/Part/Settings.php | 2 +- src/PhpWord/Writer/Word2007/Part/Styles.php | 10 +- .../Writer/Word2007/Style/AbstractStyle.php | 10 +- src/PhpWord/Writer/Word2007/Style/Frame.php | 4 +- .../Writer/Word2007/Style/MarginBorder.php | 4 +- .../Writer/Word2007/Style/Paragraph.php | 6 +- src/PhpWord/Writer/Word2007/Style/Table.php | 16 +- tests/PhpWord/Shared/XMLWriterTest.php | 74 +++++++ tests/PhpWord/Writer/ODText/ElementTest.php | 2 +- tests/PhpWord/Writer/ODText/StyleTest.php | 2 +- tests/PhpWord/Writer/Word2007/ElementTest.php | 2 +- tests/PhpWord/Writer/Word2007/StyleTest.php | 2 +- 35 files changed, 362 insertions(+), 104 deletions(-) create mode 100644 src/PhpWord/Shared/XMLWriter.php create mode 100644 tests/PhpWord/Shared/XMLWriterTest.php diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php new file mode 100644 index 00000000..1d825dec --- /dev/null +++ b/src/PhpWord/Shared/XMLWriter.php @@ -0,0 +1,184 @@ +openMemory(); + } else { + if (!is_dir($pTemporaryStorageDir)) { + $pTemporaryStorageDir = sys_get_temp_dir(); + } + // Create temporary filename + $this->tempFileName = @tempnam($pTemporaryStorageDir, 'xml'); + + // Open storage + $this->openUri($this->tempFileName); + } + + if ($compatibility) { + $this->setIndent(false); + $this->setIndentString(''); + } else { + $this->setIndent(true); + $this->setIndentString(' '); + } + } + + /** + * Destructor + */ + public function __destruct() + { + // Unlink temporary files + if (empty($this->tempFileName)) { + return; + } + if (PHP_OS != 'WINNT' && @unlink($this->tempFileName) === false) { + throw new \Exception('The file '.$this->tempFileName.' could not be deleted.'); + } + } + + /** + * Get written data + * + * @return string + */ + public function getData() + { + if ($this->tempFileName == '') { + return $this->outputMemory(true); + } + + $this->flush(); + return file_get_contents($this->tempFileName); + } + + + /** + * Write simple element and attribute(s) block + * + * There are two options: + * 1. If the `$attributes` is an array, then it's an associative array of attributes + * 2. If not, then it's a simple attribute-value pair + * + * @param string $element + * @param string|array $attributes + * @param string $value + * @return void + */ + public function writeElementBlock($element, $attributes, $value = null) + { + $this->startElement($element); + if (!is_array($attributes)) { + $attributes = array($attributes => $value); + } + foreach ($attributes as $attribute => $value) { + $this->writeAttribute($attribute, $value); + } + $this->endElement(); + } + + /** + * Write element if ... + * + * @param bool $condition + * @param string $element + * @param string $attribute + * @param mixed $value + * @return void + */ + public function writeElementIf($condition, $element, $attribute = null, $value = null) + { + if ($condition == true) { + if (is_null($attribute)) { + $this->writeElement($element, $value); + } else { + $this->startElement($element); + $this->writeAttribute($attribute, $value); + $this->endElement(); + } + } + } + + /** + * Write attribute if ... + * + * @param bool $condition + * @param string $attribute + * @param mixed $value + * @return void + */ + public function writeAttributeIf($condition, $attribute, $value) + { + if ($condition == true) { + $this->writeAttribute($attribute, $value); + } + } + + /** + * @param string $name + * @param mixed $value + * @return bool + */ + public function writeAttribute($name, $value) + { + if (is_float($value)) { + $value = json_encode($value); + } + return parent::writeAttribute($name, $value); + } +} diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 7806530b..103e1556 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Escaper\RegExp; use PhpOffice\PhpWord\Escaper\Xml; use PhpOffice\PhpWord\Exception\CopyFileException; diff --git a/src/PhpWord/Writer/ODText/Element/Table.php b/src/PhpWord/Writer/ODText/Element/Table.php index 088330ae..19f5ac96 100644 --- a/src/PhpWord/Writer/ODText/Element/Table.php +++ b/src/PhpWord/Writer/ODText/Element/Table.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Row as RowElement; use PhpOffice\PhpWord\Element\Table as TableElement; @@ -60,7 +60,7 @@ class Table extends AbstractElement /** * Write column. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Table $element */ private function writeColumns(XMLWriter $xmlWriter, TableElement $element) @@ -77,7 +77,7 @@ class Table extends AbstractElement /** * Write row. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Row $row */ private function writeRow(XMLWriter $xmlWriter, RowElement $row) diff --git a/src/PhpWord/Writer/ODText/Part/AbstractPart.php b/src/PhpWord/Writer/ODText/Part/AbstractPart.php index f2844de6..67b7a7ae 100644 --- a/src/PhpWord/Writer/ODText/Part/AbstractPart.php +++ b/src/PhpWord/Writer/ODText/Part/AbstractPart.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; @@ -36,7 +36,7 @@ abstract class AbstractPart extends Word2007AbstractPart /** * Write common root attributes. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ protected function writeCommonRootAttributes(XMLWriter $xmlWriter) { @@ -72,7 +72,7 @@ abstract class AbstractPart extends Word2007AbstractPart /** * Write font faces declaration. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ protected function writeFontFaces(XMLWriter $xmlWriter) { diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index f0e60441..4a84896d 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\AbstractContainer; use PhpOffice\PhpWord\Element\Field; use PhpOffice\PhpWord\Element\Image; @@ -151,7 +151,7 @@ class Content extends AbstractPart * * @since 0.11.0 * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writeAutoStyles(XMLWriter $xmlWriter) { @@ -173,7 +173,7 @@ class Content extends AbstractPart /** * Write automatic styles. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writeTextStyles(XMLWriter $xmlWriter) { diff --git a/src/PhpWord/Writer/ODText/Part/Meta.php b/src/PhpWord/Writer/ODText/Part/Meta.php index f38ad01d..8f3f1fb9 100644 --- a/src/PhpWord/Writer/ODText/Part/Meta.php +++ b/src/PhpWord/Writer/ODText/Part/Meta.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * ODText meta part writer: meta.xml @@ -86,7 +86,7 @@ class Meta extends AbstractPart /** * Write individual property * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $property * @param string $value * diff --git a/src/PhpWord/Writer/ODText/Part/Styles.php b/src/PhpWord/Writer/ODText/Part/Styles.php index bcd57ad5..c026e7bb 100644 --- a/src/PhpWord/Writer/ODText/Part/Styles.php +++ b/src/PhpWord/Writer/ODText/Part/Styles.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Shared\Converter; use PhpOffice\PhpWord\Style; @@ -66,7 +66,7 @@ class Styles extends AbstractPart /** * Write default styles. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writeDefault(XMLWriter $xmlWriter) { @@ -118,7 +118,7 @@ class Styles extends AbstractPart /** * Write named styles. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writeNamed(XMLWriter $xmlWriter) { @@ -155,7 +155,7 @@ class Styles extends AbstractPart /** * call writePageLayoutIndiv to write page layout styles for each page * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writePageLayout(XMLWriter $xmlWriter) { @@ -169,7 +169,7 @@ class Styles extends AbstractPart /** * Write page layout styles. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Section $section * @param int $sectionNbr */ @@ -255,7 +255,7 @@ class Styles extends AbstractPart /** * Write master style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writeMaster(XMLWriter $xmlWriter) { diff --git a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php index 6f83df67..d4ec0f7d 100644 --- a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php +++ b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Shared\Text as SharedText; @@ -32,7 +32,7 @@ abstract class AbstractElement /** * XML writer * - * @var \PhpOffice\Common\XMLWriter + * @var \PhpOffice\PhpWord\Shared\XMLWriter */ private $xmlWriter; @@ -58,7 +58,7 @@ abstract class AbstractElement /** * Create new instance * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\AbstractElement $element * @param bool $withoutP */ @@ -72,7 +72,7 @@ abstract class AbstractElement /** * Get XML Writer * - * @return \PhpOffice\Common\XMLWriter + * @return \PhpOffice\PhpWord\Shared\XMLWriter */ protected function getXmlWriter() { diff --git a/src/PhpWord/Writer/Word2007/Element/Container.php b/src/PhpWord/Writer/Word2007/Element/Container.php index 892da051..8a6aa805 100644 --- a/src/PhpWord/Writer/Word2007/Element/Container.php +++ b/src/PhpWord/Writer/Word2007/Element/Container.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\AbstractContainer as ContainerElement; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Element\TextBreak as TextBreakElement; @@ -71,7 +71,7 @@ class Container extends AbstractElement /** * Write individual element * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\AbstractElement $element * @param bool $withoutP * @return string diff --git a/src/PhpWord/Writer/Word2007/Element/FormField.php b/src/PhpWord/Writer/Word2007/Element/FormField.php index b59cf58f..e1754d0f 100644 --- a/src/PhpWord/Writer/Word2007/Element/FormField.php +++ b/src/PhpWord/Writer/Word2007/Element/FormField.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\FormField as FormFieldElement; /** @@ -105,7 +105,7 @@ class FormField extends Text * Write textinput. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFTextInput.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\FormField $element */ private function writeTextInput(XMLWriter $xmlWriter, FormFieldElement $element) @@ -121,7 +121,7 @@ class FormField extends Text * Write checkbox. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFCheckBox.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\FormField $element */ private function writeCheckBox(XMLWriter $xmlWriter, FormFieldElement $element) @@ -144,7 +144,7 @@ class FormField extends Text * Write dropdown. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_FFDDList.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\FormField $element */ private function writeDropDown(XMLWriter $xmlWriter, FormFieldElement $element) diff --git a/src/PhpWord/Writer/Word2007/Element/Image.php b/src/PhpWord/Writer/Word2007/Element/Image.php index 5bebb89c..8fc4849d 100644 --- a/src/PhpWord/Writer/Word2007/Element/Image.php +++ b/src/PhpWord/Writer/Word2007/Element/Image.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Image as ImageElement; use PhpOffice\PhpWord\Style\Font as FontStyle; use PhpOffice\PhpWord\Style\Frame as FrameStyle; diff --git a/src/PhpWord/Writer/Word2007/Element/SDT.php b/src/PhpWord/Writer/Word2007/Element/SDT.php index edf89b53..e2d0d368 100644 --- a/src/PhpWord/Writer/Word2007/Element/SDT.php +++ b/src/PhpWord/Writer/Word2007/Element/SDT.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\SDT as SDTElement; /** @@ -77,7 +77,7 @@ class SDT extends Text * Write text. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtText.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writePlainText(XMLWriter $xmlWriter) { @@ -89,7 +89,7 @@ class SDT extends Text * Write combo box. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtComboBox.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\SDT $element */ private function writeComboBox(XMLWriter $xmlWriter, SDTElement $element) @@ -108,7 +108,7 @@ class SDT extends Text * Write drop down list. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtDropDownList.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\SDT $element */ private function writeDropDownList(XMLWriter $xmlWriter, SDTElement $element) @@ -120,7 +120,7 @@ class SDT extends Text * Write date. * * @see http://www.datypic.com/sc/ooxml/t-w_CT_SdtDate.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\SDT $element */ private function writeDate(XMLWriter $xmlWriter, SDTElement $element) diff --git a/src/PhpWord/Writer/Word2007/Element/Shape.php b/src/PhpWord/Writer/Word2007/Element/Shape.php index 250d5c1d..445be7e4 100644 --- a/src/PhpWord/Writer/Word2007/Element/Shape.php +++ b/src/PhpWord/Writer/Word2007/Element/Shape.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Shape as ShapeElement; use PhpOffice\PhpWord\Style\Shape as ShapeStyle; use PhpOffice\PhpWord\Writer\Word2007\Style\Shape as ShapeStyleWriter; @@ -77,7 +77,7 @@ class Shape extends AbstractElement /** * Write arc. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style */ private function writeArc(XMLWriter $xmlWriter, ShapeStyle $style) @@ -91,7 +91,7 @@ class Shape extends AbstractElement /** * Write curve. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style */ private function writeCurve(XMLWriter $xmlWriter, ShapeStyle $style) @@ -106,7 +106,7 @@ class Shape extends AbstractElement /** * Write line. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style */ private function writeLine(XMLWriter $xmlWriter, ShapeStyle $style) @@ -120,7 +120,7 @@ class Shape extends AbstractElement /** * Write polyline. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style */ private function writePolyline(XMLWriter $xmlWriter, ShapeStyle $style) @@ -131,7 +131,7 @@ class Shape extends AbstractElement /** * Write rectangle. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Shape $style */ private function writeRoundRect(XMLWriter $xmlWriter, ShapeStyle $style) diff --git a/src/PhpWord/Writer/Word2007/Element/TOC.php b/src/PhpWord/Writer/Word2007/Element/TOC.php index 94437cbf..78989f81 100644 --- a/src/PhpWord/Writer/Word2007/Element/TOC.php +++ b/src/PhpWord/Writer/Word2007/Element/TOC.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\TOC as TOCElement; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Writer\Word2007\Style\Font as FontStyleWriter; @@ -64,7 +64,7 @@ class TOC extends AbstractElement /** * Write title * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\TOC $element * @param \PhpOffice\PhpWord\Element\Title $title * @param bool $writeFieldMark @@ -132,7 +132,7 @@ class TOC extends AbstractElement /** * Write style * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\TOC $element * @param int $indent */ @@ -178,7 +178,7 @@ class TOC extends AbstractElement /** * Write TOC Field. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\TOC $element */ private function writeFieldMark(XMLWriter $xmlWriter, TOCElement $element) diff --git a/src/PhpWord/Writer/Word2007/Element/Table.php b/src/PhpWord/Writer/Word2007/Element/Table.php index c365b028..4067868d 100644 --- a/src/PhpWord/Writer/Word2007/Element/Table.php +++ b/src/PhpWord/Writer/Word2007/Element/Table.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Cell as CellElement; use PhpOffice\PhpWord\Element\Row as RowElement; use PhpOffice\PhpWord\Element\Table as TableElement; @@ -71,7 +71,7 @@ class Table extends AbstractElement /** * Write column. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Table $element */ private function writeColumns(XMLWriter $xmlWriter, TableElement $element) @@ -93,7 +93,7 @@ class Table extends AbstractElement /** * Write row. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Row $row */ private function writeRow(XMLWriter $xmlWriter, RowElement $row) @@ -119,7 +119,7 @@ class Table extends AbstractElement /** * Write cell. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Cell $cell */ private function writeCell(XMLWriter $xmlWriter, CellElement $cell) diff --git a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php index ce4e41cb..e14b394e 100644 --- a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php +++ b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Writer\AbstractWriter; @@ -73,7 +73,7 @@ abstract class AbstractPart /** * Get XML Writer * - * @return \PhpOffice\Common\XMLWriter + * @return \PhpOffice\PhpWord\Shared\XMLWriter */ protected function getXmlWriter() { diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 812d3bf1..e413d273 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Chart as ChartElement; /** @@ -99,7 +99,7 @@ class Chart extends AbstractPart * Write chart * * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_Chart.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writeChart(XMLWriter $xmlWriter) { @@ -121,7 +121,7 @@ class Chart extends AbstractPart * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_AreaChart.html * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_RadarChart.html * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_ScatterChart.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter */ private function writePlotArea(XMLWriter $xmlWriter) { @@ -209,7 +209,7 @@ class Chart extends AbstractPart /** * Write series. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param bool $scatter */ private function writeSeries(XMLWriter $xmlWriter, $scatter = false) @@ -294,7 +294,7 @@ class Chart extends AbstractPart /** * Write series items. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $type * @param array $values */ @@ -335,7 +335,7 @@ class Chart extends AbstractPart * Write axis * * @see http://www.datypic.com/sc/ooxml/t-draw-chart_CT_CatAx.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $type */ private function writeAxis(XMLWriter $xmlWriter, $type) @@ -400,7 +400,7 @@ class Chart extends AbstractPart * Write shape * * @see http://www.datypic.com/sc/ooxml/t-a_CT_ShapeProperties.html - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param bool $line */ private function writeShape(XMLWriter $xmlWriter, $line = false) diff --git a/src/PhpWord/Writer/Word2007/Part/Comments.php b/src/PhpWord/Writer/Word2007/Part/Comments.php index 33c9f59e..6bff63ee 100644 --- a/src/PhpWord/Writer/Word2007/Part/Comments.php +++ b/src/PhpWord/Writer/Word2007/Part/Comments.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Comment; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; @@ -70,7 +70,7 @@ class Comments extends AbstractPart /** * Write comment item. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Comment $comment */ protected function writeComment(XMLWriter $xmlWriter, Comment $comment) diff --git a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php index 28a2d294..14fc5853 100644 --- a/src/PhpWord/Writer/Word2007/Part/ContentTypes.php +++ b/src/PhpWord/Writer/Word2007/Part/ContentTypes.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 contenttypes part writer: [Content_Types].xml @@ -80,7 +80,7 @@ class ContentTypes extends AbstractPart /** * Write content types element * - * @param \PhpOffice\Common\XMLWriter $xmlWriter XML Writer + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter XML Writer * @param array $parts * @param bool $isDefault */ diff --git a/src/PhpWord/Writer/Word2007/Part/Document.php b/src/PhpWord/Writer/Word2007/Part/Document.php index e0cabd7e..09ef13f0 100644 --- a/src/PhpWord/Writer/Word2007/Part/Document.php +++ b/src/PhpWord/Writer/Word2007/Part/Document.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; use PhpOffice\PhpWord\Writer\Word2007\Style\Section as SectionStyleWriter; @@ -80,7 +80,7 @@ class Document extends AbstractPart /** * Write begin section. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Section $section */ private function writeSection(XMLWriter $xmlWriter, Section $section) @@ -95,7 +95,7 @@ class Document extends AbstractPart /** * Write end section. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Section $section */ private function writeSectionSettings(XMLWriter $xmlWriter, Section $section) diff --git a/src/PhpWord/Writer/Word2007/Part/Footnotes.php b/src/PhpWord/Writer/Word2007/Part/Footnotes.php index 59bf1830..34bf737b 100644 --- a/src/PhpWord/Writer/Word2007/Part/Footnotes.php +++ b/src/PhpWord/Writer/Word2007/Part/Footnotes.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Footnote; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; use PhpOffice\PhpWord\Writer\Word2007\Style\Paragraph as ParagraphStyleWriter; @@ -135,7 +135,7 @@ class Footnotes extends AbstractPart /** * Write note item. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Element\Footnote|\PhpOffice\PhpWord\Element\Endnote $element */ protected function writeNote(XMLWriter $xmlWriter, $element) diff --git a/src/PhpWord/Writer/Word2007/Part/Numbering.php b/src/PhpWord/Writer/Word2007/Part/Numbering.php index 61e5cc23..1b4f01a6 100644 --- a/src/PhpWord/Writer/Word2007/Part/Numbering.php +++ b/src/PhpWord/Writer/Word2007/Part/Numbering.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Numbering as NumberingStyle; use PhpOffice\PhpWord\Style\NumberingLevel; @@ -97,7 +97,7 @@ class Numbering extends AbstractPart /** * Write level. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\NumberingLevel $level */ private function writeLevel(XMLWriter $xmlWriter, NumberingLevel $level) @@ -137,7 +137,7 @@ class Numbering extends AbstractPart * * @since 0.11.0 * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\NumberingLevel $level * @todo Use paragraph style writer */ @@ -169,7 +169,7 @@ class Numbering extends AbstractPart * * @since 0.11.0 * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\NumberingLevel $level * @todo Use font style writer */ diff --git a/src/PhpWord/Writer/Word2007/Part/Rels.php b/src/PhpWord/Writer/Word2007/Part/Rels.php index 661a4fa8..0a3f934e 100644 --- a/src/PhpWord/Writer/Word2007/Part/Rels.php +++ b/src/PhpWord/Writer/Word2007/Part/Rels.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Exception\Exception; /** @@ -49,7 +49,7 @@ class Rels extends AbstractPart /** * Write relationships. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param array $xmlRels * @param array $mediaRels * @param int $relId @@ -76,7 +76,7 @@ class Rels extends AbstractPart /** * Write media relationships. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param int $relId * @param array $mediaRel */ @@ -101,7 +101,7 @@ class Rels extends AbstractPart * Format: * * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param int $relId Relationship ID * @param string $type Relationship type * @param string $target Relationship target diff --git a/src/PhpWord/Writer/Word2007/Part/Settings.php b/src/PhpWord/Writer/Word2007/Part/Settings.php index 42d3a5d5..4dd8b8be 100644 --- a/src/PhpWord/Writer/Word2007/Part/Settings.php +++ b/src/PhpWord/Writer/Word2007/Part/Settings.php @@ -69,7 +69,7 @@ class Settings extends AbstractPart /** * Write indivual setting, recursive to any child settings. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $settingKey * @param array|string $settingValue */ diff --git a/src/PhpWord/Writer/Word2007/Part/Styles.php b/src/PhpWord/Writer/Word2007/Part/Styles.php index d05338c7..4d8f60c2 100644 --- a/src/PhpWord/Writer/Word2007/Part/Styles.php +++ b/src/PhpWord/Writer/Word2007/Part/Styles.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font as FontStyle; use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle; @@ -76,7 +76,7 @@ class Styles extends AbstractPart /** * Write default font and other default styles. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\AbstractStyle[] $styles */ private function writeDefaultStyles(XMLWriter $xmlWriter, $styles) @@ -161,7 +161,7 @@ class Styles extends AbstractPart /** * Write font style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $styleName * @param \PhpOffice\PhpWord\Style\Font $style */ @@ -229,7 +229,7 @@ class Styles extends AbstractPart /** * Write paragraph style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $styleName * @param \PhpOffice\PhpWord\Style\Paragraph $style */ @@ -261,7 +261,7 @@ class Styles extends AbstractPart /** * Write table style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $styleName * @param \PhpOffice\PhpWord\Style\Table $style */ diff --git a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php index 877ff1db..7e17fbe7 100644 --- a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Settings; /** @@ -30,7 +30,7 @@ abstract class AbstractStyle /** * XML writer * - * @var \PhpOffice\Common\XMLWriter + * @var \PhpOffice\PhpWord\Shared\XMLWriter */ private $xmlWriter; @@ -49,7 +49,7 @@ abstract class AbstractStyle /** * Create new instance. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string|\PhpOffice\PhpWord\Style\AbstractStyle $style */ public function __construct(XMLWriter $xmlWriter, $style = null) @@ -61,7 +61,7 @@ abstract class AbstractStyle /** * Get XML Writer * - * @return \PhpOffice\Common\XMLWriter + * @return \PhpOffice\PhpWord\Shared\XMLWriter */ protected function getXmlWriter() { @@ -106,7 +106,7 @@ abstract class AbstractStyle /** * Write child style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $name * @param mixed $value */ diff --git a/src/PhpWord/Writer/Word2007/Style/Frame.php b/src/PhpWord/Writer/Word2007/Style/Frame.php index 10e5b151..782bce52 100644 --- a/src/PhpWord/Writer/Word2007/Style/Frame.php +++ b/src/PhpWord/Writer/Word2007/Style/Frame.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style\Frame as FrameStyle; use PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment; @@ -108,7 +108,7 @@ class Frame extends AbstractStyle /** * Write wrap. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Frame $style * @param string $wrap */ diff --git a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php index f5c4b015..a9929563 100644 --- a/src/PhpWord/Writer/Word2007/Style/MarginBorder.php +++ b/src/PhpWord/Writer/Word2007/Style/MarginBorder.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Margin border style writer @@ -78,7 +78,7 @@ class MarginBorder extends AbstractStyle /** * Write side. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $side * @param int $width * @param string $color diff --git a/src/PhpWord/Writer/Word2007/Style/Paragraph.php b/src/PhpWord/Writer/Word2007/Style/Paragraph.php index 67616086..08987a6a 100644 --- a/src/PhpWord/Writer/Word2007/Style/Paragraph.php +++ b/src/PhpWord/Writer/Word2007/Style/Paragraph.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Paragraph as ParagraphStyle; use PhpOffice\PhpWord\Writer\Word2007\Element\ParagraphAlignment; @@ -146,7 +146,7 @@ class Paragraph extends AbstractStyle /** * Write tabs. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Tab[] $tabs */ private function writeTabs(XMLWriter $xmlWriter, $tabs) @@ -164,7 +164,7 @@ class Paragraph extends AbstractStyle /** * Write numbering. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param array $numbering */ private function writeNumbering(XMLWriter $xmlWriter, $numbering) diff --git a/src/PhpWord/Writer/Word2007/Style/Table.php b/src/PhpWord/Writer/Word2007/Style/Table.php index 443d6705..eb040e01 100644 --- a/src/PhpWord/Writer/Word2007/Style/Table.php +++ b/src/PhpWord/Writer/Word2007/Style/Table.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\SimpleType\TblWidth; use PhpOffice\PhpWord\Style\Table as TableStyle; use PhpOffice\PhpWord\Writer\Word2007\Element\TableAlignment; @@ -59,7 +59,7 @@ class Table extends AbstractStyle /** * Write full style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style */ private function writeStyle(XMLWriter $xmlWriter, TableStyle $style) @@ -106,7 +106,7 @@ class Table extends AbstractStyle /** * Enable/Disable automatic resizing of the table * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $layout autofit / fixed */ private function writeLayout(XMLWriter $xmlWriter, $layout) @@ -119,7 +119,7 @@ class Table extends AbstractStyle /** * Write margin. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style */ private function writeMargin(XMLWriter $xmlWriter, TableStyle $style) @@ -138,7 +138,7 @@ class Table extends AbstractStyle /** * Write border. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style */ private function writeBorder(XMLWriter $xmlWriter, TableStyle $style) @@ -158,7 +158,7 @@ class Table extends AbstractStyle /** * Writes a table width * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param string $elementName * @param string $unit * @param int|float $width @@ -177,7 +177,7 @@ class Table extends AbstractStyle /** * Write row style. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style */ private function writeFirstRow(XMLWriter $xmlWriter, TableStyle $style) @@ -196,7 +196,7 @@ class Table extends AbstractStyle /** * Write shading. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param \PhpOffice\PhpWord\Style\Table $style */ private function writeShading(XMLWriter $xmlWriter, TableStyle $style) diff --git a/tests/PhpWord/Shared/XMLWriterTest.php b/tests/PhpWord/Shared/XMLWriterTest.php new file mode 100644 index 00000000..e717ba03 --- /dev/null +++ b/tests/PhpWord/Shared/XMLWriterTest.php @@ -0,0 +1,74 @@ +startElement('element'); + $object->text('AAA'); + $object->endElement(); + $this->assertEquals('AAA'.chr(10), $object->getData()); + + // Disk + $object = new XMLWriter(XMLWriter::STORAGE_DISK); + $object->startElement('element'); + $object->text('BBB'); + $object->endElement(); + $this->assertEquals('BBB'.chr(10), $object->getData()); + } + + public function testWriteAttribute() + { + $xmlWriter = new XMLWriter(); + $xmlWriter->startElement('element'); + $xmlWriter->writeAttribute('name', 'value'); + $xmlWriter->endElement(); + + $this->assertSame('' . chr(10), $xmlWriter->getData()); + } + + public function testWriteAttributeShouldWriteFloatValueLocaleIndependent() + { + $value = 1.2; + + $xmlWriter = new XMLWriter(); + $xmlWriter->startElement('element'); + $xmlWriter->writeAttribute('name', $value); + $xmlWriter->endElement(); + + $currentLocale = setlocale(LC_NUMERIC, 0); + + setlocale(LC_NUMERIC, 'de_DE.UTF-8', 'de'); + + $this->assertSame('1,2', (string)$value); + $this->assertSame('' . chr(10), $xmlWriter->getData()); + + setlocale(LC_NUMERIC, $currentLocale); + } +} diff --git a/tests/PhpWord/Writer/ODText/ElementTest.php b/tests/PhpWord/Writer/ODText/ElementTest.php index eda4568d..8b827347 100644 --- a/tests/PhpWord/Writer/ODText/ElementTest.php +++ b/tests/PhpWord/Writer/ODText/ElementTest.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\ODText; -use PhpOffice\Common\XMLWriter; use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\TestHelperDOCX; /** diff --git a/tests/PhpWord/Writer/ODText/StyleTest.php b/tests/PhpWord/Writer/ODText/StyleTest.php index b1bf417d..bf8a3dd1 100644 --- a/tests/PhpWord/Writer/ODText/StyleTest.php +++ b/tests/PhpWord/Writer/ODText/StyleTest.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\ODText; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Test class for PhpOffice\PhpWord\Writer\ODText\Style subnamespace diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php index e799e022..8193b3db 100644 --- a/tests/PhpWord/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Writer/Word2007/ElementTest.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Comment; use PhpOffice\PhpWord\Element\TextRun; use PhpOffice\PhpWord\Element\TrackChange; diff --git a/tests/PhpWord/Writer/Word2007/StyleTest.php b/tests/PhpWord/Writer/Word2007/StyleTest.php index 48cff871..8bd27980 100644 --- a/tests/PhpWord/Writer/Word2007/StyleTest.php +++ b/tests/PhpWord/Writer/Word2007/StyleTest.php @@ -17,7 +17,7 @@ namespace PhpOffice\PhpWord\Writer\Word2007; -use PhpOffice\Common\XMLWriter; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Test class for PhpOffice\PhpWord\Writer\Word2007\Style subnamespace From 67b15986a76fb9c938084731105dacd6f087c481 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 2 Jan 2021 08:36:05 +0100 Subject: [PATCH 097/112] remove require phpoffice/common package --- composer.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 19997215..7fbf900c 100644 --- a/composer.json +++ b/composer.json @@ -60,8 +60,7 @@ "require": { "php": "^5.3.3 || ^7.0 || ^8.0", "ext-xml": "*", - "laminas/laminas-escaper": "^2.2", - "phpoffice/common": "^0.2.9" + "laminas/laminas-escaper": "^2.2" }, "require-dev": { "ext-zip": "*", From 681d5c47094462fe7ad2945cecb904cd3d645280 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 2 Jan 2021 09:08:43 +0100 Subject: [PATCH 098/112] fix PHP 8.0 compatibility --- src/PhpWord/Shared/XMLReader.php | 8 ++++++-- tests/PhpWord/Shared/XMLWriterTest.php | 1 - 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php index 77013f6c..ca5cb558 100644 --- a/src/PhpWord/Shared/XMLReader.php +++ b/src/PhpWord/Shared/XMLReader.php @@ -72,10 +72,14 @@ class XMLReader */ public function getDomFromString($content) { - $originalLibXMLEntityValue = libxml_disable_entity_loader(true); + if (\PHP_VERSION_ID < 80000) { + $originalLibXMLEntityValue = libxml_disable_entity_loader(true); + } $this->dom = new \DOMDocument(); $this->dom->loadXML($content); - libxml_disable_entity_loader($originalLibXMLEntityValue); + if (\PHP_VERSION_ID < 80000) { + libxml_disable_entity_loader($originalLibXMLEntityValue); + } return $this->dom; } diff --git a/tests/PhpWord/Shared/XMLWriterTest.php b/tests/PhpWord/Shared/XMLWriterTest.php index e717ba03..843ae716 100644 --- a/tests/PhpWord/Shared/XMLWriterTest.php +++ b/tests/PhpWord/Shared/XMLWriterTest.php @@ -66,7 +66,6 @@ class XMLWriterTest extends \PHPUnit\Framework\TestCase setlocale(LC_NUMERIC, 'de_DE.UTF-8', 'de'); - $this->assertSame('1,2', (string)$value); $this->assertSame('' . chr(10), $xmlWriter->getData()); setlocale(LC_NUMERIC, $currentLocale); From 7a131d0ae18ab7718e7443cf0f26bee390b81faa Mon Sep 17 00:00:00 2001 From: Libor M Date: Sat, 2 Jan 2021 09:37:43 +0100 Subject: [PATCH 099/112] allow dompdf/dompdf 1.0 version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 7fbf900c..64363fe1 100644 --- a/composer.json +++ b/composer.json @@ -70,7 +70,7 @@ "friendsofphp/php-cs-fixer": "^2.2", "phpmd/phpmd": "2.*", "phploc/phploc": "2.* || 3.* || 4.* || 5.* || 6.* || 7.*", - "dompdf/dompdf":"0.8.*", + "dompdf/dompdf":"0.8.* || 1.0.*", "tecnickcom/tcpdf": "6.*", "mpdf/mpdf": "5.7.4 || 6.* || 7.* || 8.*", "php-coveralls/php-coveralls": "1.1.0 || ^2.0" From ea917c28daa8c8f7f7b06477aae4a1faae537654 Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 10 Jan 2021 14:06:19 +0100 Subject: [PATCH 100/112] fix coverage --- src/PhpWord/Shared/Drawing.php | 26 ++++++++++++----- src/PhpWord/Shared/Text.php | 29 ++++++++++--------- src/PhpWord/Shared/XMLReader.php | 9 +++--- src/PhpWord/Shared/XMLWriter.php | 8 ++--- src/PhpWord/TemplateProcessor.php | 2 +- src/PhpWord/Writer/ODText/Element/Table.php | 2 +- .../Writer/ODText/Part/AbstractPart.php | 2 +- src/PhpWord/Writer/ODText/Part/Content.php | 2 +- src/PhpWord/Writer/ODText/Part/Styles.php | 2 +- .../Word2007/Element/AbstractElement.php | 2 +- .../Writer/Word2007/Element/Container.php | 2 +- .../Writer/Word2007/Element/FormField.php | 2 +- src/PhpWord/Writer/Word2007/Element/Image.php | 2 +- src/PhpWord/Writer/Word2007/Element/SDT.php | 2 +- src/PhpWord/Writer/Word2007/Element/Shape.php | 2 +- src/PhpWord/Writer/Word2007/Element/TOC.php | 2 +- src/PhpWord/Writer/Word2007/Element/Table.php | 2 +- .../Writer/Word2007/Part/AbstractPart.php | 2 +- src/PhpWord/Writer/Word2007/Part/Chart.php | 2 +- src/PhpWord/Writer/Word2007/Part/Comments.php | 2 +- src/PhpWord/Writer/Word2007/Part/Document.php | 2 +- .../Writer/Word2007/Part/Footnotes.php | 2 +- src/PhpWord/Writer/Word2007/Part/Rels.php | 2 +- .../Writer/Word2007/Style/AbstractStyle.php | 2 +- tests/PhpWord/Shared/DrawingTest.php | 22 ++++---------- tests/PhpWord/Shared/TextTest.php | 7 ++--- tests/PhpWord/Shared/XMLReaderTest.php | 6 ++-- tests/PhpWord/Shared/XMLWriterTest.php | 10 +++---- tests/PhpWord/Writer/Word2007/ElementTest.php | 2 +- 29 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/PhpWord/Shared/Drawing.php b/src/PhpWord/Shared/Drawing.php index 25110582..531ee245 100644 --- a/src/PhpWord/Shared/Drawing.php +++ b/src/PhpWord/Shared/Drawing.php @@ -46,6 +46,7 @@ class Drawing if ($pValue == 0) { return 0; } + return round($pValue / 9525); } @@ -71,9 +72,10 @@ class Drawing if ($pValue == 0) { return 0; } - return ((($pValue * 1.333333333) / self::DPI_96) * 2.54); + + return (($pValue * 1.333333333) / self::DPI_96) * 2.54; } - + /** * Convert points width to pixels * @@ -85,6 +87,7 @@ class Drawing if ($pValue == 0) { return 0; } + return $pValue * 1.333333333; } @@ -97,7 +100,7 @@ class Drawing public static function pixelsToCentimeters($pValue = 0) { //return $pValue * 0.028; - return (($pValue / self::DPI_96) * 2.54); + return ($pValue / self::DPI_96) * 2.54; } /** @@ -111,6 +114,7 @@ class Drawing if ($pValue == 0) { return 0; } + return ($pValue / 2.54) * self::DPI_96; } @@ -136,13 +140,14 @@ class Drawing if ($pValue == 0) { return 0; } + return round($pValue / 60000); } /** * Convert centimeters width to twips * - * @param integer $pValue + * @param int $pValue * @return float */ public static function centimetersToTwips($pValue = 0) @@ -150,13 +155,14 @@ class Drawing if ($pValue == 0) { return 0; } + return $pValue * 566.928; } /** * Convert twips width to centimeters * - * @param integer $pValue + * @param int $pValue * @return float */ public static function twipsToCentimeters($pValue = 0) @@ -164,13 +170,14 @@ class Drawing if ($pValue == 0) { return 0; } + return $pValue / 566.928; } /** * Convert inches width to twips * - * @param integer $pValue + * @param int $pValue * @return float */ public static function inchesToTwips($pValue = 0) @@ -178,13 +185,14 @@ class Drawing if ($pValue == 0) { return 0; } + return $pValue * 1440; } /** * Convert twips width to inches * - * @param integer $pValue + * @param int $pValue * @return float */ public static function twipsToInches($pValue = 0) @@ -192,13 +200,14 @@ class Drawing if ($pValue == 0) { return 0; } + return $pValue / 1440; } /** * Convert twips width to pixels * - * @param integer $pValue + * @param int $pValue * @return float */ public static function twipsToPixels($pValue = 0) @@ -206,6 +215,7 @@ class Drawing if ($pValue == 0) { return 0; } + return round($pValue / 15.873984); } diff --git a/src/PhpWord/Shared/Text.php b/src/PhpWord/Shared/Text.php index b9bea3ad..c4a1ad62 100644 --- a/src/PhpWord/Shared/Text.php +++ b/src/PhpWord/Shared/Text.php @@ -36,8 +36,8 @@ class Text { for ($i = 0; $i <= 19; ++$i) { if ($i != 9 && $i != 10 && $i != 13) { - $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_'; - $replace = chr($i); + $find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_'; + $replace = chr($i); self::$controlCharacters[$find] = $replace; } } @@ -69,7 +69,7 @@ class Text /** * Return a number formatted for being integrated in xml files * @param float $number - * @param integer $decimals + * @param int $decimals * @return string */ public static function numberFormat($number, $decimals) @@ -79,24 +79,25 @@ class Text /** * @param int $dec - * @link http://stackoverflow.com/a/7153133/2235790 + * @see http://stackoverflow.com/a/7153133/2235790 * @author velcrow * @return string */ public static function chr($dec) { - if ($dec<=0x7F) { + if ($dec <= 0x7F) { return chr($dec); } - if ($dec<=0x7FF) { - return chr(($dec>>6)+192).chr(($dec&63)+128); + if ($dec <= 0x7FF) { + return chr(($dec >> 6) + 192) . chr(($dec & 63) + 128); } - if ($dec<=0xFFFF) { - return chr(($dec>>12)+224).chr((($dec>>6)&63)+128).chr(($dec&63)+128); + if ($dec <= 0xFFFF) { + return chr(($dec >> 12) + 224) . chr((($dec >> 6) & 63) + 128) . chr(($dec & 63) + 128); } - if ($dec<=0x1FFFFF) { - return chr(($dec>>18)+240).chr((($dec>>12)&63)+128).chr((($dec>>6)&63)+128).chr(($dec&63)+128); + if ($dec <= 0x1FFFFF) { + return chr(($dec >> 18) + 240) . chr((($dec >> 12) & 63) + 128) . chr((($dec >> 6) & 63) + 128) . chr(($dec & 63) + 128); } + return ''; } @@ -119,7 +120,7 @@ class Text * Check if a string contains UTF-8 data * * @param string $value - * @return boolean + * @return bool */ public static function isUTF8($value = '') { @@ -161,7 +162,7 @@ class Text * @param string $text UTF8 text * @return array * @since 0.11.0 - * @link http://www.randomchaos.com/documents/?source=php_and_unicode + * @see http://www.randomchaos.com/documents/?source=php_and_unicode */ public static function utf8ToUnicode($text) { @@ -201,7 +202,7 @@ class Text * @param array $unicode * @return string * @since 0.11.0 - * @link http://www.randomchaos.com/documents/?source=php_and_unicode + * @see http://www.randomchaos.com/documents/?source=php_and_unicode */ private static function unicodeToEntities($unicode) { diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php index ca5cb558..3905c52f 100644 --- a/src/PhpWord/Shared/XMLReader.php +++ b/src/PhpWord/Shared/XMLReader.php @@ -43,8 +43,8 @@ class XMLReader * * @param string $zipFile * @param string $xmlFile - * @return \DOMDocument|false * @throws \Exception + * @return \DOMDocument|false */ public function getDomFromZip($zipFile, $xmlFile) { @@ -112,8 +112,8 @@ class XMLReader * * @param string $prefix The prefix * @param string $namespaceURI The URI of the namespace - * @return bool true on success or false on failure * @throws \InvalidArgumentException If called before having loaded the DOM document + * @return bool true on success or false on failure */ public function registerNamespace($prefix, $namespaceURI) { @@ -123,6 +123,7 @@ class XMLReader if ($this->xpath === null) { $this->xpath = new \DOMXpath($this->dom); } + return $this->xpath->registerNamespace($prefix, $namespaceURI); } @@ -192,7 +193,7 @@ class XMLReader * * @param string $path * @param \DOMElement $contextNode - * @return integer + * @return int */ public function countElements($path, \DOMElement $contextNode = null) { @@ -206,7 +207,7 @@ class XMLReader * * @param string $path * @param \DOMElement $contextNode - * @return boolean + * @return bool */ public function elementExists($path, \DOMElement $contextNode = null) { diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php index 1d825dec..930ad62e 100644 --- a/src/PhpWord/Shared/XMLWriter.php +++ b/src/PhpWord/Shared/XMLWriter.php @@ -88,7 +88,7 @@ class XMLWriter extends \XMLWriter return; } if (PHP_OS != 'WINNT' && @unlink($this->tempFileName) === false) { - throw new \Exception('The file '.$this->tempFileName.' could not be deleted.'); + throw new \Exception('The file ' . $this->tempFileName . ' could not be deleted.'); } } @@ -104,10 +104,10 @@ class XMLWriter extends \XMLWriter } $this->flush(); + return file_get_contents($this->tempFileName); } - /** * Write simple element and attribute(s) block * @@ -118,7 +118,6 @@ class XMLWriter extends \XMLWriter * @param string $element * @param string|array $attributes * @param string $value - * @return void */ public function writeElementBlock($element, $attributes, $value = null) { @@ -139,7 +138,6 @@ class XMLWriter extends \XMLWriter * @param string $element * @param string $attribute * @param mixed $value - * @return void */ public function writeElementIf($condition, $element, $attribute = null, $value = null) { @@ -160,7 +158,6 @@ class XMLWriter extends \XMLWriter * @param bool $condition * @param string $attribute * @param mixed $value - * @return void */ public function writeAttributeIf($condition, $attribute, $value) { @@ -179,6 +176,7 @@ class XMLWriter extends \XMLWriter if (is_float($value)) { $value = json_encode($value); } + return parent::writeAttribute($name, $value); } } diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 103e1556..fa8d8e65 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -17,13 +17,13 @@ namespace PhpOffice\PhpWord; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Escaper\RegExp; use PhpOffice\PhpWord\Escaper\Xml; use PhpOffice\PhpWord\Exception\CopyFileException; use PhpOffice\PhpWord\Exception\CreateTemporaryFileException; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Shared\Text; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Shared\ZipArchive; class TemplateProcessor diff --git a/src/PhpWord/Writer/ODText/Element/Table.php b/src/PhpWord/Writer/ODText/Element/Table.php index 19f5ac96..adcf8ab4 100644 --- a/src/PhpWord/Writer/ODText/Element/Table.php +++ b/src/PhpWord/Writer/ODText/Element/Table.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\ODText\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Row as RowElement; use PhpOffice\PhpWord\Element\Table as TableElement; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Table element writer diff --git a/src/PhpWord/Writer/ODText/Part/AbstractPart.php b/src/PhpWord/Writer/ODText/Part/AbstractPart.php index 67b7a7ae..4a7ace78 100644 --- a/src/PhpWord/Writer/ODText/Part/AbstractPart.php +++ b/src/PhpWord/Writer/ODText/Part/AbstractPart.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Writer\Word2007\Part\AbstractPart as Word2007AbstractPart; diff --git a/src/PhpWord/Writer/ODText/Part/Content.php b/src/PhpWord/Writer/ODText/Part/Content.php index 4a84896d..b6a1c95e 100644 --- a/src/PhpWord/Writer/ODText/Part/Content.php +++ b/src/PhpWord/Writer/ODText/Part/Content.php @@ -17,7 +17,6 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\AbstractContainer; use PhpOffice\PhpWord\Element\Field; use PhpOffice\PhpWord\Element\Image; @@ -26,6 +25,7 @@ use PhpOffice\PhpWord\Element\Text; use PhpOffice\PhpWord\Element\TextRun; use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Style\Paragraph; diff --git a/src/PhpWord/Writer/ODText/Part/Styles.php b/src/PhpWord/Writer/ODText/Part/Styles.php index c026e7bb..befd23a2 100644 --- a/src/PhpWord/Writer/ODText/Part/Styles.php +++ b/src/PhpWord/Writer/ODText/Part/Styles.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\ODText\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Shared\Converter; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style; /** diff --git a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php index d4ec0f7d..ee0fb2f9 100644 --- a/src/PhpWord/Writer/Word2007/Element/AbstractElement.php +++ b/src/PhpWord/Writer/Word2007/Element/AbstractElement.php @@ -17,10 +17,10 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Settings; use PhpOffice\PhpWord\Shared\Text as SharedText; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Abstract element writer diff --git a/src/PhpWord/Writer/Word2007/Element/Container.php b/src/PhpWord/Writer/Word2007/Element/Container.php index 8a6aa805..0a39403d 100644 --- a/src/PhpWord/Writer/Word2007/Element/Container.php +++ b/src/PhpWord/Writer/Word2007/Element/Container.php @@ -17,10 +17,10 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\AbstractContainer as ContainerElement; use PhpOffice\PhpWord\Element\AbstractElement as Element; use PhpOffice\PhpWord\Element\TextBreak as TextBreakElement; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Container element writer (section, textrun, header, footnote, cell, etc.) diff --git a/src/PhpWord/Writer/Word2007/Element/FormField.php b/src/PhpWord/Writer/Word2007/Element/FormField.php index e1754d0f..72063864 100644 --- a/src/PhpWord/Writer/Word2007/Element/FormField.php +++ b/src/PhpWord/Writer/Word2007/Element/FormField.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\FormField as FormFieldElement; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * FormField element writer diff --git a/src/PhpWord/Writer/Word2007/Element/Image.php b/src/PhpWord/Writer/Word2007/Element/Image.php index 8fc4849d..f136ba0b 100644 --- a/src/PhpWord/Writer/Word2007/Element/Image.php +++ b/src/PhpWord/Writer/Word2007/Element/Image.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Image as ImageElement; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style\Font as FontStyle; use PhpOffice\PhpWord\Style\Frame as FrameStyle; use PhpOffice\PhpWord\Writer\Word2007\Style\Font as FontStyleWriter; diff --git a/src/PhpWord/Writer/Word2007/Element/SDT.php b/src/PhpWord/Writer/Word2007/Element/SDT.php index e2d0d368..e4964242 100644 --- a/src/PhpWord/Writer/Word2007/Element/SDT.php +++ b/src/PhpWord/Writer/Word2007/Element/SDT.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\SDT as SDTElement; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Structured document tag element writer diff --git a/src/PhpWord/Writer/Word2007/Element/Shape.php b/src/PhpWord/Writer/Word2007/Element/Shape.php index 445be7e4..ef30c484 100644 --- a/src/PhpWord/Writer/Word2007/Element/Shape.php +++ b/src/PhpWord/Writer/Word2007/Element/Shape.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Shape as ShapeElement; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style\Shape as ShapeStyle; use PhpOffice\PhpWord\Writer\Word2007\Style\Shape as ShapeStyleWriter; diff --git a/src/PhpWord/Writer/Word2007/Element/TOC.php b/src/PhpWord/Writer/Word2007/Element/TOC.php index 78989f81..3dac76fb 100644 --- a/src/PhpWord/Writer/Word2007/Element/TOC.php +++ b/src/PhpWord/Writer/Word2007/Element/TOC.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\TOC as TOCElement; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style\Font; use PhpOffice\PhpWord\Writer\Word2007\Style\Font as FontStyleWriter; use PhpOffice\PhpWord\Writer\Word2007\Style\Paragraph as ParagraphStyleWriter; diff --git a/src/PhpWord/Writer/Word2007/Element/Table.php b/src/PhpWord/Writer/Word2007/Element/Table.php index 4067868d..25a44fb1 100644 --- a/src/PhpWord/Writer/Word2007/Element/Table.php +++ b/src/PhpWord/Writer/Word2007/Element/Table.php @@ -17,10 +17,10 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Element; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Cell as CellElement; use PhpOffice\PhpWord\Element\Row as RowElement; use PhpOffice\PhpWord\Element\Table as TableElement; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Style\Cell as CellStyle; use PhpOffice\PhpWord\Style\Row as RowStyle; use PhpOffice\PhpWord\Writer\Word2007\Style\Cell as CellStyleWriter; diff --git a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php index e14b394e..4bee5c54 100644 --- a/src/PhpWord/Writer/Word2007/Part/AbstractPart.php +++ b/src/PhpWord/Writer/Word2007/Part/AbstractPart.php @@ -17,9 +17,9 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Exception\Exception; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Writer\AbstractWriter; /** diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index e413d273..8907160b 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Chart as ChartElement; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 chart part writer: word/charts/chartx.xml diff --git a/src/PhpWord/Writer/Word2007/Part/Comments.php b/src/PhpWord/Writer/Word2007/Part/Comments.php index 6bff63ee..5b867324 100644 --- a/src/PhpWord/Writer/Word2007/Part/Comments.php +++ b/src/PhpWord/Writer/Word2007/Part/Comments.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Comment; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; /** diff --git a/src/PhpWord/Writer/Word2007/Part/Document.php b/src/PhpWord/Writer/Word2007/Part/Document.php index 09ef13f0..d2ab836b 100644 --- a/src/PhpWord/Writer/Word2007/Part/Document.php +++ b/src/PhpWord/Writer/Word2007/Part/Document.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Section; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; use PhpOffice\PhpWord\Writer\Word2007\Style\Section as SectionStyleWriter; diff --git a/src/PhpWord/Writer/Word2007/Part/Footnotes.php b/src/PhpWord/Writer/Word2007/Part/Footnotes.php index 34bf737b..58c2e9b1 100644 --- a/src/PhpWord/Writer/Word2007/Part/Footnotes.php +++ b/src/PhpWord/Writer/Word2007/Part/Footnotes.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Footnote; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Writer\Word2007\Element\Container; use PhpOffice\PhpWord\Writer\Word2007\Style\Paragraph as ParagraphStyleWriter; diff --git a/src/PhpWord/Writer/Word2007/Part/Rels.php b/src/PhpWord/Writer/Word2007/Part/Rels.php index 0a3f934e..70f632f7 100644 --- a/src/PhpWord/Writer/Word2007/Part/Rels.php +++ b/src/PhpWord/Writer/Word2007/Part/Rels.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Part; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Exception\Exception; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Word2007 main relationship writer: _rels/.rels diff --git a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php index 7e17fbe7..3624a7be 100644 --- a/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php +++ b/src/PhpWord/Writer/Word2007/Style/AbstractStyle.php @@ -17,8 +17,8 @@ namespace PhpOffice\PhpWord\Writer\Word2007\Style; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\Shared\XMLWriter; /** * Style writer diff --git a/tests/PhpWord/Shared/DrawingTest.php b/tests/PhpWord/Shared/DrawingTest.php index 906a32ce..b7110923 100644 --- a/tests/PhpWord/Shared/DrawingTest.php +++ b/tests/PhpWord/Shared/DrawingTest.php @@ -20,12 +20,10 @@ namespace PhpOffice\PhpWord\Shared; /** * Test class for PhpOffice\PhpWord\Shared\Drawing * - * @coversDefaultClass PhpOffice\PhpWord\Shared\Drawing + * @coversDefaultClass \PhpOffice\PhpWord\Shared\Drawing */ class DrawingTest extends \PHPUnit\Framework\TestCase { - /** - */ public function testDegreesAngle() { $value = rand(1, 100); @@ -36,8 +34,6 @@ class DrawingTest extends \PHPUnit\Framework\TestCase $this->assertEquals(round($value / 60000), Drawing::angleToDegrees($value)); } - /** - */ public function testPixelsCentimeters() { $value = rand(1, 100); @@ -48,32 +44,26 @@ class DrawingTest extends \PHPUnit\Framework\TestCase $this->assertEquals($value / 2.54 * Drawing::DPI_96, Drawing::centimetersToPixels($value)); } - /** - */ public function testPixelsEMU() { $value = rand(1, 100); $this->assertEquals(0, Drawing::pixelsToEmu()); - $this->assertEquals(round($value*9525), Drawing::pixelsToEmu($value)); + $this->assertEquals(round($value * 9525), Drawing::pixelsToEmu($value)); $this->assertEquals(0, Drawing::emuToPixels()); - $this->assertEquals(round($value/9525), Drawing::emuToPixels($value)); + $this->assertEquals(round($value / 9525), Drawing::emuToPixels($value)); } - /** - */ public function testPixelsPoints() { $value = rand(1, 100); $this->assertEquals(0, Drawing::pixelsToPoints()); - $this->assertEquals($value*0.67777777, Drawing::pixelsToPoints($value)); + $this->assertEquals($value * 0.67777777, Drawing::pixelsToPoints($value)); $this->assertEquals(0, Drawing::pointsToPixels()); - $this->assertEquals($value* 1.333333333, Drawing::pointsToPixels($value)); + $this->assertEquals($value * 1.333333333, Drawing::pointsToPixels($value)); } - /** - */ public function testPointsCentimeters() { $value = rand(1, 100); @@ -82,8 +72,6 @@ class DrawingTest extends \PHPUnit\Framework\TestCase $this->assertEquals($value * 1.333333333 / Drawing::DPI_96 * 2.54, Drawing::pointsToCentimeters($value)); } - /** - */ public function testTwips() { $value = rand(1, 100); diff --git a/tests/PhpWord/Shared/TextTest.php b/tests/PhpWord/Shared/TextTest.php index ded00e96..6df19b12 100644 --- a/tests/PhpWord/Shared/TextTest.php +++ b/tests/PhpWord/Shared/TextTest.php @@ -20,12 +20,10 @@ namespace PhpOffice\PhpWord\Shared; /** * Test class for Text * - * @coversDefaultClass PhpOffice\PhpWord\Shared\Text + * @coversDefaultClass \PhpOffice\PhpWord\Shared\Text */ class TextTest extends \PHPUnit\Framework\TestCase { - /** - */ public function testControlCharacters() { $this->assertEquals('', Text::controlCharacterPHP2OOXML()); @@ -33,7 +31,7 @@ class TextTest extends \PHPUnit\Framework\TestCase $this->assertEquals('àéîöù', Text::controlCharacterPHP2OOXML('àéîöù')); $value = rand(0, 8); - $this->assertEquals('_x'.sprintf('%04s', strtoupper(dechex($value))).'_', Text::controlCharacterPHP2OOXML(chr($value))); + $this->assertEquals('_x' . sprintf('%04s', strtoupper(dechex($value))) . '_', Text::controlCharacterPHP2OOXML(chr($value))); $this->assertEquals('', Text::controlCharacterOOXML2PHP('')); $this->assertEquals(chr(0x08), Text::controlCharacterOOXML2PHP('_x0008_')); @@ -58,6 +56,7 @@ class TextTest extends \PHPUnit\Framework\TestCase $this->assertEquals('🌃', Text::chr(0x1F303)); $this->assertEquals('', Text::chr(2097152)); } + /** * Is UTF8 */ diff --git a/tests/PhpWord/Shared/XMLReaderTest.php b/tests/PhpWord/Shared/XMLReaderTest.php index a34d650a..4d0ee64c 100644 --- a/tests/PhpWord/Shared/XMLReaderTest.php +++ b/tests/PhpWord/Shared/XMLReaderTest.php @@ -20,7 +20,7 @@ namespace PhpOffice\PhpWord\Shared; /** * Test class for XMLReader * - * @coversDefaultClass PhpOffice\PhpWord\Shared\XMLReader + * @coversDefaultClass \PhpOffice\PhpWord\Shared\XMLReader */ class XMLReaderTest extends \PHPUnit\Framework\TestCase { @@ -57,7 +57,7 @@ class XMLReaderTest extends \PHPUnit\Framework\TestCase /** * Test that read from non existing archive throws exception * - * @expectedException Exception + * @expectedException \Exception */ public function testThrowsExceptionOnNonExistingArchive() { @@ -124,7 +124,7 @@ class XMLReaderTest extends \PHPUnit\Framework\TestCase /** * Test that xpath fails if custom namespace is not registered * - * @expectedException InvalidArgumentException + * @expectedException \InvalidArgumentException */ public function testShouldThowExceptionIfTryingToRegisterNamespaceBeforeReadingDoc() { diff --git a/tests/PhpWord/Shared/XMLWriterTest.php b/tests/PhpWord/Shared/XMLWriterTest.php index 843ae716..e86cd50b 100644 --- a/tests/PhpWord/Shared/XMLWriterTest.php +++ b/tests/PhpWord/Shared/XMLWriterTest.php @@ -24,23 +24,21 @@ namespace PhpOffice\PhpWord\Shared; */ class XMLWriterTest extends \PHPUnit\Framework\TestCase { - /** - */ public function testConstruct() { // Memory $object = new XMLWriter(); $object->startElement('element'); - $object->text('AAA'); + $object->text('AAA'); $object->endElement(); - $this->assertEquals('AAA'.chr(10), $object->getData()); + $this->assertEquals('AAA' . chr(10), $object->getData()); // Disk $object = new XMLWriter(XMLWriter::STORAGE_DISK); $object->startElement('element'); - $object->text('BBB'); + $object->text('BBB'); $object->endElement(); - $this->assertEquals('BBB'.chr(10), $object->getData()); + $this->assertEquals('BBB' . chr(10), $object->getData()); } public function testWriteAttribute() diff --git a/tests/PhpWord/Writer/Word2007/ElementTest.php b/tests/PhpWord/Writer/Word2007/ElementTest.php index 8193b3db..38b1ba75 100644 --- a/tests/PhpWord/Writer/Word2007/ElementTest.php +++ b/tests/PhpWord/Writer/Word2007/ElementTest.php @@ -17,11 +17,11 @@ namespace PhpOffice\PhpWord\Writer\Word2007; -use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\Element\Comment; use PhpOffice\PhpWord\Element\TextRun; use PhpOffice\PhpWord\Element\TrackChange; use PhpOffice\PhpWord\PhpWord; +use PhpOffice\PhpWord\Shared\XMLWriter; use PhpOffice\PhpWord\TestHelperDOCX; /** From 293efab0c2bcbfa4331ca6594f5c5a51d83c9c3b Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 10 Jan 2021 14:33:13 +0100 Subject: [PATCH 101/112] TCPDF doesn't support PHP 8.0, skip test --- tests/PhpWord/Writer/PDF/TCPDFTest.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/PhpWord/Writer/PDF/TCPDFTest.php b/tests/PhpWord/Writer/PDF/TCPDFTest.php index c8f5d222..53e55ca0 100644 --- a/tests/PhpWord/Writer/PDF/TCPDFTest.php +++ b/tests/PhpWord/Writer/PDF/TCPDFTest.php @@ -39,6 +39,12 @@ class TCPDFTest extends \PHPUnit\Framework\TestCase return; } + // TCPDF version 6.3.5 doesn't support PHP 8.0, fixed via https://github.com/tecnickcom/TCPDF/pull/293, + // pending new release. + if (version_compare(PHP_VERSION, '8.0.0', '>=')) { + return; + } + $file = __DIR__ . '/../../_files/tcpdf.pdf'; $phpWord = new PhpWord(); From 40966dd5c5fb1f7f02950fac82e4ee2eadc2f58b Mon Sep 17 00:00:00 2001 From: Libor M Date: Sun, 10 Jan 2021 14:56:54 +0100 Subject: [PATCH 102/112] remove allow failures for PHP 8.0 --- .travis.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index aca5dbf8..c368913d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,6 @@ matrix: dist: trusty - php: 7.0 env: COVERAGE=1 - - php: 8.0 - env: DEPENDENCIES="--ignore-platform-reqs" exclude: - php: 5.3 dist: xenial @@ -33,8 +31,6 @@ matrix: dist: xenial - php: 5.5 dist: xenial - allow_failures: - - php: 8.0 cache: directories: From ca1272bd695ad88c4eff6c4abc0d9bab81ae5d72 Mon Sep 17 00:00:00 2001 From: troosan Date: Fri, 5 Feb 2021 20:48:05 +0100 Subject: [PATCH 103/112] remove whitespace --- tests/PhpWord/Reader/Word2007/ElementTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhpWord/Reader/Word2007/ElementTest.php b/tests/PhpWord/Reader/Word2007/ElementTest.php index d5db6be8..08b72418 100644 --- a/tests/PhpWord/Reader/Word2007/ElementTest.php +++ b/tests/PhpWord/Reader/Word2007/ElementTest.php @@ -64,7 +64,7 @@ class ElementTest extends AbstractTestReader $text = $elements[0]; $this->assertEquals('Test node value', trim($text->getElement(0)->getText())); } - + /** * Test reading of textbreak */ From 13384f63d3e001b5ebcea3007533f8acb89cf5f4 Mon Sep 17 00:00:00 2001 From: troosan Date: Fri, 5 Feb 2021 21:15:51 +0100 Subject: [PATCH 104/112] update comment --- src/PhpWord/Writer/HTML/Element/Table.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php index f19ad683..059574a2 100644 --- a/src/PhpWord/Writer/HTML/Element/Table.php +++ b/src/PhpWord/Writer/HTML/Element/Table.php @@ -52,7 +52,7 @@ class Table extends AbstractElement for ($j = 0; $j < $rowCellCount; $j++) { $cellStyle = $rowCells[$j]->getStyle(); $cellBgColor = $cellStyle->getBgColor(); - $cellBgColor === 'auto' && $cellBgColor = null; // Fix deprecated warning for non-hexadecimal number + $cellBgColor === 'auto' && $cellBgColor = null; // auto cannot be parsed to hexadecimal number $cellFgColor = null; if ($cellBgColor) { $red = hexdec(substr($cellBgColor, 0, 2)); From 24e46544d894c1d401c444ee867a50b04a6a651f Mon Sep 17 00:00:00 2001 From: troosan Date: Fri, 5 Feb 2021 21:25:21 +0100 Subject: [PATCH 105/112] remove space --- src/PhpWord/TemplateProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index ece29c35..18ec6ec4 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -276,7 +276,7 @@ class TemplateProcessor $where = $this->findContainingXmlBlockForMacro($search, 'w:r'); if ($where === false) { - return ; + return; } $block = $this->getSlice($where['start'], $where['end']); From 31002fc6e867fef24f4dd82827e8f527fd9d479e Mon Sep 17 00:00:00 2001 From: dbarzin Date: Sat, 6 Feb 2021 10:40:52 +0100 Subject: [PATCH 106/112] add chart in template --- samples/Sample_41_TemplateSetChart.php | 45 ++++++++++++++++++ .../resources/Sample_41_TemplateSetChart.docx | Bin 0 -> 9576 bytes src/PhpWord/TemplateProcessor.php | 40 ++++++++++++++++ src/PhpWord/Writer/Word2007/Part/Chart.php | 23 +++++---- 4 files changed, 96 insertions(+), 12 deletions(-) create mode 100644 samples/Sample_41_TemplateSetChart.php create mode 100644 samples/resources/Sample_41_TemplateSetChart.docx diff --git a/samples/Sample_41_TemplateSetChart.php b/samples/Sample_41_TemplateSetChart.php new file mode 100644 index 00000000..102fa3ac --- /dev/null +++ b/samples/Sample_41_TemplateSetChart.php @@ -0,0 +1,45 @@ +addSeries($categories, $series2); + } + if (in_array($chartType, $threeSeries)) { + $chart->addSeries($categories, $series3); + } + + $chart->getStyle() + ->setWidth(Converter::inchToEmu(3)) + ->setHeight(Converter::inchToEmu(3)); + + $templateProcessor->setChart("chart{$i}", $chart); + $i++; +} + +echo date('H:i:s'), ' Saving the result document...', EOL; +$templateProcessor->saveAs('results/Sample_41_TemplateSetChart.docx'); + +echo getEndingNotes(array('Word2007' => 'docx'), 'results/Sample_41_TemplateSetChart.docx'); +if (!CLI) { + include_once 'Sample_Footer.php'; +} diff --git a/samples/resources/Sample_41_TemplateSetChart.docx b/samples/resources/Sample_41_TemplateSetChart.docx new file mode 100644 index 0000000000000000000000000000000000000000..c958b335c8ffe06c109ef3ccea5fb34bc81e1f8c GIT binary patch literal 9576 zcma)i1y~)+vMwIn-Q6JscZWb=A;CS!f}jg`2`<6i-JRfW0fM^+cPBW(9^{;TlC$r- z`@LG<%r~oh{_2_P>gt~UOF;$_3KI+#78Wc>yFdZ#4?%do*0Tj#*u7?cIhRdzzJq*& z3bX2#VUXnJRHq_tRNWdhmLlcxDRSr-CPh{%Uvp(y0lrB=wUe0bc4vnocZu+!&9xww z=oNHHlXr-Z{y|sfs>5|nOCX^Bqu$4gJn^$#gYp5nZ}5^)H=h!RrF3_aDg13G?anj@ z-XP)Dx=cc@HJKi>JRE+qn$WN;^Qoz2D0Iw-dYlbQ7uRCKN21Z}Rj!NS9>~)!@D}$5 zo@@8s#um!3tpdk>o{%29ne0XlcPd57F!RDg%Q&Xz8m3|_X)75aYs@{Ji5CnJ?p-D4 znyxRNclKusM2sd&e2&SO_W)Orc?G*=dnLa6{Q6@MU|a|qebMpSx_aeQ@hU6d5<8n3QhfmOBHZx2tTaL;mcfcpC$S&6LSHmpfr?e zE2{-oo`&7s8Xv7yYjEeB={McurM+_>mdCOpLCrghZgkubN1d)+qQp^Kt`60QX9G$` zQRNsrI%Eg9DcW>19K5SZpvd=SXPqLli`EKhD0kG(VMF~(8nxfP@NIQO5?XX|aJChP z_R>_W5WVM5fvQ2Bgwjq4*fQIdv*+=a0i1-2VG|sFEpZZV6`n=Kl~2aDAgh)wekA5L zj7XRq8@v-W%r1Yu!vqDH*P|qzrZ1Gan8VOY1Z2uL7NG3sHnV3uMm&G5_&WdVSRhj! zuI#i@XD^^w*S&40td}=Mo~VnXf}iICO*={X3Gyyk05w9w>k-Zi^QLljn&!pQw#i0% z^x$A%Tu}dHX~MrO{V#ts0vS4306Ild_M@^#e&*$hoyhQSr!t+eo5}5Rz!k2 zc?7vgNm;VHLZDpZ6BI~n!pX-b%c-N&6rVPJ+xrn*8WU2iOm=+tU5DUGC{E>o_X?6T5SG{vGA@7?XjHK=+J`QYU^*H(9TrzyM zA486$y$81Y)0{JOS$u=VbaW&7zUC{Njwea?#9es^^I{!Gqhiac=XX!|M?XaS%Q|*I zdwWwW6T26?P@$8%R2&#=UW9uK9JlMyK+$JvBN6<*y!UvIDIvO0yW-s_jN>*-=#Lm&TRLQ(OpvmhmJ@ z&I~rU##1-ot1gPv0hfip&A)^)4vTls7|K$(3#p@DLY?7vQcxi`UR$!E7E@y2&ES07 z{@o;Q4G^x8w*OA)&W8t<`6%tNI&eHmECM>rMchDf7x5?HYcQUK*d{2lnBF3Np$Te` zzlLFfH_qcb1zIvf=F^*N;^hVxbjexBxsI>?+B_{QXN-3v8JkniY>fh%l<@(_OX(`0 zs)}tESLZ`<5f@FDGW2nYqLKM2l=X`q?3I^(ul6pa_s6ns%!J+0{4}4ndbDsnZ(+mc{Yt>x7il@9-$NwPv=0La=04L1c65 zrL1R`^nITOYNNS=5feIL6dzz3vu%t%Djzc`AnLAyR>Rn0V_=w;P>vvsqGmr5f^y<1 z8klVl)UDEYV_A!L5Y~{2GMh^*%~+Y~Z85kRr8OhNX$o zmlag95lFJXe|B=m#k9&r4bhp#6N#3A3&+dboB5ya&C;Dj0L@X$6ASC+>}z)OwO))S z3UN@zWjc>%7`-okSpeJ`{1z0)-EQj{}u;dc`N` z+x)k*R5H8#L|$A`6Nky2!L6`#IYaI!ZfUv#tDkvlQsBhUEg_ax=fc60jm?)rGZTd` zuXBOR;^=gj^sI53GFCDUO?4UAP4KFD%m|*fxyja$Bp9-4)N`Lx(WG<^q)?UOU@a+; z=x`gALwHA4&6*JV(DlG-?`kePyQm94EU)wC zOCsHy($6V_qd2u4>D)Vd_ps)aI;eKshe(7@Mkl`zZ&zhZ7Jb8?DLq$bh4K?eb0)3~ zg?R&j6_rRTOk;~tUnW&#KUtTDvZ$`RNhiI!9P}BNK|N#)8A=d>`Azjft>$F-8vtYM zYGO^9Ru0-F9KI1_SsC6rv2TTVzeTsnddDc$J|+(*F5i4a>HsswE^2ihv@L`uJLz?t zKW;p{RZW@ZW7T2V;8ki+5mpVpj?h!AJ(UPc`Hnp~;Rsw>TeFR+$w<5Isb24;Ypd00 z()l3x+;!)B6S#vbEjg23B}lA&_)op)o;j0y&IDNQhTcvIr)8RxOXX^( z#}Fq>2P^Z(jwx6z^&MOZrw=j&fclV85wx6sXSlQ;JK`iBqp$NE6fMxFGmPNav|5AZ zK7xuRwoJX(4ZDz}D9oztB4)|aXjkB~JwFr_QsFvMpv;{@nDm$}fnOpopaCk2KWTdu z&=9_r>;JVHI(UMwa=w6SmGg+Cqj z$$LF)A+Razb>Qd;+&;|5;OKc>^<76Go?3bb^12Hfl8*yy{jOXP1Mr|Cv$zR*NV}8^ z=Hx_!F;d5I#{{rc#<&U+^&!i}iiuk&GNBVcsnJ~1!;^>`WEX-v3xqIq=!|FtBIE#O z*)*DPsHHz*GgI?gk!*;?93BVXTv5|)vT{@%O6F#76Z=MPL{f@p9TJmZ7?3v0nD$gO zPvK~vMTt7m?SC+{@Bf4xm$Zu?M#iiZr{M)eCb?6p;$vH#n7t;R{G8otmrZ_WNr<-W4_qJ>gg*>e93L7{$pA3 z`FaK-?)-k!qeR8szMZA;Ebz9Q?H^BtXrOMkeq}hC#S{AP{rQVq)y3YC!8 z0MCrc-7e`<5>XU_OrGk4OOt>)5bZ##I6y@RSsK@JUQG$TaV`LI1cPE%ZRV$~Bly{A zJwmPL{~l-R8*V8>smemjkRc%}tz`jfTeH>c!FQFbFlW6E#$x-5zQ zTi?YfDF`E3CBw&23##%LH7l-@sV#GU7*EW=MKh1+kd5#K(oV-`8@*1TkSS)chthO$ z7!?(iTbzLnplt|MTBWIm@zY4GVbY7sZ>dX^$G4D^DX5!R4GB_~s`UjbD{R#pp8%r$ zA00-A@2v%4aGSf$=fums@3DKZ^Y4TP@&&`{6=j+;=61Wr2`&! zI#f7{4M9J}gi)b+3&cuw7gADvWo+srRM0&n7(I`J|jq zW4mi^0rspKGZY*mhw<$qHV*yznEyo3PY-ZkF=m7dQ3dRzcR|3Dcs8B>wQLX;e+V^> zq*+FQl)|n(J-Xjv(Zt9si{1XubBEr-LpD1HpyoYtKbgL7scTBo19phGq&Qd_gB0KTM+P(WLL^;@GAIpNSU63=9s|grVJ(?Q4jx9!4 z%}?yr76+-!%wHwB*Ira$kSs&a^I0n-vrv7;k-lCloKJhu19Lxw-kbTgG|D;mr>%ho z0}~|pr?8LmSJ=0+cd-EeTA&{3EIHRBDt2-`Ce9;UlkfsWs3mr6})hPrIr+M};1a`gXXI+=HFB>Fy}q&fR&` zr5ioj2#p^@`WWv-N?o$Am?ZGoirVr~x=o^B4wP^B2Ql3Vk!W(p_gIYeSqne(DuH1J zdeXC2#R}b!6NhZEUNi>0-`u*!Cn$?Hn+ zb7B#*V7o>u;LEl^D)7t_`%7<{Ckz+LxsoKKi^5%ox;@Wo|9FB6ky*1T8q9tTqd;=| zFj62eCGbu_img=LQg8YB-WfFHKp{@~PtjPvj%LQ4> z1zV28=@O97%S?-;FY zLxRORv`A|CJR}~rJcPluN)RRFSI*3N$#UCqKPV9*HX$siYF-Kg2CJ^p_6l3nlO^g9 zZnPODK#*za<9v0%UIxE;;$!2>1usFWVq>8=#hG%j zWxJ?&fY^<~167eS-99SW&?fE?wx7H zp{{Q^GSuZKLQBrq25D*;jfNk=R^aF)WCJvqu^%58^zME0y+JCLsV%6NS4`u@Q7|d~ zP%-k}ln~^UaW(+2aS3lP%(_Xp`9~1-I*E{uzAc=@A>U z^a;QWjjIU%;gezy-rj}0{f|DdoDeo7Nh}lmQ2G&eT$NO!_UjwlqUaAzbiJQ)LwF#g zt+J&lBWB5gdkAQoDql%(JG)H19TP%;p4IJAA$JvY6Z6BO@2MA|39grOgZDk6~O`*R&g!Dbg>^BG9x>IF;Du{oqof$djI3LV&ufA-;ON$o< zbp^&|Mu7}ovk(=6Ng37~d%vbYAxl86aBNwk5Vhy|fc{nd(#~%%Z5D(bO_nHT zjRG0eFXU8Ax~TsZcb2Fd&U5k0@t>*}rT<3z!4IYR!7Kjj@m`vjcZ0`T*zkit(s?e{ z{;#TkD}F|!)3Z3C|9<#|_39RF(`8S+=I?%bmo*}QCcP$lg3I~)S|>}ST0Z4zdcK)l zwi!M$;?^S&hB;~9l1P^XCVZ>-1H=c|Jgb_<+8ljA?&r`91gX(mh~(VOu?^jda@>hsqn)`!gm@Yvfd z;#%U0qSS{C1(4ZWFFIS`raRxRwtze)#^nnZ-2mXCTHN@sCXRk}m44b9vsZjE#&35A z(uuMd!W^M6GgNW;s~lR&Saa?3OHtpKT64wbQR+ zv!MT}P$Vui|3%?1qz}e4*k3TS9Lr7R`auM#NMeCz{gsZT@rY1; zW^lG!s!U0_jVdzKZdTLL(m7hSP&f7^@%X_G&nG;`lK4aAjp97&;wArmRVEI_u#))F zmjcydo9Xuq(+y_*avbHFX4&{^&y`1l+|4R7|EVg9Gt;g$D)?3Pj7u);RgnQGU#CVc z{V({xZu);{*eIrYY5AA>sdvxK|IC#Y_30BCHjIB4ahzm3%cvzi^8_BC7aeOt?z-+B zc)Wkwc(q@2Pxy1#Xy3VrH(6-@`%PHX^5q*AJ$Nik6zAbJ+l>|p-CMb`Gt#BgGyIar zRO4b)<_NqnY@Er@8139q8!21rY|2lqZ$X~V zy+5;e8_VznV*SWtywghGyjNZmvVUWX0pX`>rpV4E(5HtqP}9Cp4O25o2N{&0a-tZ@O-5J&NT6YQJsNlRXuSV zX<;Vi+RkXsOJ238#Ky~;EMlDdjyazxnQQFs#KZ5znt@*$$phRtw+)3aKlJ7gBUy>a$z4zuk~Zx5?iZQX5o ziBUeTd-7R3e!gGV1-PB=SO+eE!oTcRjk-`D@##9I^4-bBcO3Mn4)OC~?C~NvKf>T3 zH|`2lgwf>4ozDUaHx>AK?M9-9R=?^m=P+RcVQQCY^ANhKK&RtLIEXB{Ak8opnJtf|xXtjAIkwr`3UmR# zzVO~%Mu(H-nvdoow!j>s3G3=jW%;tQQs{1zZG;QPg^>eTn)0g|1M_VY4 z2!(0E6(Uvv9_Hn}!W0J{De*3EfToV&3^h(pwRB8>r5Re(;K|{dzHuhegH>+()3w{) zHF4m+MQ944f;xZ0Klp$)~0V63;FYahnkZHnjkh|{wP z%MbPW%kOzVT)`G(ZTDK=+WKX$FD+`&s+;tgB71;G01|AxHLFFV-WSu6u*_`&IHCCm z1Ave|(y{j*(i9rHPFx9mseFhQ4){MeVFf>$n?)DkNq0z<)R>x|jkgS)NTDm>Qn{LE zQk%dU^?>GjwO$c(_)((E7D@;1#>c|UcanddM7kR#5S4x!`t?GJPq{ zwiac9pZXpqY)i!)aidrG<&0JD#$tr|jNAN!b*u1dMM8>4F(YzlctL3u?N3RRj`QM> zUfOwIyViJ!4c8k@FE{7A!5kIM{Cd+jTIP1#M{UQ&I<->NNX<9tB#}~s7FW(=t;sl{ za{3k&ny~yP7n+r2m-MIPh8LPu1#-@0Zyu3G2cyS>)19@#mYOlny68^|bTDWFT*h73 zn&xU7#KT!bY|ig-UX1-kT3YM%GXsJ0j|>FHpT;(Hu(JnQzSz5V5&-HV#eH>pCn(X~ zDq5mqzJPDZ!FO2sHGwZ@D!Nt}Y$+MI;$dIw%03@E*)8)ZM2b2rbTGi9n3uHr<`f2& zI~F5~%`>Res@buiV;tt3P|ar&mtEP{2lPV{j?es;~^5{Yo0`O3T7#!|j5(spGjCX7Ex zgG#7IF_?>(bdlVdDCEQI$4TO=I)T-I=p2LmJ!{T)yc5_?8S@~1)RT7y2;vf5?*^=fRwf>Fy8*6gzw|* z)P-ZUi9lDoH0bZt z;lPyHmDx9uqW-RnX-R2Im#!=es=zPLn-67yL5{HRga?}xT;GVluC{RvL*Mad7S}Mx z%_ibzUsr=PO)O>NpI`WN39pSFT4OkYI6mRl&EjYa7iXQ8Ip&=@TZb-~>*yU8xncm$ zO_3YOaGH&(yV}dbQAWY5j{T*Uok2^|SJdEn(RoCaPZZ)+OKh>lj4i$`u0hw%X07N- zsdYJ1q}niV!JESWuKOOdU7k2&qqt~dSV>yg-5S?V8lg)p3JS6gQvLB6I|mz2eL_?wBc5AtQO2yX#O4O)^-du`cTO z{B|deY$0*(O*0N(AV^|43^@k^7{Mtlvt&7pug_C1QUNr+?rk=x==&V{L)bybKIf!3 znG)YhA;sb*T$v(uJ$lk2N3$V3c?r{POp$y#F>_v}Y^o`Q0K@t(lf$)``!#ImuOy}t z;dacZpx-C;(VtAY6m}D@4PcT?7=}-RnB}Dv|qUUh(OVgj+{omn#QfvPWfAhTa z`V0Oi<@|T}pIfvq{M&C)f&Dl9?`+)PRs1>EzwG7y7Fp!KUf@4FyMG7&Id{D*2Y!p> zvswQWF8{qa_&fT~!22@4{g!H?KhVEtyT2>=GY_1H=NRPgU;{df4EuKC~XZgetNextRelationsIndex($this->getMainPartName()); + $chart->setRelationId($rId); + + // Define the chart filename + $filename = "charts/chart{$rId}.xml"; + + // Get the part writer + $writerPart = new \PhpOffice\PhpWord\Writer\Word2007\Part\Chart(); + $writerPart->setElement($chart); + + // ContentTypes.xml + $this->zipClass->addFromString("word/{$filename}", $writerPart->write()); + + // add chart to content type + $xmlRelationsType = ""; + $this->tempDocumentContentTypes = str_replace('', $xmlRelationsType, $this->tempDocumentContentTypes) . ''; + + // Add the chart to relations + $xmlChartRelation = ""; + $this->tempDocumentRelations[$this->getMainPartName()] = str_replace('', $xmlChartRelation, $this->tempDocumentRelations[$this->getMainPartName()]) . ''; + + // Write the chart + $xmlWriter = new XMLWriter(); + $elementWriter = new $objectClass($xmlWriter, $chart, true); + $elementWriter->write(); + + // Place it in the template + $this->replaceXmlBlock($search, '' . $xmlWriter->getData() . '', 'w:p'); + } + private function getImageArgs($varNameWithArgs) { $varElements = explode(':', $varNameWithArgs); diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 8907160b..58fa55d6 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -209,7 +209,7 @@ class Chart extends AbstractPart /** * Write series. * - * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter + * @param \PhpOffice\Common\XMLWriter $xmlWriter * @param bool $scatter */ private function writeSeries(XMLWriter $xmlWriter, $scatter = false) @@ -219,6 +219,7 @@ class Chart extends AbstractPart $colors = $style->getColors(); $index = 0; + $colorIndex = 0; foreach ($series as $seriesItem) { $categories = $seriesItem['categories']; $values = $seriesItem['values']; @@ -265,23 +266,21 @@ class Chart extends AbstractPart $this->writeSeriesItem($xmlWriter, 'cat', $categories); $this->writeSeriesItem($xmlWriter, 'val', $values); - // setting the chart colors was taken from https://github.com/PHPOffice/PHPWord/issues/494 - if (is_array($colors) && count($colors)) { - // This is a workaround to make each series in a stack chart use a different color - if ($this->options['type'] == 'bar' && stristr($this->options['grouping'], 'stacked')) { - array_shift($colors); - } - $colorIndex = 0; - foreach ($colors as $color) { + // check that there are colors + if (is_array($colors) && count($colors)>0) { + // assign a color to each value + $valueIndex=0; + foreach ($values as $value) { + // check that there are still enought colors $xmlWriter->startElement('c:dPt'); - $xmlWriter->writeElementBlock('c:idx', 'val', $colorIndex); + $xmlWriter->writeElementBlock('c:idx', 'val', $valueIndex); $xmlWriter->startElement('c:spPr'); $xmlWriter->startElement('a:solidFill'); - $xmlWriter->writeElementBlock('a:srgbClr', 'val', $color); + $xmlWriter->writeElementBlock('a:srgbClr', 'val', $colors[$colorIndex++ % count($colors)]); $xmlWriter->endElement(); // a:solidFill $xmlWriter->endElement(); // c:spPr $xmlWriter->endElement(); // c:dPt - $colorIndex++; + $valueIndex++; } } } From 48ee2eb78a5d74bd9845ea86fb277b156759ae34 Mon Sep 17 00:00:00 2001 From: dbarzin Date: Sat, 6 Feb 2021 10:50:21 +0100 Subject: [PATCH 107/112] fix param error --- src/PhpWord/Writer/Word2007/Part/Chart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 58fa55d6..705a5c28 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -209,7 +209,7 @@ class Chart extends AbstractPart /** * Write series. * - * @param \PhpOffice\Common\XMLWriter $xmlWriter + * @param \PhpOffice\PhpWord\Shared\XMLWriter $xmlWriter * @param bool $scatter */ private function writeSeries(XMLWriter $xmlWriter, $scatter = false) From 214df1a1cbede6a99b643e14a0be2a0dbd31a898 Mon Sep 17 00:00:00 2001 From: dbarzin Date: Sat, 6 Feb 2021 12:57:16 +0100 Subject: [PATCH 108/112] fix unit test --- tests/PhpWord/Writer/Word2007/Element/ChartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhpWord/Writer/Word2007/Element/ChartTest.php b/tests/PhpWord/Writer/Word2007/Element/ChartTest.php index 432d8db8..a4931919 100644 --- a/tests/PhpWord/Writer/Word2007/Element/ChartTest.php +++ b/tests/PhpWord/Writer/Word2007/Element/ChartTest.php @@ -113,7 +113,7 @@ class ChartTest extends \PHPUnit\Framework\TestCase for ($idxp1 = 1; $idxp1 < $numColor; ++$idxp1) { $idx = $idxp1; // stacked bar chart is shifted $element = $path . "/c:ser/c:dPt[$idxp1]/c:spPr/a:solidFill/a:srgbClr"; - self::assertEquals($colorArray[$idx], $doc->getElementAttribute($element, 'val'), "bar chart idx=$idx"); + self::assertEquals($colorArray[$idx-1], $doc->getElementAttribute($element, 'val'), "bar chart idx=$idx"); } } From 1168789e8a55e2564cbc02236b23cf69cb0c1504 Mon Sep 17 00:00:00 2001 From: Antoine de Troostembergh Date: Sat, 6 Feb 2021 21:32:30 +0100 Subject: [PATCH 109/112] fix formatting --- docs/templates-processing.rst | 17 +++++++++++--- samples/Sample_41_TemplateSetChart.php | 26 +++++++++++----------- src/PhpWord/TemplateProcessor.php | 4 ++-- src/PhpWord/Writer/Word2007/Part/Chart.php | 6 ++--- tests/PhpWord/TemplateProcessorTest.php | 2 +- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/docs/templates-processing.rst b/docs/templates-processing.rst index 9166890e..7d0ef2e6 100644 --- a/docs/templates-processing.rst +++ b/docs/templates-processing.rst @@ -249,9 +249,20 @@ See ``Sample_40_TemplateSetComplexValue.php`` for examples. $table->addCell(150)->addText('Cell B2'); $table->addCell(150)->addText('Cell B3'); $templateProcessor->setComplexBlock('table', $table); - + +setChartValue +""""""""""""" +Replace a variable by a chart. + +.. code-block:: php + + $categories = array('A', 'B', 'C', 'D', 'E'); + $series1 = array(1, 3, 2, 5, 4); + $chart = new Chart('doughnut', $categories, $series1); + $templateProcessor->setChartValue('myChart', $chart); + save -""""""""" +"""" Saves the loaded template within the current directory. Returns the file path. .. code-block:: php @@ -259,7 +270,7 @@ Saves the loaded template within the current directory. Returns the file path. $filepath = $templateProcessor->save(); saveAs -""""""""" +"""""" Saves a copy of the loaded template in the indicated path. .. code-block:: php diff --git a/samples/Sample_41_TemplateSetChart.php b/samples/Sample_41_TemplateSetChart.php index 102fa3ac..2b017d7f 100644 --- a/samples/Sample_41_TemplateSetChart.php +++ b/samples/Sample_41_TemplateSetChart.php @@ -17,23 +17,23 @@ $series1 = array(1, 3, 2, 5, 4); $series2 = array(3, 1, 7, 2, 6); $series3 = array(8, 3, 2, 5, 4); -$i=0; +$i = 0; foreach ($chartTypes as $chartType) { - $chart = new Chart($chartType, $categories, $series1); + $chart = new Chart($chartType, $categories, $series1); - if (in_array($chartType, $twoSeries)) { - $chart->addSeries($categories, $series2); - } - if (in_array($chartType, $threeSeries)) { - $chart->addSeries($categories, $series3); - } + if (in_array($chartType, $twoSeries)) { + $chart->addSeries($categories, $series2); + } + if (in_array($chartType, $threeSeries)) { + $chart->addSeries($categories, $series3); + } - $chart->getStyle() - ->setWidth(Converter::inchToEmu(3)) - ->setHeight(Converter::inchToEmu(3)); + $chart->getStyle() + ->setWidth(Converter::inchToEmu(3)) + ->setHeight(Converter::inchToEmu(3)); - $templateProcessor->setChart("chart{$i}", $chart); - $i++; + $templateProcessor->setChart("chart{$i}", $chart); + $i++; } echo date('H:i:s'), ' Saving the result document...', EOL; diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index f67bc1cd..2d4ab2be 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -365,12 +365,12 @@ class TemplateProcessor $objectClass = 'PhpOffice\\PhpWord\\Writer\\Word2007\\Element\\' . $elementName; // Get the next relation id - $rId= $this->getNextRelationsIndex($this->getMainPartName()); + $rId = $this->getNextRelationsIndex($this->getMainPartName()); $chart->setRelationId($rId); // Define the chart filename $filename = "charts/chart{$rId}.xml"; - + // Get the part writer $writerPart = new \PhpOffice\PhpWord\Writer\Word2007\Part\Chart(); $writerPart->setElement($chart); diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 705a5c28..168f0216 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -267,10 +267,10 @@ class Chart extends AbstractPart $this->writeSeriesItem($xmlWriter, 'val', $values); // check that there are colors - if (is_array($colors) && count($colors)>0) { + if (is_array($colors) && count($colors) > 0) { // assign a color to each value - $valueIndex=0; - foreach ($values as $value) { + $valueIndex = 0; + for ($i = 1; $i < count($values); $i++) { // check that there are still enought colors $xmlWriter->startElement('c:dPt'); $xmlWriter->writeElementBlock('c:idx', 'val', $valueIndex); diff --git a/tests/PhpWord/TemplateProcessorTest.php b/tests/PhpWord/TemplateProcessorTest.php index 852bf687..391daa2d 100644 --- a/tests/PhpWord/TemplateProcessorTest.php +++ b/tests/PhpWord/TemplateProcessorTest.php @@ -401,7 +401,7 @@ final class TemplateProcessorTest extends \PHPUnit\Framework\TestCase return $imagePath; }, 'documentContent' => array('path' => $imagePath, 'width' => 500, 'height' => 500), - 'footerValue' => array('path' => $imagePath, 'width' => 100, 'height' => 50, 'ratio' => false), + 'footerValue' => array('path' => $imagePath, 'width' => 100, 'height' => 50, 'ratio' => false), ); $templateProcessor->setImageValue(array_keys($variablesReplace), $variablesReplace); From 31259f6448c943e126654ca0ebf86900735b35c7 Mon Sep 17 00:00:00 2001 From: Antoine de Troostembergh Date: Sat, 6 Feb 2021 22:12:19 +0100 Subject: [PATCH 110/112] fix --- src/PhpWord/Writer/Word2007/Part/Chart.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PhpWord/Writer/Word2007/Part/Chart.php b/src/PhpWord/Writer/Word2007/Part/Chart.php index 168f0216..e0d1752b 100644 --- a/src/PhpWord/Writer/Word2007/Part/Chart.php +++ b/src/PhpWord/Writer/Word2007/Part/Chart.php @@ -270,7 +270,7 @@ class Chart extends AbstractPart if (is_array($colors) && count($colors) > 0) { // assign a color to each value $valueIndex = 0; - for ($i = 1; $i < count($values); $i++) { + for ($i = 0; $i < count($values); $i++) { // check that there are still enought colors $xmlWriter->startElement('c:dPt'); $xmlWriter->writeElementBlock('c:idx', 'val', $valueIndex); From dc9b1edb5b39ff308f4ede2e729fbc57e96122fd Mon Sep 17 00:00:00 2001 From: troosan Date: Sat, 6 Feb 2021 22:50:11 +0100 Subject: [PATCH 111/112] fix formatting --- tests/PhpWord/Writer/Word2007/Element/ChartTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PhpWord/Writer/Word2007/Element/ChartTest.php b/tests/PhpWord/Writer/Word2007/Element/ChartTest.php index a4931919..a69838a9 100644 --- a/tests/PhpWord/Writer/Word2007/Element/ChartTest.php +++ b/tests/PhpWord/Writer/Word2007/Element/ChartTest.php @@ -113,7 +113,7 @@ class ChartTest extends \PHPUnit\Framework\TestCase for ($idxp1 = 1; $idxp1 < $numColor; ++$idxp1) { $idx = $idxp1; // stacked bar chart is shifted $element = $path . "/c:ser/c:dPt[$idxp1]/c:spPr/a:solidFill/a:srgbClr"; - self::assertEquals($colorArray[$idx-1], $doc->getElementAttribute($element, 'val'), "bar chart idx=$idx"); + self::assertEquals($colorArray[$idx - 1], $doc->getElementAttribute($element, 'val'), "bar chart idx=$idx"); } } From 26e479422abda292e255e98aef41b93a900362d9 Mon Sep 17 00:00:00 2001 From: troosan Date: Sun, 7 Feb 2021 15:06:01 +0100 Subject: [PATCH 112/112] Fix formatting --- src/PhpWord/TemplateProcessor.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php index 5973dd88..534f8d60 100644 --- a/src/PhpWord/TemplateProcessor.php +++ b/src/PhpWord/TemplateProcessor.php @@ -610,8 +610,8 @@ class TemplateProcessor // replace on each iteration, because in one tag we can have 2+ inline variables => before proceed next variable we need to change $partContent $partContent = $this->setValueForPart($wholeTag, $replaceXml, $partContent, $limit); } - $i++; - if($i >= $limit) { + + if (++$i >= $limit) { break; } }