nA)Bn{l?5
z8#e_1@k8v~X5@#X{uhDZVREHQS8C1-^lPtI;`_c4g{LesNdX1Ehe>bw-+V;Jp6?HG
zwJ1?QIGz&sr1o=|PQ*X8sK%810{=(_{v}uE$AYeWERAIiH;OReNR2*V)jEx!=?ln2
zGwfP7mrI2#kV^#FIU!k7o;rz3EYyx~h?NKo94>RzHpn?YuOSC*T9}}9TSx9Fqp`p@`0o9a2B%-^6?owPToiGg0y26IW^
zJ!00Ueo(hS!V1iufY7u0g_g-c=S&w%NDz5j?#CJUK}0s4m*C4eUrX6|P
z7#f}P(Wmxr=gHfM=fHY(jalweN|3Pn`FZq$*4)L|{EJZjh$}iNqBB7r5_TGm`kQ!{
zPd;cB1-&;*`VW_0k<%o}r<>Y@Z#!?;<;*UY-M#_yX5c%w5V$NeoF15bgLc#Z@Kl_|
z1l&8IEez1UN)GL(zcRM|rjz|AZ|ncLWWRUUAD^&~6(P_?0T7n1mloR1?NaBS_haSD
zGCB*%pf?grNk20hP`9*LY*~NH?7Ybke0!<*zS$qB!MYo>^%5oRADnR35?W^4bzM
zq^rA~4R^GSjHq^gL(1brmkiS34o-2l1TRNuu3hQ$5FraFjw4~cmGJkOus|FP!HbJ;%?{JE6IPvH0OIp`sOTUg^R
z@a`G|zo1vpim!jIIB*yKCl&H9Fbqr_`VaX3AV$8c=`NS;FI{9%n$LeT+up_BFoRU51fgXbGA>(0}ob+(qB5ult35&G`d;
zx7zNmio5j{zf|<_{ZR3HwZ&cdpP8e-z%Vc@(A>aZqtZX)(Om_9rds~4K=|=p1-}w6
z?`pZ5arjG*$CDpt@kjpQUHsjIgkShn>7Q5OpY(*g;JYD%Utm7XAK*Iygu5E@8bV-!hfM*V9cOi`@dZByYN4qp5NiThQGmoxIlN&e?E$SM=u!v
e{tN!sgXy^}Jan-5o^*@@^AQdPX4Ulj*8c%+K=Wn*
literal 0
HcmV?d00001
From 0ac841e338884e7cac15ea11ff9d623fb4013b1b Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 6 Feb 2024 16:13:40 -0800
Subject: [PATCH 002/153] Update Html.php
---
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 21d8404ddc..6f168164c6 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -109,7 +109,7 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
$dom->loadXML($html);
static::$xpath = new DOMXPath($dom);
$node = $dom->getElementsByTagName('html');
- if (count($node) === 0) {
+ if (count($node) === 0 || $node->item(0) === null) {
$node = $dom->getElementsByTagName('body');
}
From 39fbabc5e81512078d9f15251d957701da56dca0 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 9 May 2024 00:37:05 -0700
Subject: [PATCH 003/153] Load Html as Html Not Xml
Better from a technical standpoint.
Pre-processing much simplified.
Some post-processing added because loadHTML preserveWhiteSpace=false does not work well.
Not-UTF8 html can now be loaded.
No need to surround snippets with body tag.
---
src/PhpWord/Shared/Html.php | 69 +++++++++++++++---
.../PhpWordTests/Reader/Html/CharsetTest.php | 63 ++++++++++++++++
.../Reader/{ => Html}/HTMLTest.php | 4 +-
tests/PhpWordTests/Shared/HtmlTest.php | 40 +++++++---
.../_files/html/charset.ISO-8859-1.html | 17 +++++
.../_files/html/charset.ISO-8859-1.html4.html | 17 +++++
.../_files/html/charset.ISO-8859-2.html | 17 +++++
.../_files/html/charset.UTF-16.bebom.html | Bin 0 -> 604 bytes
.../_files/html/charset.UTF-16.lebom.html | Bin 0 -> 604 bytes
.../_files/html/charset.UTF-8.bom.html | 16 ++++
.../_files/html/charset.UTF-8.html | 17 +++++
.../_files/html/charset.gb18030.html | 9 +++
.../_files/html/charset.nocharset.html | 8 ++
.../_files/html/charset.unknown.html | 17 +++++
14 files changed, 270 insertions(+), 24 deletions(-)
create mode 100644 tests/PhpWordTests/Reader/Html/CharsetTest.php
rename tests/PhpWordTests/Reader/{ => Html}/HTMLTest.php (92%)
create mode 100644 tests/PhpWordTests/_files/html/charset.ISO-8859-1.html
create mode 100644 tests/PhpWordTests/_files/html/charset.ISO-8859-1.html4.html
create mode 100644 tests/PhpWordTests/_files/html/charset.ISO-8859-2.html
create mode 100644 tests/PhpWordTests/_files/html/charset.UTF-16.bebom.html
create mode 100644 tests/PhpWordTests/_files/html/charset.UTF-16.lebom.html
create mode 100644 tests/PhpWordTests/_files/html/charset.UTF-8.bom.html
create mode 100644 tests/PhpWordTests/_files/html/charset.UTF-8.html
create mode 100644 tests/PhpWordTests/_files/html/charset.gb18030.html
create mode 100644 tests/PhpWordTests/_files/html/charset.nocharset.html
create mode 100644 tests/PhpWordTests/_files/html/charset.unknown.html
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 6f168164c6..9210aec19c 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -46,6 +46,8 @@ class Html
private const RGB_REGEXP = '/^\s*rgb\s*[(]\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*[)]\s*$/';
+ private const DECLARES_CHARSET = '/ charset=/i';
+
protected static $listIndex = 0;
protected static $xpath;
@@ -55,6 +57,9 @@ class Html
/** @var ?DocInfo */
protected static $docInfo;
+ /** @var bool */
+ private static $addbody = false;
+
/**
* @var Css
*/
@@ -88,16 +93,14 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
}
}
- // Preprocess: remove all line ends, decode HTML entity,
- // fix ampersand and angle brackets and add body tag for HTML fragments
- $html = str_replace(["\n", "\r"], '', $html);
- $html = str_replace(['<', '>', '&', '"'], ['_lt_', '_gt_', '_amp_', '_quot_'], $html);
- $html = html_entity_decode($html, ENT_QUOTES, 'UTF-8');
- $html = str_replace('&', '&', $html);
- $html = str_replace(['_lt_', '_gt_', '_amp_', '_quot_'], ['<', '>', '&', '"'], $html);
-
- if (false === $fullHTML) {
- $html = '' . $html . '';
+ if (substr($html, 0, 2) === "\xfe\xff" || substr($html, 0, 2) === "\xff\xfe") {
+ $html = mb_convert_encoding($html, 'UTF-8', 'UTF-16');
+ }
+ if (substr($html, 0, 3) === "\xEF\xBB\xBF") {
+ $html = substr($html, 3);
+ }
+ if (self::$addbody && false === $fullHTML) {
+ $html = '' . $html . ''; // @codeCoverageIgnore
}
// Load DOM
@@ -105,8 +108,20 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
$orignalLibEntityLoader = libxml_disable_entity_loader(true); // @codeCoverageIgnore
}
$dom = new DOMDocument();
+ $html = self::replaceNonAsciiIfNeeded($html);
$dom->preserveWhiteSpace = $preserveWhiteSpace;
- $dom->loadXML($html);
+
+ try {
+ $result = $dom->loadHTML($html);
+ $exceptionMessage = 'DOM loadHTML failed';
+ } catch (Exception $e) {
+ $result = false;
+ $exceptionMessage = $e->getMessage();
+ }
+ if ($result === false) {
+ throw new Exception($exceptionMessage);
+ }
+ self::removeAnnoyingWhitespaceTextNodes($dom);
static::$xpath = new DOMXPath($dom);
$node = $dom->getElementsByTagName('html');
if (count($node) === 0 || $node->item(0) === null) {
@@ -119,6 +134,38 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
}
}
+ // https://www.php.net/manual/en/domdocument.loadhtml.php
+ private static function removeAnnoyingWhitespaceTextNodes(DOMNode $node): void
+ {
+ if ($node->hasChildNodes()) {
+ for ($i = $node->childNodes->length - 1; $i >= 0; --$i) {
+ self::removeAnnoyingWhitespaceTextNodes($node->childNodes->item($i));
+ }
+ }
+ if ($node->nodeType === XML_TEXT_NODE && !$node->hasChildNodes() && !$node->hasAttributes() && empty(trim($node->textContent))) {
+ $node->parentNode->removeChild($node);
+ }
+ }
+
+ private static function replaceNonAscii(array $matches): string
+ {
+ return '' . mb_ord($matches[0], 'UTF-8') . ';';
+ }
+
+ private static function replaceNonAsciiIfNeeded(string $convert): ?string
+ {
+ if (preg_match(self::DECLARES_CHARSET, $convert) !== 1) {
+ $lowend = "\u{80}";
+ $highend = "\u{10ffff}";
+ $regexp = "/[$lowend-$highend]/u";
+ /** @var callable $callback */
+ $callback = [self::class, 'replaceNonAscii'];
+ $convert = preg_replace_callback($regexp, $callback, $convert);
+ }
+
+ return $convert;
+ }
+
/**
* parse Inline style of a node.
*
diff --git a/tests/PhpWordTests/Reader/Html/CharsetTest.php b/tests/PhpWordTests/Reader/Html/CharsetTest.php
new file mode 100644
index 0000000000..60e80964a2
--- /dev/null
+++ b/tests/PhpWordTests/Reader/Html/CharsetTest.php
@@ -0,0 +1,63 @@
+expectException(Throwable::class);
+ $this->expectExceptionMessage('unknown encoding');
+ }
+ $directory = 'tests/PhpWordTests/_files/html';
+ $reader = new HTML();
+ $doc = $reader->load("$directory/$filename");
+ $sections = $doc->getSections();
+ self::assertCount(1, $sections);
+ $section = $sections[0];
+ $elements = $section->getElements();
+ $element = $elements[0];
+ self::assertInstanceOf(TextRun::class, $element);
+ self::assertSame($expectedResult, $element->getText());
+ }
+
+ public static function providerCharset(): array
+ {
+ return [
+ ['charset.ISO-8859-1.html', 'À1'],
+ ['charset.ISO-8859-1.html4.html', 'À1'],
+ ['charset.ISO-8859-2.html', 'Ŕ1'],
+ ['charset.nocharset.html', 'À1'],
+ ['charset.UTF-8.html', 'À1'],
+ ['charset.UTF-8.bom.html', 'À1'],
+ ['charset.UTF-16.bebom.html', 'À1'],
+ ['charset.UTF-16.lebom.html', 'À1'],
+ ['charset.gb18030.html', '电视机'],
+ ['charset.unknown.html', 'exception'],
+ ];
+ }
+}
diff --git a/tests/PhpWordTests/Reader/HTMLTest.php b/tests/PhpWordTests/Reader/Html/HTMLTest.php
similarity index 92%
rename from tests/PhpWordTests/Reader/HTMLTest.php
rename to tests/PhpWordTests/Reader/Html/HTMLTest.php
index f091e5a275..c0e150e495 100644
--- a/tests/PhpWordTests/Reader/HTMLTest.php
+++ b/tests/PhpWordTests/Reader/Html/HTMLTest.php
@@ -15,7 +15,7 @@
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
-namespace PhpOffice\PhpWordTests\Reader;
+namespace PhpOffice\PhpWordTests\Reader\Html;
use Exception;
use PhpOffice\PhpWord\IOFactory;
@@ -34,7 +34,7 @@ class HTMLTest extends \PHPUnit\Framework\TestCase
*/
public function testLoad(): void
{
- $filename = __DIR__ . '/../_files/documents/reader.html';
+ $filename = 'tests/PhpWordTests/_files/documents/reader.html';
$phpWord = IOFactory::load($filename, 'HTML');
self::assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $phpWord);
}
diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index 765d79f906..76e8273ec4 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -23,6 +23,7 @@
use PhpOffice\PhpWord\Element\Text;
use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\PhpWord;
+use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Shared\Html;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
@@ -52,6 +53,7 @@ protected function tearDown(): void
*/
public function testAddHtml(): void
{
+ Settings::setOutputEscapingEnabled(true);
$content = '';
// Default
@@ -80,16 +82,31 @@ public function testAddHtml(): void
// Other parts
$section = $phpWord->addSection();
$content = '';
+ $expectd = '';
$content .= '';
$content .= '';
$content .= '- Bullet
';
$content .= "'Single Quoted Text'";
+ $expectd .= "'Single Quoted Text'";
$content .= '"Double Quoted Text"';
- $content .= '& Ampersand';
+ $expectd .= '"Double Quoted Text"';
+ $content .= '& Ampersand';
+ $expectd .= '& Ampersand';
$content .= '<>“‘’«»‹›';
+ $expectd .= '<>“‘’«»‹›';
$content .= '&•°…™©®—';
+ $expectd .= '&•°…™©®—';
$content .= '– ²³¼½¾';
+ $expectd .= "–\u{a0} ²³¼½¾";
Html::addHtml($section, $content);
+ $elements = $section->getElements();
+ foreach ($elements as $element) {
+ if ($element instanceof Text) {
+ self::assertSame($expectd, $element->getText());
+
+ break;
+ }
+ }
}
/**
@@ -110,7 +127,7 @@ public function testParseFullHtml(): void
*/
public function testParseHtmlEntities(): void
{
- \PhpOffice\PhpWord\Settings::setOutputEscapingEnabled(true);
+ Settings::setOutputEscapingEnabled(true);
$phpWord = new PhpWord();
$section = $phpWord->addSection();
Html::addHtml($section, 'text with entities <my text>');
@@ -138,13 +155,14 @@ public function testParseStyle(): void
Html::addHtml($section, $html);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
- self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]'));
- self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r'));
- self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:t'));
- self::assertEquals('Calculator', $doc->getElement('/w:document/w:body/w:p[2]/w:r/w:t')->nodeValue);
- self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:rPr'));
- self::assertTrue($doc->elementExists('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz'));
- self::assertEquals('22.5', $doc->getElementAttribute('/w:document/w:body/w:p[2]/w:r/w:rPr/w:sz', 'w:val'));
+ $element = '/w:document/w:body/w:p';
+ self::assertTrue($doc->elementExists($element));
+ self::assertTrue($doc->elementExists("$element/w:r"));
+ self::assertTrue($doc->elementExists("$element/w:r/w:t"));
+ self::assertEquals('Calculator', $doc->getElement("$element/w:r/w:t")->nodeValue);
+ self::assertTrue($doc->elementExists("$element/w:r/w:rPr"));
+ self::assertTrue($doc->elementExists("$element/w:r/w:rPr/w:sz"));
+ self::assertEquals('22.5', $doc->getElementAttribute("$element/w:r/w:rPr/w:sz", 'w:val'));
}
public function testParseStyleTableClassName(): void
@@ -778,7 +796,7 @@ public function testParseListWithFormat(): void
{
$phpWord = new PhpWord();
$section = $phpWord->addSection();
- $html = preg_replace('/\s+/', ' ', '
+ $html = '
- Some text before
list item1 bold with text after bold
@@ -790,7 +808,7 @@ public function testParseListWithFormat(): void
list item2
-
');
+
';
Html::addHtml($section, $html, false, false);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
diff --git a/tests/PhpWordTests/_files/html/charset.ISO-8859-1.html b/tests/PhpWordTests/_files/html/charset.ISO-8859-1.html
new file mode 100644
index 0000000000..fd27c975f3
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.ISO-8859-1.html
@@ -0,0 +1,17 @@
+
+
+
+
+ ISO-8859-1
+
+
+ 1
+
B1
+ 1
+ D1
+ 2
+ B2
+ C2
+ 2
+
+
diff --git a/tests/PhpWordTests/_files/html/charset.ISO-8859-1.html4.html b/tests/PhpWordTests/_files/html/charset.ISO-8859-1.html4.html
new file mode 100644
index 0000000000..8a14894517
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.ISO-8859-1.html4.html
@@ -0,0 +1,17 @@
+
+
+
+
+ ISO-8859-1 Html4 Doctype and Meta
+
+
+ 1
+ B1
+ 1
+ D1
+ 2
+ B2
+ C2
+ 2
+
+
diff --git a/tests/PhpWordTests/_files/html/charset.ISO-8859-2.html b/tests/PhpWordTests/_files/html/charset.ISO-8859-2.html
new file mode 100644
index 0000000000..c2b494ff99
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.ISO-8859-2.html
@@ -0,0 +1,17 @@
+
+
+
+
+ ISO-8859-2
+
+
+ 1
+ B1
+ 1
+ D1
+ 2
+ B2
+ C2
+ 2
+
+
diff --git a/tests/PhpWordTests/_files/html/charset.UTF-16.bebom.html b/tests/PhpWordTests/_files/html/charset.UTF-16.bebom.html
new file mode 100644
index 0000000000000000000000000000000000000000..6b29e7d2b011f7d8099fd407244dad7ef0d3121a
GIT binary patch
literal 604
zcmezOpFsf%Z5R|8Tp0WroEbtGA{hc0T!G>l3?&S?3^@#T3|vU+V4?~@nM8&>hI9s7
z26cv1AWI!XWhzib3YusMLnhGN9H0(6hESkwZVb8%h74v5HVpbeafBSQo%%qxU^6R;
zAs=XdC5jC&AHirFh614c0iYcq8$fJ?$uKoA8lu*T1hvmeQ0qd1+QSS+B)FG&waz4%
UdjYFjP$*&xcaS(NZ2-d<0BnUsYybcN
literal 0
HcmV?d00001
diff --git a/tests/PhpWordTests/_files/html/charset.UTF-16.lebom.html b/tests/PhpWordTests/_files/html/charset.UTF-16.lebom.html
new file mode 100644
index 0000000000000000000000000000000000000000..4ba47a81395d2f8076299b471e0357e1287ce1e3
GIT binary patch
literal 604
zcmezWPk{jfZ5R|8Tp0WroEbtGA{hc0T!G>l3?&S?3^@#T3|vrE3Jf-IQ3aq(B10ZS
zI)g2PIzuXurH-L86{sQwO|*m|6KHM@P=_5uD9|=H23-b21~Z_^`ap4n9I~DIKwGhy
zmBf$_G`|wX2AGdvv<*W6Q2qeW4v-BXHo|0>8W;^x>qLUu=On0gAwlh71|t&OOT1cV
U63o4TRV^qKv4uNG967}R0Iu{zYybcN
literal 0
HcmV?d00001
diff --git a/tests/PhpWordTests/_files/html/charset.UTF-8.bom.html b/tests/PhpWordTests/_files/html/charset.UTF-8.bom.html
new file mode 100644
index 0000000000..5a49399018
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.UTF-8.bom.html
@@ -0,0 +1,16 @@
+
+
+
+ UTF-8
+
+
+ À1
+ B1
+ ç1
+ D1
+ Ã2
+ B2
+ C2
+ Ð2
+
+
diff --git a/tests/PhpWordTests/_files/html/charset.UTF-8.html b/tests/PhpWordTests/_files/html/charset.UTF-8.html
new file mode 100644
index 0000000000..9ae5a8e343
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.UTF-8.html
@@ -0,0 +1,17 @@
+
+
+
+
+ UTF-8
+
+
+ À1
+ B1
+ ç1
+ D1
+ Ã2
+ B2
+ C2
+ Ð2
+
+
diff --git a/tests/PhpWordTests/_files/html/charset.gb18030.html b/tests/PhpWordTests/_files/html/charset.gb18030.html
new file mode 100644
index 0000000000..271a55fc54
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.gb18030.html
@@ -0,0 +1,9 @@
+
+
+
+gb18030
+
+
+ ӻ
+
+
diff --git a/tests/PhpWordTests/_files/html/charset.nocharset.html b/tests/PhpWordTests/_files/html/charset.nocharset.html
new file mode 100644
index 0000000000..d6829b2edc
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.nocharset.html
@@ -0,0 +1,8 @@
+ À1
+ B1
+ ç1
+ D1
+ Ã2
+ B2
+ C2
+ Ð2
diff --git a/tests/PhpWordTests/_files/html/charset.unknown.html b/tests/PhpWordTests/_files/html/charset.unknown.html
new file mode 100644
index 0000000000..189638a80f
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/charset.unknown.html
@@ -0,0 +1,17 @@
+
+
+
+
+ UTF-8
+
+
+ À1
+ B1
+ ç1
+ D1
+ Ã2
+ B2
+ C2
+ Ð2
+
+
From cf1fd2a112419e72878b40a7589212f2dfd29c3c Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 9 May 2024 17:35:13 -0700
Subject: [PATCH 004/153] Permit Some Backwards Compatibility
It's debatable, but allow unescaped ampersand and unknown charset.
---
phpunit.xml.dist | 4 ++--
src/PhpWord/Shared/Html.php | 4 ++--
tests/PhpWordTests/Reader/Html/CharsetTest.php | 2 +-
tests/PhpWordTests/Shared/HtmlTest.php | 8 ++++----
4 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 6f1f5445ab..ff0c676fad 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -7,10 +7,10 @@
./src/PhpWord/Shared/PCLZip
-
+
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 9210aec19c..2d68806f33 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -112,14 +112,14 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
$dom->preserveWhiteSpace = $preserveWhiteSpace;
try {
- $result = $dom->loadHTML($html);
+ $result = $dom->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR);
$exceptionMessage = 'DOM loadHTML failed';
} catch (Exception $e) {
$result = false;
$exceptionMessage = $e->getMessage();
}
if ($result === false) {
- throw new Exception($exceptionMessage);
+ throw new Exception($exceptionMessage); // @codeCoverageIgnore
}
self::removeAnnoyingWhitespaceTextNodes($dom);
static::$xpath = new DOMXPath($dom);
diff --git a/tests/PhpWordTests/Reader/Html/CharsetTest.php b/tests/PhpWordTests/Reader/Html/CharsetTest.php
index 60e80964a2..4c73d70bb0 100644
--- a/tests/PhpWordTests/Reader/Html/CharsetTest.php
+++ b/tests/PhpWordTests/Reader/Html/CharsetTest.php
@@ -57,7 +57,7 @@ public static function providerCharset(): array
['charset.UTF-16.bebom.html', 'À1'],
['charset.UTF-16.lebom.html', 'À1'],
['charset.gb18030.html', '电视机'],
- ['charset.unknown.html', 'exception'],
+ 'loadhtml gives its best shot' => ['charset.unknown.html', "Ã\u{80}1"],
];
}
}
diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index 76e8273ec4..3fa4b5c1d0 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -90,14 +90,14 @@ public function testAddHtml(): void
$expectd .= "'Single Quoted Text'";
$content .= '"Double Quoted Text"';
$expectd .= '"Double Quoted Text"';
- $content .= '& Ampersand';
+ $content .= '& Ampersand';
$expectd .= '& Ampersand';
- $content .= '<>“‘’«»‹›';
- $expectd .= '<>“‘’«»‹›';
+ $content .= '<>“”‘’«»‹›';
+ $expectd .= '<>“”‘’«»‹›';
$content .= '&•°…™©®—';
$expectd .= '&•°…™©®—';
$content .= '– ²³¼½¾';
- $expectd .= "–\u{a0} ²³¼½¾";
+ $expectd .= "–\u{a0}\u{2003}\u{2002}²³¼½¾";
Html::addHtml($section, $content);
$elements = $section->getElements();
foreach ($elements as $element) {
From 2f799c2ba9be781106ac84545a58b58b2fc00f98 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Fri, 16 Aug 2024 08:10:24 -0700
Subject: [PATCH 005/153] Phpstan False Positives
---
src/PhpWord/Shared/Html.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index d78338526a..2d96c375f7 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -180,7 +180,7 @@ protected static function parseInlineStyle($node, &$styles)
$attributes = $node->attributes; // get all the attributes(eg: id, class)
$bidi = false;
- $direction = isset($attributes['dir']) ? $attributes['dir']->value : '';
+ $direction = isset($attributes['dir']) ? $attributes['dir']->value : ''; // @phpstan-ignore-line
if ($direction === 'rtl') {
$bidi = $styles['bidi'] = $styles['rtl'] = true;
$styles['textDirection'] = TextDirection::RLTB;
@@ -550,7 +550,7 @@ protected static function parseTable($node, $element, &$styles)
$attributes = $node->attributes;
if ($attributes->getNamedItem('border') !== null && is_object($newElement->getStyle())) {
- $border = (int) $attributes->getNamedItem('border')->value;
+ $border = (int) $attributes->getNamedItem('border')->value; // @phpstan-ignore-line
$newElement->getStyle()->setBorderSize((int) Converter::pixelToTwip($border));
$newElement->getStyle()->setBorderStyle(($border === 0) ? 'none' : 'single');
}
From d9120c7f3782496d606ec087baa524ced709a51e Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Fri, 16 Aug 2024 08:39:59 -0700
Subject: [PATCH 006/153] Improve Resolution of Phpstan Problem
---
src/PhpWord/Shared/Html.php | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 2d96c375f7..7fa9c6f36f 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -180,7 +180,8 @@ protected static function parseInlineStyle($node, &$styles)
$attributes = $node->attributes; // get all the attributes(eg: id, class)
$bidi = false;
- $direction = isset($attributes['dir']) ? $attributes['dir']->value : ''; // @phpstan-ignore-line
+ $attrDir = $attributes->getNamedItem('dir');
+ $direction = isset($attrDir) ? $attrDir->nodeValue : '';
if ($direction === 'rtl') {
$bidi = $styles['bidi'] = $styles['rtl'] = true;
$styles['textDirection'] = TextDirection::RLTB;
@@ -550,7 +551,7 @@ protected static function parseTable($node, $element, &$styles)
$attributes = $node->attributes;
if ($attributes->getNamedItem('border') !== null && is_object($newElement->getStyle())) {
- $border = (int) $attributes->getNamedItem('border')->value; // @phpstan-ignore-line
+ $border = (int) $attributes->getNamedItem('border')->nodeValue;
$newElement->getStyle()->setBorderSize((int) Converter::pixelToTwip($border));
$newElement->getStyle()->setBorderStyle(($border === 0) ? 'none' : 'single');
}
From 1093a3b3ad5295a71cd3d7dbb5ed56d4f5de69c6 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 3 Sep 2024 23:34:34 -0700
Subject: [PATCH 007/153] Update Sample_45_RTLTitles.php
---
samples/Sample_45_RTLTitles.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/Sample_45_RTLTitles.php b/samples/Sample_45_RTLTitles.php
index 83dd9b9872..e39510c97f 100644
--- a/samples/Sample_45_RTLTitles.php
+++ b/samples/Sample_45_RTLTitles.php
@@ -6,7 +6,7 @@
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Shared\Html as SharedHtml;
-// Suggested by issue 2427.
+// Suggested by issue #2427.
echo date('H:i:s'), ' Create new PhpWord object', EOL;
$phpWord = new PhpWord();
Settings::setDefaultRtl(true);
From 74918d0909c8224b1a46a4dfbf1494f11e8dd5ed Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 3 Sep 2024 23:38:32 -0700
Subject: [PATCH 008/153] Update Sample_45_RTLTitles.php
---
samples/Sample_45_RTLTitles.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/Sample_45_RTLTitles.php b/samples/Sample_45_RTLTitles.php
index e39510c97f..83dd9b9872 100644
--- a/samples/Sample_45_RTLTitles.php
+++ b/samples/Sample_45_RTLTitles.php
@@ -6,7 +6,7 @@
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Shared\Html as SharedHtml;
-// Suggested by issue #2427.
+// Suggested by issue 2427.
echo date('H:i:s'), ' Create new PhpWord object', EOL;
$phpWord = new PhpWord();
Settings::setDefaultRtl(true);
From 28b1b08ee77462e87b0b69d364f1688d0e0eacd7 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 4 Sep 2024 00:20:17 -0700
Subject: [PATCH 009/153] Update Sample_45_RTLTitles.php
---
samples/Sample_45_RTLTitles.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/samples/Sample_45_RTLTitles.php b/samples/Sample_45_RTLTitles.php
index 83dd9b9872..e39510c97f 100644
--- a/samples/Sample_45_RTLTitles.php
+++ b/samples/Sample_45_RTLTitles.php
@@ -6,7 +6,7 @@
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Shared\Html as SharedHtml;
-// Suggested by issue 2427.
+// Suggested by issue #2427.
echo date('H:i:s'), ' Create new PhpWord object', EOL;
$phpWord = new PhpWord();
Settings::setDefaultRtl(true);
From 32a73505a08fd5c2136d4c128046e0585c4be5ab Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 9 Jan 2025 14:50:15 -0800
Subject: [PATCH 010/153] Update HTMLTest.php
---
tests/PhpWordTests/Reader/Html/HTMLTest.php | 1 +
1 file changed, 1 insertion(+)
diff --git a/tests/PhpWordTests/Reader/Html/HTMLTest.php b/tests/PhpWordTests/Reader/Html/HTMLTest.php
index c0e150e495..9b16662c08 100644
--- a/tests/PhpWordTests/Reader/Html/HTMLTest.php
+++ b/tests/PhpWordTests/Reader/Html/HTMLTest.php
@@ -1,4 +1,5 @@
Date: Thu, 9 Jan 2025 15:29:38 -0800
Subject: [PATCH 011/153] Move One Test to Avoid Merge Conflict
---
tests/PhpWordTests/Reader/{Html => }/HTMLTest.php | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
rename tests/PhpWordTests/Reader/{Html => }/HTMLTest.php (92%)
diff --git a/tests/PhpWordTests/Reader/Html/HTMLTest.php b/tests/PhpWordTests/Reader/HTMLTest.php
similarity index 92%
rename from tests/PhpWordTests/Reader/Html/HTMLTest.php
rename to tests/PhpWordTests/Reader/HTMLTest.php
index 9b16662c08..7a35a06f78 100644
--- a/tests/PhpWordTests/Reader/Html/HTMLTest.php
+++ b/tests/PhpWordTests/Reader/HTMLTest.php
@@ -16,7 +16,7 @@
* @license http://www.gnu.org/licenses/lgpl.txt LGPL version 3
*/
-namespace PhpOffice\PhpWordTests\Reader\Html;
+namespace PhpOffice\PhpWordTests\Reader;
use Exception;
use PhpOffice\PhpWord\IOFactory;
@@ -35,7 +35,7 @@ class HTMLTest extends \PHPUnit\Framework\TestCase
*/
public function testLoad(): void
{
- $filename = 'tests/PhpWordTests/_files/documents/reader.html';
+ $filename = __DIR__ . '/../_files/documents/reader.html';
$phpWord = IOFactory::load($filename, 'HTML');
self::assertInstanceOf('PhpOffice\\PhpWord\\PhpWord', $phpWord);
}
From e9316c07c277036fb875b0f322e8da55533666ba Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 9 Jan 2025 15:48:25 -0800
Subject: [PATCH 012/153] Try to Correct Merge Conflicts in
Writer/HTML/FontTest
---
tests/PhpWordTests/Writer/HTML/FontTest.php | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/tests/PhpWordTests/Writer/HTML/FontTest.php b/tests/PhpWordTests/Writer/HTML/FontTest.php
index 08a8fca6a4..20ab8d9920 100644
--- a/tests/PhpWordTests/Writer/HTML/FontTest.php
+++ b/tests/PhpWordTests/Writer/HTML/FontTest.php
@@ -1,4 +1,5 @@
Date: Thu, 9 Jan 2025 16:03:07 -0800
Subject: [PATCH 013/153] Trying FontTest One More Time Before Giving Up
---
tests/PhpWordTests/Writer/HTML/FontTest.php | 42 ++++++++++-----------
1 file changed, 21 insertions(+), 21 deletions(-)
diff --git a/tests/PhpWordTests/Writer/HTML/FontTest.php b/tests/PhpWordTests/Writer/HTML/FontTest.php
index 20ab8d9920..6416a44d2f 100644
--- a/tests/PhpWordTests/Writer/HTML/FontTest.php
+++ b/tests/PhpWordTests/Writer/HTML/FontTest.php
@@ -87,27 +87,27 @@ public function testFontNames1(): void
$style = Helper::getTextContent($xpath, '/html/head/style');
$prg = preg_match('/^body[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('body {font-family: \'Courier New\'; font-size: 12pt;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style1 {font-family: \'Tahoma\'; font-size: 10pt; color: #1B2232; font-weight: bold;}', $matches[0]);
$prg = preg_match('/^[.]style2[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style2 {font-family: \'Arial\'; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style3 {font-family: \'hack attempt'}; display:none\'; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style4[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style4 {font-family: \'padmaa 1.1\'; font-size: 10pt; font-weight: bold;}', $matches[0]);
$prg = preg_match('/^[.]style5[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style5 {font-family: \'MingLiU-ExtB\'; font-size: 10pt; font-weight: bold;}', $matches[0]);
}
@@ -143,23 +143,23 @@ public function testFontNames2(): void
$style = Helper::getTextContent($xpath, '/html/head/style');
$prg = preg_match('/^body[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('body {font-family: \'Courier New\'; font-size: 12pt;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style1 {font-family: \'Tahoma\'; font-size: 10pt; color: #1B2232; font-weight: bold;}', $matches[0]);
- self::assertNotEmpty($matches);
$prg = preg_match('/^[.]style2[^\\r\\n]*/m', $style, $matches);
- self::assertSame(1, $prg);
+ self::assertNotEmpty($matches);
+ self::assertNotFalse($prg);
self::assertEquals('.style2 {font-family: \'Arial\', sans-serif; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style3 {font-family: \'DejaVu Sans Monospace\', monospace; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style4[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style4 {font-family: \'Arial\'; font-size: 10pt;}', $matches[0]);
}
@@ -195,23 +195,23 @@ public function testFontNames3(): void
$style = Helper::getTextContent($xpath, '/html/head/style');
$prg = preg_match('/^body[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('body {font-family: \'Courier New\', monospace; font-size: 12pt;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style1 {font-family: \'Tahoma\'; font-size: 10pt; color: #1B2232; font-weight: bold;}', $matches[0]);
$prg = preg_match('/^[.]style2[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style2 {font-family: \'Arial\', sans-serif; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style3 {font-family: \'DejaVu Sans Monospace\', monospace; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style4[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style4 {font-family: \'Arial\'; font-size: 10pt;}', $matches[0]);
}
@@ -242,19 +242,19 @@ public function testWhiteSpace(): void
self::assertEquals('body {font-family: \'Arial\'; font-size: 12pt; white-space: pre-wrap;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style1 {font-family: \'Courier New\'; font-size: 10pt; white-space: pre-wrap;}', $matches[0]);
$prg = preg_match('/^[.]style2[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style2 {font-family: \'Courier New\'; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style3 {font-family: \'Courier New\'; font-size: 10pt; white-space: normal;}', $matches[0]);
$prg = preg_match('/^[.]style4[^\\r\\n]*/m', $style, $matches);
self::assertNotEmpty($matches);
- self::assertSame(1, $prg);
+ self::assertNotFalse($prg);
self::assertEquals('.style4 {font-family: \'Courier New\'; font-size: 10pt;}', $matches[0]);
}
From 8e6726c24a8e6a5db7c3dea0f6f3b17eb1064b5a Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 9 Jan 2025 16:55:59 -0800
Subject: [PATCH 014/153] Try to Catch Up
---
composer.json | 2 +-
src/PhpWord/Shared/Html.php | 4 ++--
src/PhpWord/SimpleType/TextDirection.php | 1 +
tests/PhpWordTests/Reader/Html/CharsetTest.php | 1 +
tests/PhpWordTests/Reader/Word2007/StyleTableTest.php | 1 +
tests/PhpWordTests/SettingsRtlTest.php | 1 +
tests/PhpWordTests/Shared/Html2402Test.php | 1 +
tests/PhpWordTests/Shared/HtmlFullTest.php | 1 +
tests/PhpWordTests/Shared/HtmlHeadingsTest.php | 1 +
tests/PhpWordTests/Shared/HtmlRtlTest.php | 5 +++--
tests/PhpWordTests/Shared/HtmlTest.php | 8 ++++----
tests/PhpWordTests/TemplateProcessorSectionTest.php | 1 +
tests/PhpWordTests/Writer/RTF/RichTextTitleTest.php | 1 +
13 files changed, 19 insertions(+), 9 deletions(-)
diff --git a/composer.json b/composer.json
index efebe941e7..2d06f890b3 100644
--- a/composer.json
+++ b/composer.json
@@ -122,7 +122,7 @@
"phpmd/phpmd": "^2.13",
"phpstan/phpstan": "^0.12.88 || ^1.0.0",
"phpstan/phpstan-phpunit": "^1.0 || ^2.0",
- "phpunit/phpunit": ">=7.0",
+ "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0 || ^10.0",
"symfony/process": "^4.4 || ^5.0",
"tecnickcom/tcpdf": "^6.5"
},
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 7c2fcab0e6..81573cd83d 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -417,7 +417,7 @@ protected static function parseChildNodes($node, $element, $styles, $data): void
* @param AbstractContainer $element
* @param array &$styles
*
- * @return \PhpOffice\PhpWord\Element\PageBreak|\PhpOffice\PhpWord\Element\TextRun
+ * @return \PhpOffice\PhpWord\Element\PageBreak|TextRun
*/
protected static function parseParagraph($node, $element, &$styles)
{
@@ -590,7 +590,7 @@ protected static function parseRow($node, $element, &$styles)
* @param Table $element
* @param array &$styles
*
- * @return \PhpOffice\PhpWord\Element\Cell|\PhpOffice\PhpWord\Element\TextRun $element
+ * @return \PhpOffice\PhpWord\Element\Cell|TextRun $element
*/
protected static function parseCell($node, $element, &$styles)
{
diff --git a/src/PhpWord/SimpleType/TextDirection.php b/src/PhpWord/SimpleType/TextDirection.php
index 0797fa9294..ab1a045337 100644
--- a/src/PhpWord/SimpleType/TextDirection.php
+++ b/src/PhpWord/SimpleType/TextDirection.php
@@ -1,4 +1,5 @@
addSection([
- 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE,
+ 'orientation' => Style\Section::ORIENTATION_LANDSCAPE,
]);
// borders & backgrounds are here just for better visual comparison
@@ -548,7 +548,7 @@ public function testParseTableRowHeight(): void
{
$phpWord = new PhpWord();
$section = $phpWord->addSection([
- 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE,
+ 'orientation' => Style\Section::ORIENTATION_LANDSCAPE,
]);
$html = <<addSection([
- 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE,
+ 'orientation' => Style\Section::ORIENTATION_LANDSCAPE,
]);
// borders & backgrounds are here just for better visual comparison
@@ -670,7 +670,7 @@ public function testParseTableStyleAttributeInlineStyle(): void
{
$phpWord = new PhpWord();
$section = $phpWord->addSection([
- 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE,
+ 'orientation' => Style\Section::ORIENTATION_LANDSCAPE,
]);
$html = '
diff --git a/tests/PhpWordTests/TemplateProcessorSectionTest.php b/tests/PhpWordTests/TemplateProcessorSectionTest.php
index 0402d4fc66..a6e9d61163 100644
--- a/tests/PhpWordTests/TemplateProcessorSectionTest.php
+++ b/tests/PhpWordTests/TemplateProcessorSectionTest.php
@@ -1,4 +1,5 @@
Date: Sun, 12 Jan 2025 14:45:02 -0800
Subject: [PATCH 015/153] Experiment with Different Phpunit Xml Dists
---
.github/workflows/php.yml | 14 +++++++++++---
phpunit.7.8.xml.dist | 12 ++++++++++++
phpunit10.xml.dist | 20 ++++++++++++++++++++
3 files changed, 43 insertions(+), 3 deletions(-)
create mode 100644 phpunit.7.8.xml.dist
create mode 100644 phpunit10.xml.dist
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index 0e2e9ea96d..b403e57d8a 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -83,11 +83,19 @@ jobs:
- name: Composer Install
run: composer install --ansi --prefer-dist --no-interaction --no-progress
- - name: Run phpunit
- if: matrix.php != '7.3'
+ - name: Run phpunit 7.1 7.2
+ if: matrix.php == '7.1' || matrix.php == '7.2'
+ run: ./vendor/bin/phpunit -c phpunit.7.8.xml.dist --no-coverage
+
+ - name: Run phpunit 7.4 8.0
+ if: matrix.php == '7.4' || matrix.php == '8.0'
run: ./vendor/bin/phpunit -c phpunit.xml.dist --no-coverage
- - name: Run phpunit
+ - name: Run phpunit 8.1 8.2 8.3 8.4
+ if: matrix.php == '8.1' || matrix.php == '8.2' || matrix.php == '8.3' || matrix.php == '8.4'
+ run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
+
+ - name: Run phpunit 7.3
if: matrix.php == '7.3'
run: ./vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover build/clover.xml
diff --git a/phpunit.7.8.xml.dist b/phpunit.7.8.xml.dist
new file mode 100644
index 0000000000..a8327fb286
--- /dev/null
+++ b/phpunit.7.8.xml.dist
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+ ./tests/PhpWordTests
+
+
+
+
diff --git a/phpunit10.xml.dist b/phpunit10.xml.dist
new file mode 100644
index 0000000000..b97fbee3fb
--- /dev/null
+++ b/phpunit10.xml.dist
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+ ./tests/PhpWordTests
+
+
+
+
+
+ ./src
+
+
+ ./src/PhpWord/Shared/PCLZip
+
+
+
From 737cb3708a9be2bd0f13c42b15ca8d635baa2e7b Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 12 Jan 2025 14:53:38 -0800
Subject: [PATCH 016/153] Remove Logging from 7.8 Config
---
phpunit.7.8.xml.dist | 1 -
1 file changed, 1 deletion(-)
diff --git a/phpunit.7.8.xml.dist b/phpunit.7.8.xml.dist
index a8327fb286..22e77a0ea6 100644
--- a/phpunit.7.8.xml.dist
+++ b/phpunit.7.8.xml.dist
@@ -8,5 +8,4 @@
./tests/PhpWordTests
-
From 508464223d3b5dae4e9524fd2ab3768248b2b62f Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 12 Jan 2025 15:13:50 -0800
Subject: [PATCH 017/153] More Phpunit Tweaking
---
.github/workflows/php.yml | 4 ++--
phpunit.xml.dist | 22 +++++++++-------------
phpunit9.xml.dist | 24 ++++++++++++++++++++++++
3 files changed, 35 insertions(+), 15 deletions(-)
create mode 100644 phpunit9.xml.dist
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index b403e57d8a..016120d913 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -89,7 +89,7 @@ jobs:
- name: Run phpunit 7.4 8.0
if: matrix.php == '7.4' || matrix.php == '8.0'
- run: ./vendor/bin/phpunit -c phpunit.xml.dist --no-coverage
+ run: ./vendor/bin/phpunit -c phpunit9.xml.dist --no-coverage
- name: Run phpunit 8.1 8.2 8.3 8.4
if: matrix.php == '8.1' || matrix.php == '8.2' || matrix.php == '8.3' || matrix.php == '8.4'
@@ -97,7 +97,7 @@ jobs:
- name: Run phpunit 7.3
if: matrix.php == '7.3'
- run: ./vendor/bin/phpunit -c phpunit.xml.dist --coverage-clover build/clover.xml
+ run: ./vendor/bin/phpunit -c phpunit9.xml.dist --coverage-clover build/clover.xml
- name: Upload coverage results to Coveralls
if: matrix.php == '7.3'
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index ff0c676fad..b97fbee3fb 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -1,17 +1,5 @@
-
-
-
- ./src
-
-
- ./src/PhpWord/Shared/PCLZip
-
-
-
+
@@ -21,4 +9,12 @@
+
+
+ ./src
+
+
+ ./src/PhpWord/Shared/PCLZip
+
+
diff --git a/phpunit9.xml.dist b/phpunit9.xml.dist
new file mode 100644
index 0000000000..ff0c676fad
--- /dev/null
+++ b/phpunit9.xml.dist
@@ -0,0 +1,24 @@
+
+
+
+
+ ./src
+
+
+ ./src/PhpWord/Shared/PCLZip
+
+
+
+
+
+
+
+
+ ./tests/PhpWordTests
+
+
+
+
From d1a16fef6c785311810115213f68d4a04eaf4e3e Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 15 Jan 2025 15:43:15 -0800
Subject: [PATCH 018/153] Update HtmlTest.php
---
tests/PhpWordTests/Shared/HtmlTest.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index cf4d627c92..bc0899a8ac 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -28,9 +28,9 @@
use PhpOffice\PhpWord\Shared\Html;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
+use PhpOffice\PhpWord\SimpleType\TblWidth;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Style\Font;
-use PhpOffice\PhpWord\SimpleType\TblWidth;
use PhpOffice\PhpWord\Style\Paragraph;
use PhpOffice\PhpWordTests\AbstractWebServerEmbedded;
use PhpOffice\PhpWordTests\TestHelperDOCX;
From f82eb47f8f6327e8883a6d921ac3b73017d6b78b Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 30 Jan 2025 15:00:10 -0800
Subject: [PATCH 019/153] Eliminate JSON Dependency
JSON is used in exactly 2 places in the code and should not be used in either.
XMLWriter uses it to cast a float to string. Aside from being silly, this actually causes a problem in Shared/HtmlTest, where one of the results should be 4000 but is instead tested for 3999.9999... The error should have been immaterial (a simple cast will give the correct answer), but the test was wrong, insisting on an exact match for a floating point answer. It should use assertEqualsWithDelta. Next, the reason why JSON got it "wrong" was because the conversions in Shared/Converter use the wrong order of operations - multiplications should be performed before divisions to help avoid rounding problems, but Converter was doing the divisions first. All the conversions there are changed to multiply before dividing. Finally, Shared/Html checks for width units of points or pixels, but should check for inches and centimers as well.
Writer/Html outputs tracking properties as a JSON string for some unfathomable reason. It is changed to output each of the three (author, id, and date) as its own discrete property. Tests didn't actually confirm the value of the properties; they do now. Oh, yes, htmlspecialchars is needed for author and id, otherwise the html may wind up broken.
Some simple changes are made to TestHelperDOCX to avoid intermittent problems on Windows, and to make the code a little cleaner.
---
.gitignore | 3 +-
composer.json | 1 -
src/PhpWord/Shared/Converter.php | 24 +++++++--------
src/PhpWord/Shared/Html.php | 6 ++++
src/PhpWord/Shared/XMLWriter.php | 2 +-
src/PhpWord/Writer/HTML/Element/Text.php | 24 ++++++++-------
tests/PhpWordTests/Shared/HtmlTest.php | 7 +++--
tests/PhpWordTests/TestHelperDOCX.php | 24 +++++++++++----
.../PhpWordTests/Writer/HTML/ElementTest.php | 30 ++++++++++++++++++-
9 files changed, 86 insertions(+), 35 deletions(-)
diff --git a/.gitignore b/.gitignore
index 0b9d0608d0..3ebe0dc1a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,4 +24,5 @@ phpword.ini
/nbproject
/.php_cs.cache
/.phpunit.result.cache
-/public
\ No newline at end of file
+/.phpunit.cache
+/public
diff --git a/composer.json b/composer.json
index 2d06f890b3..9487640f2a 100644
--- a/composer.json
+++ b/composer.json
@@ -109,7 +109,6 @@
"php": "^7.1|^8.0",
"ext-dom": "*",
"ext-gd": "*",
- "ext-json": "*",
"ext-xml": "*",
"ext-zip": "*",
"phpoffice/math": "^0.2"
diff --git a/src/PhpWord/Shared/Converter.php b/src/PhpWord/Shared/Converter.php
index 17d2e1a05d..82e11c0790 100644
--- a/src/PhpWord/Shared/Converter.php
+++ b/src/PhpWord/Shared/Converter.php
@@ -40,7 +40,7 @@ class Converter
*/
public static function cmToTwip($centimeter = 1)
{
- return $centimeter / self::INCH_TO_CM * self::INCH_TO_TWIP;
+ return $centimeter * self::INCH_TO_TWIP / self::INCH_TO_CM;
}
/**
@@ -64,7 +64,7 @@ public static function cmToInch($centimeter = 1)
*/
public static function cmToPixel($centimeter = 1)
{
- return $centimeter / self::INCH_TO_CM * self::INCH_TO_PIXEL;
+ return $centimeter * self::INCH_TO_PIXEL / self::INCH_TO_CM;
}
/**
@@ -76,7 +76,7 @@ public static function cmToPixel($centimeter = 1)
*/
public static function cmToPoint($centimeter = 1)
{
- return $centimeter / self::INCH_TO_CM * self::INCH_TO_POINT;
+ return $centimeter * self::INCH_TO_POINT / self::INCH_TO_CM;
}
/**
@@ -88,7 +88,7 @@ public static function cmToPoint($centimeter = 1)
*/
public static function cmToEmu($centimeter = 1)
{
- return round($centimeter / self::INCH_TO_CM * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU);
+ return round($centimeter * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU / self::INCH_TO_CM);
}
/**
@@ -160,7 +160,7 @@ public static function inchToEmu($inch = 1)
*/
public static function pixelToTwip($pixel = 1)
{
- return $pixel / self::INCH_TO_PIXEL * self::INCH_TO_TWIP;
+ return $pixel * self::INCH_TO_TWIP / self::INCH_TO_PIXEL;
}
/**
@@ -172,7 +172,7 @@ public static function pixelToTwip($pixel = 1)
*/
public static function pixelToCm($pixel = 1)
{
- return $pixel / self::INCH_TO_PIXEL * self::INCH_TO_CM;
+ return $pixel * self::INCH_TO_CM / self::INCH_TO_PIXEL;
}
/**
@@ -184,7 +184,7 @@ public static function pixelToCm($pixel = 1)
*/
public static function pixelToPoint($pixel = 1)
{
- return $pixel / self::INCH_TO_PIXEL * self::INCH_TO_POINT;
+ return $pixel * self::INCH_TO_POINT / self::INCH_TO_PIXEL;
}
/**
@@ -208,7 +208,7 @@ public static function pixelToEmu($pixel = 1)
*/
public static function pointToTwip($point = 1)
{
- return $point / self::INCH_TO_POINT * self::INCH_TO_TWIP;
+ return $point * self::INCH_TO_TWIP / self::INCH_TO_POINT;
}
/**
@@ -220,7 +220,7 @@ public static function pointToTwip($point = 1)
*/
public static function pointToPixel($point = 1)
{
- return $point / self::INCH_TO_POINT * self::INCH_TO_PIXEL;
+ return $point * self::INCH_TO_PIXEL / self::INCH_TO_POINT;
}
/**
@@ -232,7 +232,7 @@ public static function pointToPixel($point = 1)
*/
public static function pointToEmu($point = 1)
{
- return round($point / self::INCH_TO_POINT * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU);
+ return round($point * self::INCH_TO_PIXEL * self::PIXEL_TO_EMU / self::INCH_TO_POINT);
}
/**
@@ -244,7 +244,7 @@ public static function pointToEmu($point = 1)
*/
public static function pointToCm($point = 1)
{
- return $point / self::INCH_TO_POINT * self::INCH_TO_CM;
+ return $point * self::INCH_TO_CM / self::INCH_TO_POINT;
}
/**
@@ -268,7 +268,7 @@ public static function emuToPixel($emu = 1)
*/
public static function picaToPoint($pica = 1)
{
- return $pica / self::INCH_TO_PICA * self::INCH_TO_POINT;
+ return $pica * self::INCH_TO_POINT / self::INCH_TO_PICA;
}
/**
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 9b57f77adc..d1bf13d7f7 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -1477,6 +1477,12 @@ protected static function convertHtmlSize(string $size): float
if (false !== strpos($size, 'px')) {
return (float) str_replace('px', '', $size);
}
+ if (false !== strpos($size, 'cm')) {
+ return Converter::cmToPixel((float) str_replace('cm', '', $size));
+ }
+ if (false !== strpos($size, 'in')) {
+ return Converter::inchToPixel((float) str_replace('in', '', $size));
+ }
return (float) $size;
}
diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php
index 8dc28e1184..8b267b694c 100644
--- a/src/PhpWord/Shared/XMLWriter.php
+++ b/src/PhpWord/Shared/XMLWriter.php
@@ -180,7 +180,7 @@ public function writeAttributeIf($condition, $attribute, $value): void
public function writeAttribute($name, $value)
{
if (is_float($value)) {
- $value = json_encode($value);
+ $value = (string) $value;
}
return parent::writeAttribute($name, $value ?? '');
diff --git a/src/PhpWord/Writer/HTML/Element/Text.php b/src/PhpWord/Writer/HTML/Element/Text.php
index 312d3a19c2..c6363d1da4 100644
--- a/src/PhpWord/Writer/HTML/Element/Text.php
+++ b/src/PhpWord/Writer/HTML/Element/Text.php
@@ -163,20 +163,22 @@ private function writeTrackChangeOpening()
$content = '';
if (($changed->getChangeType() == TrackChange::INSERTED)) {
- $content .= 'getChangeType() == TrackChange::DELETED) {
- $content .= ' ['author' => $changed->getAuthor(), 'id' => $this->element->getElementId()]];
- if ($changed->getDate() != null) {
- $changedProp['changed']['date'] = $changed->getDate()->format('Y-m-d\TH:i:s\Z');
+ $author = htmlspecialchars($changed->getAuthor(), ENT_QUOTES);
+ $content .= " data-phpword-chg-author='$author'";
+ $elementId = htmlspecialchars($this->element->getElementId(), ENT_QUOTES);
+ $content .= " data-phpword-chg-id='$elementId'";
+ $date = $changed->getDate();
+ if ($date !== null) {
+ $dateout = $date->format('Y-m-d\TH:i:s\Z');
+ $content .= " data-phpword-chg-timestamp='$dateout'";
}
- $content .= json_encode($changedProp);
- $content .= '\' ';
- $content .= 'title="' . $changed->getAuthor();
- if ($changed->getDate() != null) {
- $dateUser = $changed->getDate()->format('Y-m-d H:i:s');
+ $content .= ' title="' . $author;
+ if ($date !== null) {
+ $dateUser = $date->format('Y-m-d H:i:s');
$content .= ' - ' . $dateUser;
}
$content .= '">';
diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index 142a93e5a5..5131d6ea44 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -256,7 +256,8 @@ public function testParseWidth(string $htmlSize, float $docxSize, string $docxUn
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
$xpath = '/w:document/w:body/w:tbl/w:tblPr/w:tblW';
self::assertTrue($doc->elementExists($xpath));
- self::assertEquals($docxSize, $doc->getElement($xpath)->getAttribute('w:w'));
+ $actual = (float) $doc->getElement($xpath)->getAttribute('w:w');
+ self::assertEqualsWithDelta($docxSize, $actual, 1.0e-12);
self::assertEquals($docxUnit, $doc->getElement($xpath)->getAttribute('w:type'));
}
@@ -1371,9 +1372,11 @@ public static function providerParseWidth(): array
return [
['auto', 5000, TblWidth::PERCENT],
['100%', 5000, TblWidth::PERCENT],
- ['200pt', 3999.999999999999, TblWidth::TWIP],
+ ['200pt', 4000, TblWidth::TWIP],
['300px', 4500, TblWidth::TWIP],
['400', 6000, TblWidth::TWIP],
+ ['2in', 2880, TblWidth::TWIP],
+ ['2.54cm', 1440, TblWidth::TWIP],
];
}
diff --git a/tests/PhpWordTests/TestHelperDOCX.php b/tests/PhpWordTests/TestHelperDOCX.php
index 2a6fbabae0..5ebe799cfc 100644
--- a/tests/PhpWordTests/TestHelperDOCX.php
+++ b/tests/PhpWordTests/TestHelperDOCX.php
@@ -47,13 +47,14 @@ class TestHelperDOCX
*/
public static function getDocument(PhpWord $phpWord, $writerName = 'Word2007')
{
+ $tempdir = self::getTempDirPhpunit();
self::$file = tempnam(Settings::getTempDir(), 'PhpWord');
if (false === self::$file) {
throw new CreateTemporaryFileException();
}
- if (!is_dir(Settings::getTempDir() . '/PhpWord_Unit_Test/')) {
- mkdir(Settings::getTempDir() . '/PhpWord_Unit_Test/');
+ if (!is_dir($tempdir)) {
+ mkdir($tempdir);
}
$xmlWriter = IOFactory::createWriter($phpWord, $writerName);
@@ -62,11 +63,11 @@ public static function getDocument(PhpWord $phpWord, $writerName = 'Word2007')
$zip = new ZipArchive();
$res = $zip->open(self::$file);
if (true === $res) {
- $zip->extractTo(Settings::getTempDir() . '/PhpWord_Unit_Test/');
+ $zip->extractTo($tempdir);
$zip->close();
}
- $doc = new XmlDocument(Settings::getTempDir() . '/PhpWord_Unit_Test/');
+ $doc = new XmlDocument($tempdir);
if ($writerName === 'ODText') {
$doc->setDefaultFile('content.xml');
}
@@ -83,8 +84,9 @@ public static function clear(): void
unlink(self::$file);
self::$file = '';
}
- if (is_dir(Settings::getTempDir() . '/PhpWord_Unit_Test/')) {
- self::deleteDir(Settings::getTempDir() . '/PhpWord_Unit_Test/');
+ $tempdir = self::getTempDirPhpunit();
+ if (is_dir($tempdir)) {
+ self::deleteDir($tempdir);
}
}
@@ -117,4 +119,14 @@ public static function getFile()
{
return self::$file;
}
+
+ /**
+ * Get temporary directory for PhpUnit.
+ *
+ * @return string
+ */
+ private static function getTempDirPhpunit()
+ {
+ return Settings::getTempDir() . '/PhpWord_Unit_Test';
+ }
}
diff --git a/tests/PhpWordTests/Writer/HTML/ElementTest.php b/tests/PhpWordTests/Writer/HTML/ElementTest.php
index 3b2580381f..28e785a237 100644
--- a/tests/PhpWordTests/Writer/HTML/ElementTest.php
+++ b/tests/PhpWordTests/Writer/HTML/ElementTest.php
@@ -72,13 +72,41 @@ public function testWriteTrackChanges(): void
$text = $section->addText('my dummy text');
$text->setChangeInfo(TrackChange::INSERTED, 'author name');
$text2 = $section->addText('my other text');
- $text2->setTrackChange(new TrackChange(TrackChange::DELETED, 'another author', new DateTime()));
+ $deleteTime = new DateTime();
+ $deleteAuthor = "Spec O'char";
+ $deleteTrack = new TrackChange(TrackChange::DELETED, $deleteAuthor, $deleteTime);
+ $text2->setTrackChange($deleteTrack);
$dom = Helper::getAsHTML($phpWord);
$xpath = new DOMXPath($dom);
self::assertEquals(1, $xpath->query('/html/body/div/p[1]/ins')->length);
self::assertEquals(1, $xpath->query('/html/body/div/p[2]/del')->length);
+ $node = $xpath->query('/html/body/div/p[2]/del');
+ self::assertNotFalse($node);
+ $allAttributes = $node[0]->attributes;
+ self::assertCount(4, $allAttributes);
+ $node = $xpath->query('/html/body/div/p[2]/del');
+ self::assertNotFalse($node);
+
+ $attributes = $node[0]->attributes[0];
+ self::assertSame('data-phpword-chg-author', $attributes->name);
+ self::assertSame($deleteAuthor, $attributes->value);
+
+ $text2Id = $text2->getElementId();
+ $attributes = $node[0]->attributes[1];
+ self::assertSame('data-phpword-chg-id', $attributes->name);
+ self::assertSame($text2Id, $attributes->value);
+
+ $attributes = $node[0]->attributes[2];
+ self::assertSame('data-phpword-chg-timestamp', $attributes->name);
+ self::assertSame($deleteTime->format('Y-m-d\TH:i:s\Z'), $attributes->value);
+
+ $attributes = $node[0]->attributes[3];
+ self::assertSame('title', $attributes->name);
+ $expected = $deleteAuthor . ' - '
+ . $deleteTime->format('Y-m-d H:i:s');
+ self::assertSame($expected, $attributes->value);
}
/**
From 3ace7706180af59c7b10b3b0bab5a8615849c92a Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 30 Jan 2025 21:20:49 -0800
Subject: [PATCH 020/153] Improve Test Coverage
---
src/PhpWord/Shared/Html.php | 10 +-
src/PhpWord/Shared/HtmlColours.php | 5 -
tests/PhpWordTests/Shared/Html2Test.php | 140 ++++++++++++++++++++++++
3 files changed, 145 insertions(+), 10 deletions(-)
create mode 100644 tests/PhpWordTests/Shared/Html2Test.php
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index d1bf13d7f7..26bc83e3bb 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -36,6 +36,7 @@
use PhpOffice\PhpWord\SimpleType\NumberFormat;
use PhpOffice\PhpWord\SimpleType\TextDirection;
use PhpOffice\PhpWord\Style\Paragraph;
+use Throwable;
/**
* Common Html functions.
@@ -114,20 +115,20 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
$dom->preserveWhiteSpace = $preserveWhiteSpace;
try {
- $result = $dom->loadHTML($html, LIBXML_NOWARNING | LIBXML_NOERROR);
+ $result = @$dom->loadHTML($html);
$exceptionMessage = 'DOM loadHTML failed';
- } catch (Exception $e) {
+ } catch (Throwable $e) {
$result = false;
$exceptionMessage = $e->getMessage();
}
if ($result === false) {
- throw new Exception($exceptionMessage); // @codeCoverageIgnore
+ throw new Exception($exceptionMessage);
}
self::removeAnnoyingWhitespaceTextNodes($dom);
static::$xpath = new DOMXPath($dom);
$node = $dom->getElementsByTagName('html');
if (count($node) === 0 || $node->item(0) === null) {
- $node = $dom->getElementsByTagName('body');
+ $node = $dom->getElementsByTagName('body'); // @codeCoverageIgnore
}
static::parseNode($node->item(0), $element);
@@ -1319,7 +1320,6 @@ protected static function mapListType($cssListType)
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, ..
}
diff --git a/src/PhpWord/Shared/HtmlColours.php b/src/PhpWord/Shared/HtmlColours.php
index 40bc0096c6..c50acfd5da 100644
--- a/src/PhpWord/Shared/HtmlColours.php
+++ b/src/PhpWord/Shared/HtmlColours.php
@@ -524,11 +524,6 @@ class HtmlColours
'yellowgreen' => '9acd32',
];
- public static function colourNameLookup(string $colorName): string
- {
- return self::COLOUR_MAP[$colorName] ?? '';
- }
-
public static function convertColour(string $colorName): string
{
$colorName = trim($colorName);
diff --git a/tests/PhpWordTests/Shared/Html2Test.php b/tests/PhpWordTests/Shared/Html2Test.php
new file mode 100644
index 0000000000..95866d4b07
--- /dev/null
+++ b/tests/PhpWordTests/Shared/Html2Test.php
@@ -0,0 +1,140 @@
+expectException(Exception::class);
+ $this->expectExceptionMessage('loadHTML');
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, '');
+ }
+
+ public function testCssOnIdElement(): void
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ $html = ''
+ . ''
+ . 'Id Test'
+ . ''
+ . ''
+ . 'test1.
'
+ . '';
+ Html::addHtml($section, $html);
+ $doc = TestHelperDOCX::getDocument($phpWord);
+ $marginPath = '/w:document/w:body/w:p/w:pPr/w:spacing';
+ self::assertSame('150', $doc->getElement($marginPath)->getAttribute('w:before'));
+ self::assertSame('150', $doc->getElement($marginPath)->getAttribute('w:after'));
+ $path = '/w:document/w:body/w:p/w:r';
+ self::assertSame('test1.', $doc->getElement($path)->nodeValue);
+ $boldPath = $path . '/w:rPr/w:b';
+ self::assertSame('1', $doc->getElement($boldPath)->getAttribute('w:val'));
+ }
+
+ public function testListTypes(): void
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ $html = '- Decimal number first
- second
'
+ . '- Lowercase first
- second
'
+ . '- Uppercase first
- second
'
+ . '- Lower roman first
- second
'
+ . '- Upper roman first
- second
';
+ Html::addHtml($section, $html);
+ $doc = TestHelperDOCX::getDocument($phpWord);
+
+ $item = 1;
+ $expected = '1';
+ $path = "/w:document/w:body/w:p[$item]";
+ self::assertSame('Decimal number first', $doc->getElement("$path/w:r")->nodeValue);
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+ ++$item;
+ $path = "/w:document/w:body/w:p[$item]";
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+
+ ++$item;
+ $expected = '2';
+ $path = "/w:document/w:body/w:p[$item]";
+ self::assertSame('Lowercase first', $doc->getElement("$path/w:r")->nodeValue);
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+ ++$item;
+ $path = "/w:document/w:body/w:p[$item]";
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+
+ ++$item;
+ $expected = '3';
+ $path = "/w:document/w:body/w:p[$item]";
+ self::assertSame('Uppercase first', $doc->getElement("$path/w:r")->nodeValue);
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+ ++$item;
+ $path = "/w:document/w:body/w:p[$item]";
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+
+ ++$item;
+ $expected = '4';
+ $path = "/w:document/w:body/w:p[$item]";
+ self::assertSame('Lower roman first', $doc->getElement("$path/w:r")->nodeValue);
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+ ++$item;
+ $path = "/w:document/w:body/w:p[$item]";
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+
+ ++$item;
+ $expected = '5';
+ $path = "/w:document/w:body/w:p[$item]";
+ self::assertSame('Upper roman first', $doc->getElement("$path/w:r")->nodeValue);
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+ ++$item;
+ $path = "/w:document/w:body/w:p[$item]";
+ $numIdPath = $path . '/w:pPr/w:numPr/w:numId';
+ self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
+ }
+}
From b1720b95ddac49ee56d9a2b7cde3a736ced484a9 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 30 Jan 2025 22:18:41 -0800
Subject: [PATCH 021/153] A Bit More Coverage
---
tests/PhpWordTests/Shared/Html2Test.php | 64 +++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/tests/PhpWordTests/Shared/Html2Test.php b/tests/PhpWordTests/Shared/Html2Test.php
index 95866d4b07..64a71932dd 100644
--- a/tests/PhpWordTests/Shared/Html2Test.php
+++ b/tests/PhpWordTests/Shared/Html2Test.php
@@ -137,4 +137,68 @@ public function testListTypes(): void
$numIdPath = $path . '/w:pPr/w:numPr/w:numId';
self::assertSame($expected, $doc->getElement($numIdPath)->getAttribute('w:val'));
}
+
+ public function testPadding(): void
+ {
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ $html = ''
+ . ''
+ . '| 20 | '
+ . '20 30 | '
+ . '
'
+ . '| 20 30 40 | '
+ . '20 30 40 50 | '
+ . '
';
+ Html::addHtml($section, $html);
+ $doc = TestHelperDOCX::getDocument($phpWord);
+
+ $item = 1;
+ $td = 1;
+ $path = "/w:document/w:body/w:tbl/w:tr[$item]/w:tc[$td]";
+ self::assertSame('20', $doc->getElement("$path/w:p/w:r")->nodeValue);
+ $tcMarPath = $path . '/w:tcPr/w:tcMar';
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:top')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:start')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:bottom')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:end')->getAttribute('w:w'));
+
+ ++$td;
+ $path = "/w:document/w:body/w:tbl/w:tr[$item]/w:tc[$td]";
+ self::assertSame('20 30', $doc->getElement("$path/w:p/w:r")->nodeValue);
+ $tcMarPath = $path . '/w:tcPr/w:tcMar';
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:top')->getAttribute('w:w'));
+ self::assertSame('450', $doc->getElement($tcMarPath . '/w:start')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:bottom')->getAttribute('w:w'));
+ self::assertSame('450', $doc->getElement($tcMarPath . '/w:end')->getAttribute('w:w'));
+
+ $item = 1;
+ $td = 1;
+ $path = "/w:document/w:body/w:tbl/w:tr[$item]/w:tc[$td]";
+ self::assertSame('20', $doc->getElement("$path/w:p/w:r")->nodeValue);
+ $tcMarPath = $path . '/w:tcPr/w:tcMar';
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:top')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:start')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:bottom')->getAttribute('w:w'));
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:end')->getAttribute('w:w'));
+
+ ++$item;
+ $td = 1;
+ $path = "/w:document/w:body/w:tbl/w:tr[$item]/w:tc[$td]";
+ self::assertSame('20 30 40', $doc->getElement("$path/w:p/w:r")->nodeValue);
+ $tcMarPath = $path . '/w:tcPr/w:tcMar';
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:top')->getAttribute('w:w'));
+ self::assertSame('450', $doc->getElement($tcMarPath . '/w:start')->getAttribute('w:w'));
+ self::assertSame('600', $doc->getElement($tcMarPath . '/w:bottom')->getAttribute('w:w'));
+ self::assertSame('450', $doc->getElement($tcMarPath . '/w:end')->getAttribute('w:w'));
+
+ ++$td;
+ $path = "/w:document/w:body/w:tbl/w:tr[$item]/w:tc[$td]";
+ self::assertSame('20 30 40 50', $doc->getElement("$path/w:p/w:r")->nodeValue);
+ $tcMarPath = $path . '/w:tcPr/w:tcMar';
+ self::assertSame('300', $doc->getElement($tcMarPath . '/w:top')->getAttribute('w:w'));
+ self::assertSame('750', $doc->getElement($tcMarPath . '/w:start')->getAttribute('w:w'));
+ self::assertSame('600', $doc->getElement($tcMarPath . '/w:bottom')->getAttribute('w:w'));
+ self::assertSame('450', $doc->getElement($tcMarPath . '/w:end')->getAttribute('w:w'));
+ }
}
From 16904a2a0cea68176db99dbcc60c52e804b8212a Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 5 Feb 2025 22:24:41 -0800
Subject: [PATCH 022/153] Catch Up
---
src/PhpWord/Shared/Html.php | 8 ++++----
src/PhpWord/Writer/HTML/Element/Title.php | 15 +++++++++------
src/PhpWord/Writer/HTML/Part/Head.php | 1 +
tests/PhpWordTests/Shared/HtmlHeadingsTest.php | 9 ++++-----
tests/PhpWordTests/Shared/HtmlTest.php | 5 +++++
tests/PhpWordTests/Writer/HTML/PartTest.php | 12 +++++-------
6 files changed, 28 insertions(+), 22 deletions(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index c574bd969d..9cc3abe5b7 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -470,10 +470,10 @@ protected static function parseInput($node, $element, &$styles): void
*/
protected static function parseHeading(DOMNode $node, AbstractContainer $element, array &$styles, string $headingStyle): TextRun
{
- self::parseInlineStyle($node, $styles['font']);
- // Create a TextRun to hold styles and text
- $styles['paragraph'] = $headingStyle;
- $textRun = new TextRun($styles['paragraph']);
+ $style = new Paragraph();
+ $style->setStyleName($headingStyle);
+ $style->setStyleByArray(self::parseInlineStyle($node, $styles['paragraph']));
+ $textRun = new TextRun($style);
// Create a title with level corresponding to number in heading style
// (Eg, Heading1 = 1)
diff --git a/src/PhpWord/Writer/HTML/Element/Title.php b/src/PhpWord/Writer/HTML/Element/Title.php
index 1348a30a75..e759bfdcd6 100644
--- a/src/PhpWord/Writer/HTML/Element/Title.php
+++ b/src/PhpWord/Writer/HTML/Element/Title.php
@@ -19,9 +19,8 @@
namespace PhpOffice\PhpWord\Writer\HTML\Element;
use PhpOffice\PhpWord\Element\Title as PhpWordTitle;
-use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Writer\HTML;
-use PhpOffice\PhpWord\Writer\HTML\Style\Font;
+use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph;
/**
* TextRun element HTML writer.
@@ -44,17 +43,21 @@ public function write()
$tag = 'h' . $this->element->getDepth();
$text = $this->element->getText();
+ $paragraphStyle = null;
if (is_string($text)) {
$text = $this->parentWriter->escapeHTML($text);
} else {
+ $paragraphStyle = $text->getParagraphStyle();
$writer = new Container($this->parentWriter, $text);
$text = $writer->write();
}
$css = '';
- $style = Style::getStyle('Heading_' . $this->element->getDepth());
- if ($style !== null) {
- $styleWriter = new Font($style);
- $css = ' style="' . $styleWriter->write() . '"';
+ if (is_object($paragraphStyle)) {
+ $styleWriter = new Paragraph($paragraphStyle);
+ $write = $styleWriter->write();
+ if ($write !== '') {
+ $css = " style=\"$write\"";
+ }
}
$content = "<{$tag}{$css}>{$text}{$tag}>" . PHP_EOL;
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
index 0f295e58c3..f8af52ccb1 100644
--- a/src/PhpWord/Writer/HTML/Part/Head.php
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -137,6 +137,7 @@ private function writeStyles(): string
$styleWriter = new FontStyleWriter($style);
if ($style->getStyleType() == 'title') {
$name = str_replace('Heading_', 'h', $name);
+ $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
$styleParagraph = $style->getParagraph();
$style = $styleParagraph;
} else {
diff --git a/tests/PhpWordTests/Shared/HtmlHeadingsTest.php b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
index 2f11386d04..8ecc95b773 100644
--- a/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
+++ b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
@@ -40,7 +40,7 @@ public function testRoundTripHeadings(): void
$section = $originalDoc->addSection();
$expectedStrings = [];
$section->addTitle('Title 1', 1);
- $expectedStrings[] = 'Title 1
';
+ $expectedStrings[] = 'Title 1
';
for ($i = 2; $i <= 6; ++$i) {
$textRun = new TextRun();
$textRun->addText('Title ');
@@ -59,9 +59,8 @@ public function testRoundTripHeadings(): void
SharedHtml::addHtml($newSection, $content, true);
$newWriter = new HtmlWriter($newDoc);
$newContent = $newWriter->getContent();
- // Reader transforms Text to TextRun,
- // but result is functionally the same.
- $firstStringAsTextRun = 'Title 1
';
- self::assertSame($content, str_replace($firstStringAsTextRun, $expectedStrings[0], $newContent));
+
+ // This needs work
+ self::assertSame($newContent, str_replace('h1 {font-size: 20pt;}' . PHP_EOL, '', $content));
}
}
diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index d50a5176d7..50f1e699a6 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -24,6 +24,7 @@
use PhpOffice\PhpWord\Element\Table;
use PhpOffice\PhpWord\Element\Text;
use PhpOffice\PhpWord\Element\TextRun;
+use PhpOffice\PhpWord\Element\Title;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\Shared\Converter;
@@ -134,6 +135,8 @@ public function testParseHeader(): void
self::assertCount(1, $section->getElements());
$element = $section->getElement(0);
+ self::assertInstanceOf(Title::class, $element);
+ $element = $element->getText();
self::assertInstanceOf(TextRun::class, $element);
self::assertInstanceOf(Paragraph::class, $element->getParagraphStyle());
self::assertEquals('Heading1', $element->getParagraphStyle()->getStyleName());
@@ -155,6 +158,8 @@ public function testParseHeaderStyle(): void
self::assertCount(1, $section->getElements());
$element = $section->getElement(0);
+ self::assertInstanceOf(Title::class, $element);
+ $element = $element->getText();
self::assertInstanceOf(TextRun::class, $element);
self::assertInstanceOf(Paragraph::class, $element->getParagraphStyle());
self::assertEquals('Heading1', $element->getParagraphStyle()->getStyleName());
diff --git a/tests/PhpWordTests/Writer/HTML/PartTest.php b/tests/PhpWordTests/Writer/HTML/PartTest.php
index a9d91b9530..2ccb96a799 100644
--- a/tests/PhpWordTests/Writer/HTML/PartTest.php
+++ b/tests/PhpWordTests/Writer/HTML/PartTest.php
@@ -179,17 +179,15 @@ public function testTitleStyles(): void
$xpath = new DOMXPath($dom);
$style = Helper::getTextContent($xpath, '/html/head/style');
- //self::assertNotFalse(strpos($style, 'h1 {font-family: \'Calibri\'; font-weight: bold;}'));
+ self::assertNotFalse(strpos($style, 'h1 {font-family: \'Calibri\'; font-weight: bold;}'));
self::assertNotFalse(strpos($style, 'h1 {margin-top: 0.5pt; margin-bottom: 0.5pt;}'));
- //self::assertNotFalse(strpos($style, 'h2 {font-family: \'Times New Roman\'; font-style: italic;}'));
+ self::assertNotFalse(strpos($style, 'h2 {font-family: \'Times New Roman\'; font-style: italic;}'));
self::assertNotFalse(strpos($style, 'h2 {margin-top: 0.25pt; margin-bottom: 0.25pt;}'));
self::assertEquals(1, Helper::getLength($xpath, '/html/body/div/h1'));
self::assertEquals(2, Helper::getLength($xpath, '/html/body/div/h2'));
- // code for getNamedItem had been erroneous
- self::assertSame("font-family: 'Calibri'; font-weight: bold;", Helper::getNamedItem($xpath, '/html/body/div/h1', 'style')->textContent);
$html = Helper::getHtmlString($phpWord);
- self::assertStringContainsString('Header 1 #1
', $html);
- self::assertStringContainsString('Header 2 #1
', $html);
- self::assertStringContainsString('Header 2 #2
', $html);
+ self::assertStringContainsString('Header 1 #1
', $html);
+ self::assertStringContainsString('Header 2 #1
', $html);
+ self::assertStringContainsString('Header 2 #2
', $html);
}
}
From 92a4c5e1e45eb11ce5880671fd9a508a4bb2685f Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 6 Feb 2025 00:25:14 -0800
Subject: [PATCH 023/153] Html Writer Duplicate Header Styles in Style Tags
Nominally redundant, but makes things easier for Html Reader.
---
src/PhpWord/Writer/HTML/Element/Title.php | 19 +++++++++++++++----
.../PhpWordTests/Shared/HtmlHeadingsTest.php | 16 +++++++++++++---
tests/PhpWordTests/Writer/HTML/PartTest.php | 6 +++---
3 files changed, 31 insertions(+), 10 deletions(-)
diff --git a/src/PhpWord/Writer/HTML/Element/Title.php b/src/PhpWord/Writer/HTML/Element/Title.php
index e759bfdcd6..43806a648c 100644
--- a/src/PhpWord/Writer/HTML/Element/Title.php
+++ b/src/PhpWord/Writer/HTML/Element/Title.php
@@ -19,7 +19,9 @@
namespace PhpOffice\PhpWord\Writer\HTML\Element;
use PhpOffice\PhpWord\Element\Title as PhpWordTitle;
+use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Writer\HTML;
+use PhpOffice\PhpWord\Writer\HTML\Style\Font;
use PhpOffice\PhpWord\Writer\HTML\Style\Paragraph;
/**
@@ -51,14 +53,23 @@ public function write()
$writer = new Container($this->parentWriter, $text);
$text = $writer->write();
}
- $css = '';
+ $write1 = $write2 = $write3 = '';
+ $style = Style::getStyle('Heading_' . $this->element->getDepth());
+ if ($style !== null) {
+ $styleWriter = new Font($style);
+ $write1 = $styleWriter->write();
+ }
if (is_object($paragraphStyle)) {
$styleWriter = new Paragraph($paragraphStyle);
- $write = $styleWriter->write();
- if ($write !== '') {
- $css = " style=\"$write\"";
+ $write3 = $styleWriter->write();
+ if ($write1 !== '' && $write3 !== '') {
+ $write2 = ' ';
}
}
+ $css = "$write1$write2$write3";
+ if ($css !== '') {
+ $css = " style=\"$css\"";
+ }
$content = "<{$tag}{$css}>{$text}{$tag}>" . PHP_EOL;
diff --git a/tests/PhpWordTests/Shared/HtmlHeadingsTest.php b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
index 8ecc95b773..331935fbae 100644
--- a/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
+++ b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
@@ -40,7 +40,7 @@ public function testRoundTripHeadings(): void
$section = $originalDoc->addSection();
$expectedStrings = [];
$section->addTitle('Title 1', 1);
- $expectedStrings[] = 'Title 1
';
+ $expectedStrings[] = 'Title 1
';
for ($i = 2; $i <= 6; ++$i) {
$textRun = new TextRun();
$textRun->addText('Title ');
@@ -59,8 +59,18 @@ public function testRoundTripHeadings(): void
SharedHtml::addHtml($newSection, $content, true);
$newWriter = new HtmlWriter($newDoc);
$newContent = $newWriter->getContent();
+ // Reader does not yet support h1 declaration in css.
+ $content = str_replace('h1 {font-size: 20pt;}' . PHP_EOL, '', $content);
- // This needs work
- self::assertSame($newContent, str_replace('h1 {font-size: 20pt;}' . PHP_EOL, '', $content));
+ // Reader transforms Text to TextRun,
+ // but result is functionally the same.
+ self::assertSame(
+ $newContent,
+ str_replace(
+ 'Title 1
',
+ 'Title 1
',
+ $content
+ )
+ );
}
}
diff --git a/tests/PhpWordTests/Writer/HTML/PartTest.php b/tests/PhpWordTests/Writer/HTML/PartTest.php
index 2ccb96a799..0fe43c2350 100644
--- a/tests/PhpWordTests/Writer/HTML/PartTest.php
+++ b/tests/PhpWordTests/Writer/HTML/PartTest.php
@@ -186,8 +186,8 @@ public function testTitleStyles(): void
self::assertEquals(1, Helper::getLength($xpath, '/html/body/div/h1'));
self::assertEquals(2, Helper::getLength($xpath, '/html/body/div/h2'));
$html = Helper::getHtmlString($phpWord);
- self::assertStringContainsString('Header 1 #1
', $html);
- self::assertStringContainsString('Header 2 #1
', $html);
- self::assertStringContainsString('Header 2 #2
', $html);
+ self::assertStringContainsString('Header 1 #1
', $html);
+ self::assertStringContainsString('Header 2 #1
', $html);
+ self::assertStringContainsString('Header 2 #2
', $html);
}
}
From 60dbd8cd26e1abcb966c5977a155dc6ff591e62f Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 12 Feb 2025 13:11:27 -0800
Subject: [PATCH 024/153] Keep Up With 2533
---
src/PhpWord/Writer/HTML/Element/Title.php | 4 ++--
src/PhpWord/Writer/HTML/Part/Head.php | 1 -
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/PhpWord/Writer/HTML/Element/Title.php b/src/PhpWord/Writer/HTML/Element/Title.php
index 43806a648c..f3aeb395f4 100644
--- a/src/PhpWord/Writer/HTML/Element/Title.php
+++ b/src/PhpWord/Writer/HTML/Element/Title.php
@@ -18,7 +18,7 @@
namespace PhpOffice\PhpWord\Writer\HTML\Element;
-use PhpOffice\PhpWord\Element\Title as PhpWordTitle;
+use PhpOffice\PhpWord\Element\Title as ElementTitle;
use PhpOffice\PhpWord\Style;
use PhpOffice\PhpWord\Writer\HTML;
use PhpOffice\PhpWord\Writer\HTML\Style\Font;
@@ -38,7 +38,7 @@ class Title extends AbstractElement
*/
public function write()
{
- if (!$this->element instanceof PhpWordTitle) {
+ if (!$this->element instanceof ElementTitle) {
return '';
}
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
index f8af52ccb1..4da526075d 100644
--- a/src/PhpWord/Writer/HTML/Part/Head.php
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -101,7 +101,6 @@ private function writeStyles(): string
foreach ([
'body' => $bodyarray,
- //'*' => $astarray,
'a.NoteRef' => [
'text-decoration' => 'none',
],
From efd5701adc6a81ee7d4327f80596ae73364e5fa2 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Mon, 14 Apr 2025 10:01:06 -0700
Subject: [PATCH 025/153] Broken Test (not mine)
---
.../Writer/Word2007/Element/TOCTest.php | 5 ++++-
.../_files/html/loripsum.net.html | 19 +++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
create mode 100644 tests/PhpWordTests/_files/html/loripsum.net.html
diff --git a/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php b/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php
index 95e79114aa..c7054c195a 100644
--- a/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php
+++ b/tests/PhpWordTests/Writer/Word2007/Element/TOCTest.php
@@ -66,7 +66,10 @@ public function testWriteTitleWithoutpageNumber(): void
//more than one title and random text for create more than one page
for ($i = 1; $i <= 10; ++$i) {
$section->addTitle('Title ' . $i, 1);
- $content = file_get_contents('https://loripsum.net/api/10/long');
+ // This server seems to have disappeared.
+ //$content = file_get_contents('https://loripsum.net/api/10/long');
+ // Contents recovered using wayback machine.
+ $content = file_get_contents('tests/PhpWordTests/_files/html/loripsum.net.html');
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $content ? $content : '', false, false);
$section->addPageBreak();
}
diff --git a/tests/PhpWordTests/_files/html/loripsum.net.html b/tests/PhpWordTests/_files/html/loripsum.net.html
new file mode 100644
index 0000000000..7cf1357e71
--- /dev/null
+++ b/tests/PhpWordTests/_files/html/loripsum.net.html
@@ -0,0 +1,19 @@
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cupiditates non Epicuri divisione finiebat, sed sua satietate. Hic, qui utrumque probat, ambobus debuit uti, sicut facit re, neque tamen dividit verbis. Quibus natura iure responderit non esse verum aliunde finem beate vivendi, a se principia rei gerendae peti; Quibus rebus vita consentiens virtutibusque respondens recta et honesta et constans et naturae congruens existimari potest. Illa videamus, quae a te de amicitia dicta sunt. Addo etiam illud, multa iam mihi dare signa puerum et pudoris et ingenii, sed aetatem vides. Quibus rebus vita consentiens virtutibusque respondens recta et honesta et constans et naturae congruens existimari potest. Duo Reges: constructio interrete. Quid, cum volumus nomina eorum, qui quid gesserint, nota nobis esse, parentes, patriam, multa praeterea minime necessaria? Apparet statim, quae sint officia, quae actiones.
+
+Nam ante Aristippus, et ille melius. Quamquam id quidem licebit iis existimare, qui legerint. Inde igitur, inquit, ordiendum est. Quo modo autem optimum, si bonum praeterea nullum est? Non dolere, inquam, istud quam vim habeat postea videro; Perfecto enim et concluso neque virtutibus neque amicitiis usquam locum esse, si ad voluptatem omnia referantur, nihil praeterea est magnopere dicendum. Maximas vero virtutes iacere omnis necesse est voluptate dominante. Tertium autem omnibus aut maximis rebus iis, quae secundum naturam sint, fruentem vivere.
+
+Huic ego, si negaret quicquam interesse ad beate vivendum quali uteretur victu, concederem, laudarem etiam; Maximas vero virtutes iacere omnis necesse est voluptate dominante. Polemoni et iam ante Aristoteli ea prima visa sunt, quae paulo ante dixi. Atqui haec patefactio quasi rerum opertarum, cum quid quidque sit aperitur, definitio est. Num igitur utiliorem tibi hunc Triarium putas esse posse, quam si tua sint Puteolis granaria?
+
+Unum est sine dolore esse, alterum cum voluptate. Sed quoniam et advesperascit et mihi ad villam revertendum est, nunc quidem hactenus; Quid enim mihi potest esse optatius quam cum Catone, omnium virtutum auctore, de virtutibus disputare? Ergo infelix una molestia, fellx rursus, cum is ipse anulus in praecordiis piscis inventus est? Neque enim disputari sine reprehensione nec cum iracundia aut pertinacia recte disputari potest. Paulum, cum regem Persem captum adduceret, eodem flumine invectio?
+
+Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Res enim concurrent contrariae. Tenesne igitur, inquam, Hieronymus Rhodius quid dicat esse summum bonum, quo putet omnia referri oportere? Etenim nec iustitia nec amicitia esse omnino poterunt, nisi ipsae per se expetuntur. Invidiosum nomen est, infame, suspectum. Et ille ridens: Video, inquit, quid agas;
+
+Satis est tibi in te, satis in legibus, satis in mediocribus amicitiis praesidii. Gracchum patrem non beatiorem fuisse quam fillum, cum alter stabilire rem publicam studuerit, alter evertere. Nunc dicam de voluptate, nihil scilicet novi, ea tamen, quae te ipsum probaturum esse confidam. Quid, si reviviscant Platonis illi et deinceps qui eorum auditores fuerunt, et tecum ita loquantur? Quid, si reviviscant Platonis illi et deinceps qui eorum auditores fuerunt, et tecum ita loquantur? Sed residamus, inquit, si placet. Illa videamus, quae a te de amicitia dicta sunt. A villa enim, credo, et: Si ibi te esse scissem, ad te ipse venissem. Si ista mala sunt, in quae potest incidere sapiens, sapientem esse non esse ad beate vivendum satis.
+
+Intrandum est igitur in rerum naturam et penitus quid ea postulet pervidendum; Cum autem venissemus in Academiae non sine causa nobilitata spatia, solitudo erat ea, quam volueramus.
+
+Non igitur potestis voluptate omnia dirigentes aut tueri aut retinere virtutem. Ne tum quidem te respicies et cogitabis sibi quemque natum esse et suis voluptatibus? Quae sunt igitur communia vobis cum antiquis, iis sic utamur quasi concessis; Scripsit enim et multis saepe verbis et breviter arteque in eo libro, quem modo nominavi, mortem nihil ad nos pertinere. An me, inquam, nisi te audire vellem, censes haec dicturum fuisse? Quae similitudo in genere etiam humano apparet.
+
+Tum mihi Piso: Quid ergo? Ita multo sanguine profuso in laetitia et in victoria est mortuus. Et ille ridens: Video, inquit, quid agas; Sequitur disserendi ratio cognitioque naturae; Virtutis, magnitudinis animi, patientiae, fortitudinis fomentis dolor mitigari solet. -delector enim, quamquam te non possum, ut ais, corrumpere, delector, inquam, et familia vestra et nomine. Polemoni et iam ante Aristoteli ea prima visa sunt, quae paulo ante dixi. Graecum enim hunc versum nostis omnes-: Suavis laborum est praeteritorum memoria.
+
+Itaque ne iustitiam quidem recte quis dixerit per se ipsam optabilem, sed quia iucunditatis vel plurimum afferat. An, partus ancillae sitne in fructu habendus, disseretur inter principes civitatis, P. Cum autem assumpta ratío est, tanto in dominatu locatur, ut omnia illa prima naturae hulus tutelae subiciantur. Similiter sensus, cum accessit ad naturam, tuetur illam quidem, sed etiam se tuetur; Non semper, inquam; Potius ergo illa dicantur: turpe esse, viri non esse debilitari dolore, frangi, succumbere. Quia voluptatem hanc esse sentiunt omnes, quam sensus accipiens movetur et iucunditate quadam perfunditur. Cum sciret confestim esse moriendum eamque mortem ardentiore studio peteret, quam Epicurus voluptatem petendam putat.
From 088ae3ca637e502093ce7f12a88a8b642c0f609a Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Fri, 30 May 2025 20:19:19 -0700
Subject: [PATCH 026/153] Update Html.php
---
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 9cc3abe5b7..f8ab6d57e5 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -97,7 +97,7 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit
}
if (substr($html, 0, 2) === "\xfe\xff" || substr($html, 0, 2) === "\xff\xfe") {
- $html = mb_convert_encoding($html, 'UTF-8', 'UTF-16');
+ $html = (string) mb_convert_encoding($html, 'UTF-8', 'UTF-16');
}
if (substr($html, 0, 3) === "\xEF\xBB\xBF") {
$html = substr($html, 3);
From 1e001853f8817bb297ac0d75c27db5938d11b587 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Thu, 12 Jun 2025 17:52:05 -0700
Subject: [PATCH 027/153] Catch up With Latest Commits to Master
---
src/PhpWord/Shared/Html.php | 2 +-
tests/PhpWordTests/Shared/HtmlTest.php | 1 -
2 files changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index bd3ef55a44..52c55aaf3c 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -172,7 +172,7 @@ private static function replaceNonAsciiIfNeeded(string $convert): ?string
* parse Inline style of a node.
*
* @param DOMNode $node Node to check on attributes and to compile a style array
- * @param array $styles is supplied, the inline style attributes are added to the already existing style
+ * @param array $styles is supplied, the inline style attributes are added to the already existing style
*
* @return array
*/
diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php
index 50f1e699a6..2a8705b483 100644
--- a/tests/PhpWordTests/Shared/HtmlTest.php
+++ b/tests/PhpWordTests/Shared/HtmlTest.php
@@ -234,7 +234,6 @@ public function testSpanClassName(): void
$html = 'This is bold text.
';
$section = $phpWord->addSection();
Html::addHtml($section, $html);
- self::assertTrue(true);
$element = $section->getElements()[0];
self::assertInstanceOf(TextRun::class, $element);
$textElements = $element->getElements();
From 3bf1d9d3ceded0eea54afb061fa2827b6a4db2fa Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 23 Jul 2025 10:33:54 -0700
Subject: [PATCH 028/153] Require mbstring
---
.github/workflows/php.yml | 2 +-
composer.json | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index 016120d913..dde3e7c0dc 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -75,7 +75,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
- extensions: gd, xml, zip
+ extensions: dom, gd, mbstring, xml, zip
coverage: ${{ (matrix.php == '7.3') && 'xdebug' || 'none' }}
- uses: actions/checkout@v2
diff --git a/composer.json b/composer.json
index abf006fd34..25e8e495bf 100644
--- a/composer.json
+++ b/composer.json
@@ -109,8 +109,9 @@
"php": "^7.1|^8.0",
"ext-dom": "*",
"ext-gd": "*",
- "ext-zip": "*",
+ "ext-mbstring": "*",
"ext-xml": "*",
+ "ext-zip": "*",
"phpoffice/math": "^0.3"
},
"require-dev": {
From b7b30ac1a3cbe782da8756ee072d5584ec0aea83 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 23 Jul 2025 11:31:34 -0700
Subject: [PATCH 029/153] See If I Can Run Nightly
---
.github/workflows/php.yml | 27 +++++++++++++++++++++++++--
1 file changed, 25 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index dde3e7c0dc..d90021c96c 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -64,12 +64,29 @@ jobs:
run: ./vendor/bin/phpstan analyse -c phpstan.neon.dist
phpunit:
- name: PHPUnit ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ experimental:
+ - false
+ php:
+ - '7.1'
+ - '7.2'
+ - '7.3'
+ - '7.4'
+ - '8.0'
+ - '8.1'
+ - '8.2'
+ - '8.3'
+ - '8.4'
+
+ include:
+ - php: 'nightly'
+ experimental: true
+
+ name: PHPUnit ${{ matrix.php }}
+
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -95,6 +112,12 @@ jobs:
if: matrix.php == '8.1' || matrix.php == '8.2' || matrix.php == '8.3' || matrix.php == '8.4'
run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
+ - name: Run phpunit nightly experimental
+ if: matrix.php == 'nightly'
+ env:
+ FAILURE_ACTION: "${{ matrix.experimental == true }}"
+ run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
+
- name: Run phpunit 7.3
if: matrix.php == '7.3'
run: ./vendor/bin/phpunit -c phpunit9.xml.dist --coverage-clover build/clover.xml
From 2bb935a17cbdc9976f6d33b5838a0e758bc3b69e Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 23 Jul 2025 12:26:33 -0700
Subject: [PATCH 030/153] Didn't Work
Couldn't resolve a good version for mpdf.
---
.github/workflows/php.yml | 27 ++-------------------------
1 file changed, 2 insertions(+), 25 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index d90021c96c..dde3e7c0dc 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -64,29 +64,12 @@ jobs:
run: ./vendor/bin/phpstan analyse -c phpstan.neon.dist
phpunit:
+ name: PHPUnit ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- experimental:
- - false
- php:
- - '7.1'
- - '7.2'
- - '7.3'
- - '7.4'
- - '8.0'
- - '8.1'
- - '8.2'
- - '8.3'
- - '8.4'
-
- include:
- - php: 'nightly'
- experimental: true
-
- name: PHPUnit ${{ matrix.php }}
-
+ php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -112,12 +95,6 @@ jobs:
if: matrix.php == '8.1' || matrix.php == '8.2' || matrix.php == '8.3' || matrix.php == '8.4'
run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
- - name: Run phpunit nightly experimental
- if: matrix.php == 'nightly'
- env:
- FAILURE_ACTION: "${{ matrix.experimental == true }}"
- run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
-
- name: Run phpunit 7.3
if: matrix.php == '7.3'
run: ./vendor/bin/phpunit -c phpunit9.xml.dist --coverage-clover build/clover.xml
From 76a7d28da996913f79b25e5ad14813cade45f17a Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 27 Jul 2025 14:28:03 -0700
Subject: [PATCH 031/153] Try Nightly Again
---
.github/workflows/php.yml | 31 +++++++++++++++++++++++++++++--
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index dde3e7c0dc..1972127545 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -64,12 +64,29 @@ jobs:
run: ./vendor/bin/phpstan analyse -c phpstan.neon.dist
phpunit:
- name: PHPUnit ${{ matrix.php }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
- php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ experimental:
+ - false
+ php:
+ - '7.1'
+ - '7.2'
+ - '7.3'
+ - '7.4'
+ - '8.0'
+ - '8.1'
+ - '8.2'
+ - '8.3'
+ - '8.4'
+
+ include:
+ - php: 'nightly'
+ experimental: true
+
+ name: PHPUnit ${{ matrix.php }}
+
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -80,6 +97,10 @@ jobs:
- uses: actions/checkout@v2
+ - name: Composer Config nightly
+ if: matrix.php == 'nightly'
+ run: composer config platform.php 8.4.99
+
- name: Composer Install
run: composer install --ansi --prefer-dist --no-interaction --no-progress
@@ -95,6 +116,12 @@ jobs:
if: matrix.php == '8.1' || matrix.php == '8.2' || matrix.php == '8.3' || matrix.php == '8.4'
run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
+ - name: Run phpunit nightly experimental
+ if: matrix.php == 'nightly'
+ env:
+ FAILURE_ACTION: "${{ matrix.experimental == true }}"
+ run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
+
- name: Run phpunit 7.3
if: matrix.php == '7.3'
run: ./vendor/bin/phpunit -c phpunit9.xml.dist --coverage-clover build/clover.xml
From 62383518d5b4d3cd21aff303ea80f02c7f720ad0 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 29 Jul 2025 00:17:47 -0700
Subject: [PATCH 032/153] Generate Samples for Nightly
---
.github/workflows/php.yml | 25 +++++++++++++++++--
composer.json | 2 ++
samples/Sample_45_Autoloader.php | 2 +-
..._RTLTitles.php => Sample_47_RTLTitles.php} | 0
4 files changed, 26 insertions(+), 3 deletions(-)
rename samples/{Sample_45_RTLTitles.php => Sample_47_RTLTitles.php} (100%)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index 1972127545..a7b09847c4 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -141,7 +141,22 @@ jobs:
strategy:
fail-fast: false
matrix:
- php: ['7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ experimental:
+ - false
+ php:
+ - '7.1'
+ - '7.2'
+ - '7.3'
+ - '7.4'
+ - '8.0'
+ - '8.1'
+ - '8.2'
+ - '8.3'
+ - '8.4'
+
+ include:
+ - php: 'nightly'
+ experimental: true
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
@@ -152,8 +167,14 @@ jobs:
- uses: actions/checkout@v2
+ - name: Composer Config nightly
+ if: matrix.php == 'nightly'
+ run: composer config platform.php 8.4.99
+
- name: Composer Install
run: composer install --ansi --prefer-dist --no-interaction --no-progress
- - name: Generate samples files
+ - name: "Generate samples files (Experimental: ${{ matrix.experimental }})"
+ env:
+ FAILURE_ACTION: "${{ matrix.experimental == true }}"
run: composer run samples
diff --git a/composer.json b/composer.json
index 25e8e495bf..278b85f80d 100644
--- a/composer.json
+++ b/composer.json
@@ -97,6 +97,8 @@
"php samples/Sample_43_RTLDefault.php",
"php samples/Sample_44_ExtractVariablesFromReaderWord2007.php",
"php samples/Sample_45_Autoloader.php"
+ "php samples/Sample_46_RubyPhoneticGuide.php"
+ "php samples/Sample_47_RTLTitles.php"
]
},
"scripts-descriptions": {
diff --git a/samples/Sample_45_Autoloader.php b/samples/Sample_45_Autoloader.php
index 71f443f770..bdd522f483 100644
--- a/samples/Sample_45_Autoloader.php
+++ b/samples/Sample_45_Autoloader.php
@@ -2,7 +2,7 @@
use PhpOffice\PhpWord\Style\Font;
-define('USE_AUTOLOADER', true);
+//define('USE_AUTOLOADER', true);
include_once 'Sample_Header.php';
diff --git a/samples/Sample_45_RTLTitles.php b/samples/Sample_47_RTLTitles.php
similarity index 100%
rename from samples/Sample_45_RTLTitles.php
rename to samples/Sample_47_RTLTitles.php
From 7a9b50ec20a650598d097c113b6ef635aec3d1cb Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 29 Jul 2025 00:20:14 -0700
Subject: [PATCH 033/153] Correct Json
---
composer.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/composer.json b/composer.json
index 278b85f80d..c898a700dd 100644
--- a/composer.json
+++ b/composer.json
@@ -96,8 +96,8 @@
"php samples/Sample_42_TemplateSetCheckbox.php",
"php samples/Sample_43_RTLDefault.php",
"php samples/Sample_44_ExtractVariablesFromReaderWord2007.php",
- "php samples/Sample_45_Autoloader.php"
- "php samples/Sample_46_RubyPhoneticGuide.php"
+ "php samples/Sample_45_Autoloader.php",
+ "php samples/Sample_46_RubyPhoneticGuide.php",
"php samples/Sample_47_RTLTitles.php"
]
},
From 821e0a52e783b74e23b299d02223681082c5de07 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 29 Jul 2025 07:18:29 -0700
Subject: [PATCH 034/153] Tweak
---
samples/Sample_45_Autoloader.php | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/samples/Sample_45_Autoloader.php b/samples/Sample_45_Autoloader.php
index bdd522f483..ac53c05367 100644
--- a/samples/Sample_45_Autoloader.php
+++ b/samples/Sample_45_Autoloader.php
@@ -2,7 +2,10 @@
use PhpOffice\PhpWord\Style\Font;
-//define('USE_AUTOLOADER', true);
+// When following define is TRUE,
+// this script will use PhpWord autoloader rather than Composer,
+// and will not be able to find Pdf renderer.
+define('USE_AUTOLOADER', PHP_MINOR_VERSION % 2 === 0);
include_once 'Sample_Header.php';
From b2600118aebc8a04cf852ddd9e36c3d4641106aa Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 24 Aug 2025 17:07:55 -0700
Subject: [PATCH 035/153] Php8.5 Accommodations
Dompdf (temporary till 8.5-compatible release available), `chr` in Reader/MsDoc, `setAccessible` in AbstractStyleTest.
---
src/PhpWord/Reader/MsDoc.php | 4 +--
src/PhpWord/Writer/PDF/DomPDF.php | 29 +++++++++++++++++++
.../PhpWordTests/Style/AbstractStyleTest.php | 6 +++-
3 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/src/PhpWord/Reader/MsDoc.php b/src/PhpWord/Reader/MsDoc.php
index 7cbeb1a8ac..ad88f4adac 100644
--- a/src/PhpWord/Reader/MsDoc.php
+++ b/src/PhpWord/Reader/MsDoc.php
@@ -1218,7 +1218,7 @@ private function readRecordSttbfFfn(): void
$char = self::getInt2d($this->data1Table, $posMem);
$posMem += 2;
if ($char > 0) {
- $xszFfn .= chr($char);
+ $xszFfn .= mb_chr($char, 'UTF-8');
}
} while ($char != 0);
// xszAlt
@@ -1230,7 +1230,7 @@ private function readRecordSttbfFfn(): void
if ($char == 0) {
break;
}
- $xszAlt .= chr($char);
+ $xszAlt .= mb_chr($char, 'UTF-8');
} while ($char != 0);
}
$this->arrayFonts[] = [
diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php
index 464dbfa59f..752f49ef6e 100644
--- a/src/PhpWord/Writer/PDF/DomPDF.php
+++ b/src/PhpWord/Writer/PDF/DomPDF.php
@@ -64,6 +64,14 @@ public function save(string $filename): void
$orientation = 'portrait';
// Create PDF
+ $restoreHandler = false;
+ if (PHP_VERSION_ID >= self::$temporaryVersionCheck) {
+ // @codeCoverageIgnoreStart
+ $errhandler = [$this, 'specialErrorHandler'];
+ set_error_handler($errhandler); // @phpstan-ignore-line
+ $restoreHandler = true;
+ // @codeCoverageIgnoreEnd
+ }
$pdf = $this->createExternalWriterInstance();
$pdf->setPaper(strtolower($paperSize), $orientation);
$pdf->loadHtml(str_replace(PHP_EOL, '', $this->getContent()));
@@ -72,6 +80,27 @@ public function save(string $filename): void
// Write to file
fwrite($fileHandle, $pdf->output());
+ if ($restoreHandler) {
+ restore_error_handler(); // @codecoverageignore
+ }
parent::restoreStateAfterSave($fileHandle);
}
+
+ protected static int $temporaryVersionCheck = 80500;
+
+ /*
+ * Temporary handler for Php8.5 waiting for Dompdf release.
+ *
+ * @codeCoverageIgnore
+ */
+ public function specialErrorHandler(int $errno, string $errstr, string $filename, int $lineno): bool
+ {
+ if ($errno === E_DEPRECATED) {
+ if (preg_match('/canonical|imagedestroy/', $errstr) === 1) {
+ return true;
+ }
+ }
+
+ return false; // continue error handling
+ }
}
diff --git a/tests/PhpWordTests/Style/AbstractStyleTest.php b/tests/PhpWordTests/Style/AbstractStyleTest.php
index 679c9f5e49..cf79519bac 100644
--- a/tests/PhpWordTests/Style/AbstractStyleTest.php
+++ b/tests/PhpWordTests/Style/AbstractStyleTest.php
@@ -124,6 +124,8 @@ public function testSetValEnumException(): void
self::assertEquals('b', self::callProtectedMethod($stub, 'setEnumVal', ['z', ['a', 'b'], 'b']));
}
+ protected static int $temporaryVersionCheck = 80500;
+
/**
* Helper function to call protected method.
*
@@ -134,7 +136,9 @@ public static function callProtectedMethod($object, $method, array $args = [])
{
$class = new ReflectionClass(get_class($object));
$method = $class->getMethod($method);
- $method->setAccessible(true);
+ if (PHP_VERSION_ID < self::$temporaryVersionCheck) {
+ $method->setAccessible(true);
+ }
return $method->invokeArgs($object, $args);
}
From ccc5bf4ddf3e8d8d0c3ee04a66534b75dd1e4279 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 24 Aug 2025 17:11:53 -0700
Subject: [PATCH 036/153] Accomodate Php7.1/2/3
---
tests/PhpWordTests/Style/AbstractStyleTest.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/tests/PhpWordTests/Style/AbstractStyleTest.php b/tests/PhpWordTests/Style/AbstractStyleTest.php
index cf79519bac..346fd06ce7 100644
--- a/tests/PhpWordTests/Style/AbstractStyleTest.php
+++ b/tests/PhpWordTests/Style/AbstractStyleTest.php
@@ -124,7 +124,8 @@ public function testSetValEnumException(): void
self::assertEquals('b', self::callProtectedMethod($stub, 'setEnumVal', ['z', ['a', 'b'], 'b']));
}
- protected static int $temporaryVersionCheck = 80500;
+ /** @var int */
+ protected static $temporaryVersionCheck = 80500;
/**
* Helper function to call protected method.
From 99fcc96f912df829006190670425f7c56e06d9b7 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 24 Aug 2025 17:13:32 -0700
Subject: [PATCH 037/153] More Php7.1/2/3
---
src/PhpWord/Writer/PDF/DomPDF.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php
index 752f49ef6e..9f9d35dda8 100644
--- a/src/PhpWord/Writer/PDF/DomPDF.php
+++ b/src/PhpWord/Writer/PDF/DomPDF.php
@@ -86,7 +86,8 @@ public function save(string $filename): void
parent::restoreStateAfterSave($fileHandle);
}
- protected static int $temporaryVersionCheck = 80500;
+ /** @var int */
+ protected static $temporaryVersionCheck = 80500;
/*
* Temporary handler for Php8.5 waiting for Dompdf release.
From cd209de7d07d0a0afe71f6f7f4908d93841b4d0e Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 24 Aug 2025 18:57:32 -0700
Subject: [PATCH 038/153] Change Case in Annotation
---
src/PhpWord/Writer/PDF/DomPDF.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php
index 9f9d35dda8..c918fe8a3b 100644
--- a/src/PhpWord/Writer/PDF/DomPDF.php
+++ b/src/PhpWord/Writer/PDF/DomPDF.php
@@ -81,7 +81,7 @@ public function save(string $filename): void
fwrite($fileHandle, $pdf->output());
if ($restoreHandler) {
- restore_error_handler(); // @codecoverageignore
+ restore_error_handler(); // @codeCoverageIgnore
}
parent::restoreStateAfterSave($fileHandle);
}
From 2242a8498d1935efd6d22e3aea9efc4b9c405041 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sun, 24 Aug 2025 20:26:12 -0700
Subject: [PATCH 039/153] Need Double Asterisk in Doc-block
---
src/PhpWord/Writer/PDF/DomPDF.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php
index c918fe8a3b..3de2d68d58 100644
--- a/src/PhpWord/Writer/PDF/DomPDF.php
+++ b/src/PhpWord/Writer/PDF/DomPDF.php
@@ -89,7 +89,7 @@ public function save(string $filename): void
/** @var int */
protected static $temporaryVersionCheck = 80500;
- /*
+ /**
* Temporary handler for Php8.5 waiting for Dompdf release.
*
* @codeCoverageIgnore
From 3223dba6c558bd2d8eba2fc55790141f409e1e86 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 00:14:15 -0700
Subject: [PATCH 040/153] Coverage Tweaks
---
src/PhpWord/Reader/Word2007/Styles.php | 6 ++----
src/PhpWord/Writer/HTML/Element/Title.php | 6 ++----
src/PhpWord/Writer/HTML/Part/Head.php | 5 +----
tests/PhpWordTests/Writer/PDFTest.php | 1 +
4 files changed, 6 insertions(+), 12 deletions(-)
diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php
index 81aaf203c1..0bcc96b75e 100644
--- a/src/PhpWord/Reader/Word2007/Styles.php
+++ b/src/PhpWord/Reader/Word2007/Styles.php
@@ -68,11 +68,9 @@ public function read(PhpWord $phpWord): void
if ($nodes->length > 0) {
foreach ($nodes as $node) {
$type = $xmlReader->getAttribute('w:type', $node);
- $name = $xmlReader->getAttribute('w:val', $node, 'w:name');
+ $namex = $xmlReader->getAttribute('w:val', $node, 'w:name');
$styleId = $xmlReader->getAttribute('w:styleId', $node);
- if (null === $name) {
- $name = $styleId;
- }
+ $name = ($namex !== null) ? $namex : $styleId;
$headingMatches = [];
preg_match('/Heading\s*(\d)/i', $name, $headingMatches);
// $default = ($xmlReader->getAttribute('w:default', $node) == 1);
diff --git a/src/PhpWord/Writer/HTML/Element/Title.php b/src/PhpWord/Writer/HTML/Element/Title.php
index f3aeb395f4..edd114788c 100644
--- a/src/PhpWord/Writer/HTML/Element/Title.php
+++ b/src/PhpWord/Writer/HTML/Element/Title.php
@@ -53,7 +53,7 @@ public function write()
$writer = new Container($this->parentWriter, $text);
$text = $writer->write();
}
- $write1 = $write2 = $write3 = '';
+ $write1 = $write3 = '';
$style = Style::getStyle('Heading_' . $this->element->getDepth());
if ($style !== null) {
$styleWriter = new Font($style);
@@ -62,10 +62,8 @@ public function write()
if (is_object($paragraphStyle)) {
$styleWriter = new Paragraph($paragraphStyle);
$write3 = $styleWriter->write();
- if ($write1 !== '' && $write3 !== '') {
- $write2 = ' ';
- }
}
+ $write2 = ($write1 === '' || $write3 === '') ? '' : ' ';
$css = "$write1$write2$write3";
if ($css !== '') {
$css = " style=\"$css\"";
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
index 4da526075d..f66b582001 100644
--- a/src/PhpWord/Writer/HTML/Part/Head.php
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -88,7 +88,7 @@ private function writeStyles(): string
$defaultFontColor = Settings::getDefaultFontColor();
// Default styles
$astarray = [
- 'font-family' => $this->getFontFamily(Settings::getDefaultFontName(), $this->getParentWriter()->getDefaultGenericFont()),
+ 'font-family' => $this->getFontFamily(Settings::getDefaultFontName() ?: Settings::DEFAULT_FONT_NAME, $this->getParentWriter()->getDefaultGenericFont()),
'font-size' => Settings::getDefaultFontSize() . 'pt',
'color' => "#{$defaultFontColor}",
];
@@ -199,9 +199,6 @@ private function writeStyles(): string
*/
private function getFontFamily(string $font, string $genericFont): string
{
- if (empty($font)) {
- return '';
- }
$fontfamily = "'" . htmlspecialchars($font, ENT_QUOTES, 'UTF-8') . "'";
if (!empty($genericFont)) {
$fontfamily .= ", $genericFont";
diff --git a/tests/PhpWordTests/Writer/PDFTest.php b/tests/PhpWordTests/Writer/PDFTest.php
index b9e31511cf..c7e5376fab 100644
--- a/tests/PhpWordTests/Writer/PDFTest.php
+++ b/tests/PhpWordTests/Writer/PDFTest.php
@@ -41,6 +41,7 @@ public function testConstruct(): void
$rendererLibraryPath = realpath(PHPWORD_TESTS_BASE_DIR . '/../vendor/dompdf/dompdf');
Settings::setPdfRenderer($rendererName, $rendererLibraryPath);
$writer = new PDF(new PhpWord());
+ $writer->setFont('xyz'); //* @phpstan-ignore-line
$writer->save($file);
self::assertFileExists($file);
From d685340a1b7c706e63acc9e67dfd1200d3e79f75 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 00:28:21 -0700
Subject: [PATCH 041/153] Php8.5 Dompdf Adjustment
---
.github/workflows/php.yml | 2 +-
src/PhpWord/Writer/PDF/DomPDF.php | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index a7b09847c4..fc3a074069 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -120,7 +120,7 @@ jobs:
if: matrix.php == 'nightly'
env:
FAILURE_ACTION: "${{ matrix.experimental == true }}"
- run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage
+ run: ./vendor/bin/phpunit -c phpunit10.xml.dist --no-coverage || $FAILURE_ACTION
- name: Run phpunit 7.3
if: matrix.php == '7.3'
diff --git a/src/PhpWord/Writer/PDF/DomPDF.php b/src/PhpWord/Writer/PDF/DomPDF.php
index 3de2d68d58..59c8ac9c87 100644
--- a/src/PhpWord/Writer/PDF/DomPDF.php
+++ b/src/PhpWord/Writer/PDF/DomPDF.php
@@ -97,7 +97,7 @@ public function save(string $filename): void
public function specialErrorHandler(int $errno, string $errstr, string $filename, int $lineno): bool
{
if ($errno === E_DEPRECATED) {
- if (preg_match('/canonical|imagedestroy/', $errstr) === 1) {
+ if (preg_match('/canonical|imagedestroy|http_response_header/', $errstr) === 1) {
return true;
}
}
From 5b6e415971815c72b718adca6941d1954c641c54 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 01:02:35 -0700
Subject: [PATCH 042/153] Small yaml Change
---
.github/workflows/php.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index fc3a074069..c9e2b0c97a 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -177,4 +177,4 @@ jobs:
- name: "Generate samples files (Experimental: ${{ matrix.experimental }})"
env:
FAILURE_ACTION: "${{ matrix.experimental == true }}"
- run: composer run samples
+ run: composer run samples || $FAILURE_ACTION
From 644517671aff2bb97eb7bc2896539c1ac727f324 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 08:18:09 -0700
Subject: [PATCH 043/153] Sample Warning in Php8.5
Sample_11_ReadWord2007 reads a file with color=auto, and RTF Writer doesn't handle it properly. Change to treat it as black.
---
src/PhpWord/Writer/RTF/Part/Header.php | 6 ++-
.../PhpWordTests/Writer/RTF/Sample11Test.php | 38 +++++++++++++++++++
2 files changed, 43 insertions(+), 1 deletion(-)
create mode 100644 tests/PhpWordTests/Writer/RTF/Sample11Test.php
diff --git a/src/PhpWord/Writer/RTF/Part/Header.php b/src/PhpWord/Writer/RTF/Part/Header.php
index 97644fe4ac..e07f692b47 100644
--- a/src/PhpWord/Writer/RTF/Part/Header.php
+++ b/src/PhpWord/Writer/RTF/Part/Header.php
@@ -157,7 +157,11 @@ private function writeColorTable()
$content .= '{';
$content .= '\colortbl;';
foreach ($this->colorTable as $color) {
- [$red, $green, $blue] = Converter::htmlToRgb($color);
+ $temp = Converter::htmlToRgb($color);
+ $red = $green = $blue = 0;
+ if (is_array($temp)) {
+ [$red, $green, $blue] = $temp;
+ }
$content .= "\\red{$red}\\green{$green}\\blue{$blue};";
}
$content .= '}';
diff --git a/tests/PhpWordTests/Writer/RTF/Sample11Test.php b/tests/PhpWordTests/Writer/RTF/Sample11Test.php
new file mode 100644
index 0000000000..92e6a8aa40
--- /dev/null
+++ b/tests/PhpWordTests/Writer/RTF/Sample11Test.php
@@ -0,0 +1,38 @@
+getContent();
+ $expected = '{\colortbl;\red255\green0\blue0;\red0\green0\blue0;\red0\green0\blue255;}';
+ self::assertStringContainsString($expected, $content);
+ }
+}
From 6769df275319934315d6e3c903a38cf81138fec7 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 20:38:16 -0700
Subject: [PATCH 044/153] Sample 11 Fixed for Odt and Docx, Partial for RTF and
HTML
Still not out of the woods.
Write to Docx and Odt are fine.
Write to RTF and HTML are fine only if they follow write to ODT.
---
src/PhpWord/Style.php | 31 ++++++++++++++++++++-
src/PhpWord/Writer/HTML/Part/Head.php | 3 +-
src/PhpWord/Writer/ODText/Style/Font.php | 14 ++++++----
src/PhpWord/Writer/RTF/Element/Link.php | 16 ++++++++++-
src/PhpWord/Writer/RTF/Element/Text.php | 15 ++++++++++
src/PhpWord/Writer/Word2007/Part/Styles.php | 5 ++++
src/PhpWord/Writer/Word2007/Style/Font.php | 3 +-
7 files changed, 76 insertions(+), 11 deletions(-)
diff --git a/src/PhpWord/Style.php b/src/PhpWord/Style.php
index 6552a72ce6..0b48a1df3e 100644
--- a/src/PhpWord/Style.php
+++ b/src/PhpWord/Style.php
@@ -178,15 +178,24 @@ public static function getStyles()
/**
* Get style by name.
*
- * @param string $styleName
+ * @param ?string $styleName
*
* @return ?AbstractStyle Paragraph|Font|Table|Numbering
*/
public static function getStyle($styleName)
{
+ if ($styleName === null) {
+ return null;
+ }
if (isset(self::$styles[$styleName])) {
return self::$styles[$styleName];
}
+ foreach (self::$styles as $key => $value) {
+ $styleId = self::alternateName($key);
+ if ($styleId === $styleName) {
+ return $value;
+ }
+ }
return null;
}
@@ -221,4 +230,24 @@ private static function setStyleValues($name, $style, $value = null)
return self::getStyle($name);
}
+
+ /**
+ * Get alternate name for style names with embedded spaces.
+ *
+ * @param string $name
+ *
+ * @return string
+ */
+ public static function alternateName($name)
+ {
+ $explode = explode(' ', $name);
+ if (count($explode) > 1) {
+ $name = '';
+ foreach ($explode as $explodeItem) {
+ $name .= ucfirst($explodeItem);
+ }
+ }
+
+ return $name;
+ }
}
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
index f66b582001..6c2165c0be 100644
--- a/src/PhpWord/Writer/HTML/Part/Head.php
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -130,7 +130,8 @@ private function writeStyles(): string
// Custom styles
$customStyles = Style::getStyles();
if (is_array($customStyles)) {
- foreach ($customStyles as $name => $style) {
+ foreach ($customStyles as $namex => $style) {
+ $name = Style::alternateName($namex);
$styleParagraph = null;
if ($style instanceof Font) {
$styleWriter = new FontStyleWriter($style);
diff --git a/src/PhpWord/Writer/ODText/Style/Font.php b/src/PhpWord/Writer/ODText/Style/Font.php
index 95582ec48b..2e32dcc024 100644
--- a/src/PhpWord/Writer/ODText/Style/Font.php
+++ b/src/PhpWord/Writer/ODText/Style/Font.php
@@ -18,6 +18,8 @@
namespace PhpOffice\PhpWord\Writer\ODText\Style;
+use PhpOffice\PhpWord\Style;
+
/**
* Font style writer.
*
@@ -31,13 +33,13 @@ class Font extends AbstractStyle
public function write(): void
{
$style = $this->getStyle();
- if (!$style instanceof \PhpOffice\PhpWord\Style\Font) {
+ if (!$style instanceof Style\Font) {
return;
}
$xmlWriter = $this->getXmlWriter();
$stylep = $style->getParagraph();
- if ($stylep instanceof \PhpOffice\PhpWord\Style\Paragraph) {
+ if ($stylep instanceof Style\Paragraph) {
$temp1 = clone $stylep;
$temp1->setStyleName($style->getStyleName());
$temp2 = new Paragraph($xmlWriter, $temp1);
@@ -45,7 +47,7 @@ public function write(): void
}
$xmlWriter->startElement('style:style');
- $xmlWriter->writeAttribute('style:name', $style->getStyleName());
+ $xmlWriter->writeAttribute('style:name', Style::alternateName($style->getStyleName()));
$xmlWriter->writeAttribute('style:family', 'text');
$xmlWriter->startElement('style:text-properties');
@@ -100,9 +102,9 @@ public function write(): void
$xmlWriter->writeAttribute('style:country-complex', 'none');
}
- // @todo Foreground-Color
-
- // @todo Background color
+ // Foreground-Color (which is really background color)
+ $fgColor = $style->getFgColor();
+ $xmlWriter->writeAttributeIf($fgColor != '', 'fo:background-color', '#' . \PhpOffice\PhpWord\Shared\Converter::stringToRgb($fgColor));
$xmlWriter->endElement(); // style:text-properties
$xmlWriter->endElement(); // style:style
diff --git a/src/PhpWord/Writer/RTF/Element/Link.php b/src/PhpWord/Writer/RTF/Element/Link.php
index 1687d38bca..382099862a 100644
--- a/src/PhpWord/Writer/RTF/Element/Link.php
+++ b/src/PhpWord/Writer/RTF/Element/Link.php
@@ -41,7 +41,21 @@ public function write()
$content = '';
$content .= $this->writeOpening();
$content .= '{\field {\*\fldinst {HYPERLINK "' . $this->element->getSource() . '"}}{\\fldrslt {';
- $content .= $this->writeFontStyle();
+ $temp = $this->writeFontStyle();
+ $content .= $temp;
+ if ($temp === '\cf0\f0 ') {
+ $content .= '\ul ';
+ $colors = $this->parentWriter->getColorTable();
+ $count = count($colors);
+ for ($i = 0; $i < $count; ++$i) {
+ if ($colors[$i] === '0000FF') {
+ $j = $i + 1;
+ $content .= '\cf' . $j . ' ';
+
+ break;
+ }
+ }
+ }
$content .= $this->writeText($this->element->getText());
$content .= '}}}';
$content .= $this->writeClosing();
diff --git a/src/PhpWord/Writer/RTF/Element/Text.php b/src/PhpWord/Writer/RTF/Element/Text.php
index 71d3a27ce8..8999ad75fd 100644
--- a/src/PhpWord/Writer/RTF/Element/Text.php
+++ b/src/PhpWord/Writer/RTF/Element/Text.php
@@ -45,6 +45,21 @@ public function write()
$content .= $this->writeOpening();
$content .= '{';
$content .= $this->writeFontStyle();
+ $change = $element->getTrackChange();
+ if ($change !== null) {
+ if ($change->getChangeType() === 'DELETED') {
+ $content .= '\strike ';
+ }
+ }
+ if ($this->fontStyle !== null) {
+ $fgColor = $this->fontStyle->getFgColor();
+ if ($fgColor !== null) {
+ $fgColorIndex = array_search($fgColor, $this->parentWriter->getColorTable());
+ if ($fgColorIndex !== false) {
+ $content .= '\highlight' . ++$fgColorIndex . ' ';
+ }
+ }
+ }
$content .= $this->writeText($element->getText());
$content .= '}';
$content .= $this->writeClosing();
diff --git a/src/PhpWord/Writer/Word2007/Part/Styles.php b/src/PhpWord/Writer/Word2007/Part/Styles.php
index 00ab6280e8..4d55845d38 100644
--- a/src/PhpWord/Writer/Word2007/Part/Styles.php
+++ b/src/PhpWord/Writer/Word2007/Part/Styles.php
@@ -202,6 +202,11 @@ private function writeFontStyle(XMLWriter $xmlWriter, $styleName, FontStyle $sty
} elseif (null !== $paragraphStyle) {
// if type is 'paragraph' it should have a styleId
$xmlWriter->writeAttribute('w:styleId', $styleName);
+ } else {
+ $styleId = Style::alternateName($styleName);
+ if ($styleName !== $styleId) {
+ $xmlWriter->writeAttribute('w:styleId', $styleId);
+ }
}
// Style name
diff --git a/src/PhpWord/Writer/Word2007/Style/Font.php b/src/PhpWord/Writer/Word2007/Style/Font.php
index f146307a6d..52b57bc6b1 100644
--- a/src/PhpWord/Writer/Word2007/Style/Font.php
+++ b/src/PhpWord/Writer/Word2007/Style/Font.php
@@ -39,8 +39,7 @@ public function write(): void
{
$xmlWriter = $this->getXmlWriter();
- $isStyleName = $this->isInline && null !== $this->style && is_string($this->style);
- if ($isStyleName) {
+ if ($this->isInline && null !== $this->style && is_string($this->style)) {
$xmlWriter->startElement('w:rPr');
$xmlWriter->startElement('w:rStyle');
$xmlWriter->writeAttribute('w:val', $this->style);
From de2828b3ace59d7a217c8a6ed8e3c9492806182f Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 20:46:10 -0700
Subject: [PATCH 045/153] Quick Fix
---
src/PhpWord/Writer/RTF/Element/Text.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/RTF/Element/Text.php b/src/PhpWord/Writer/RTF/Element/Text.php
index 8999ad75fd..00e7af677d 100644
--- a/src/PhpWord/Writer/RTF/Element/Text.php
+++ b/src/PhpWord/Writer/RTF/Element/Text.php
@@ -51,7 +51,7 @@ public function write()
$content .= '\strike ';
}
}
- if ($this->fontStyle !== null) {
+ if ($this->fontStyle !== null && method_exists($this->fontStyle, 'getFgColor')) {
$fgColor = $this->fontStyle->getFgColor();
if ($fgColor !== null) {
$fgColorIndex = array_search($fgColor, $this->parentWriter->getColorTable());
From 54906bafffc199792c813d7cb440a5ac2a7b6084 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 2 Sep 2025 21:58:35 -0700
Subject: [PATCH 046/153] More Sample11, Fix Html, RTF Still Incomplete
---
src/PhpWord/Writer/HTML/Element/Text.php | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/PhpWord/Writer/HTML/Element/Text.php b/src/PhpWord/Writer/HTML/Element/Text.php
index 2df802c904..01af064d6f 100644
--- a/src/PhpWord/Writer/HTML/Element/Text.php
+++ b/src/PhpWord/Writer/HTML/Element/Text.php
@@ -257,6 +257,11 @@ private function processFontStyle(): void
$fontCSS = $styleWriter->write();
if ($fontCSS) {
$attributeStyle = ' style="' . $fontCSS . '"';
+ } else {
+ $className = $fontStyle->getStyleName();
+ if ($className) {
+ $attributeStyle = ' class="' . $className . '"';
+ }
}
// Attribute Lang
$lang = $fontStyle->getLang();
From dfd34132ea9db9c577901f87fb5fae7dcfd2d096 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 3 Sep 2025 08:01:38 -0700
Subject: [PATCH 047/153] Final (I hope) Changes for Sample11
RTF now works.
In the process of saving, ODText creates a lot of new styles. Because it was being done before RTF and HTML in the samples, it was masking problems in the other formats. It is now changed to happen after the other formats.
---
samples/Sample_Header.php | 9 ++++++++-
.../Writer/RTF/Element/AbstractElement.php | 5 +++++
src/PhpWord/Writer/RTF/Part/Header.php | 19 ++++++++++++++++---
.../PhpWordTests/Writer/RTF/Sample11Test.php | 6 +++++-
4 files changed, 34 insertions(+), 5 deletions(-)
diff --git a/samples/Sample_Header.php b/samples/Sample_Header.php
index 57bb10a4c6..641220c0b8 100644
--- a/samples/Sample_Header.php
+++ b/samples/Sample_Header.php
@@ -31,7 +31,14 @@
}
// Set writers
-$writers = ['Word2007' => 'docx', 'ODText' => 'odt', 'RTF' => 'rtf', 'HTML' => 'html', 'PDF' => 'pdf', 'EPub3' => 'epub'];
+$writers = [
+ 'Word2007' => 'docx',
+ 'RTF' => 'rtf',
+ 'HTML' => 'html',
+ 'PDF' => 'pdf',
+ 'EPub3' => 'epub',
+ 'ODText' => 'odt', // creates a lot of extra style - do it last
+];
// Set PDF renderer
if (null === Settings::getPdfRendererPath()) {
diff --git a/src/PhpWord/Writer/RTF/Element/AbstractElement.php b/src/PhpWord/Writer/RTF/Element/AbstractElement.php
index e007e6aa26..ca669c1f79 100644
--- a/src/PhpWord/Writer/RTF/Element/AbstractElement.php
+++ b/src/PhpWord/Writer/RTF/Element/AbstractElement.php
@@ -107,6 +107,11 @@ protected function getStyles(): void
$this->fontStyle = $element->getFontStyle();
if (is_string($this->fontStyle)) {
$this->fontStyle = Style::getStyle($this->fontStyle);
+ } elseif ($this->fontStyle !== null && is_string($this->fontStyle->getStyleName())) {
+ $temp = Style::getStyle($this->fontStyle->getStyleName());
+ if ($temp instanceof FontStyle) {
+ $this->fontStyle = $temp;
+ }
}
}
diff --git a/src/PhpWord/Writer/RTF/Part/Header.php b/src/PhpWord/Writer/RTF/Part/Header.php
index e07f692b47..05575acc23 100644
--- a/src/PhpWord/Writer/RTF/Part/Header.php
+++ b/src/PhpWord/Writer/RTF/Part/Header.php
@@ -205,9 +205,22 @@ private function registerFont(): void
$elements = $section->getElements();
$this->registerBorderColor($section->getStyle());
foreach ($elements as $element) {
- if (method_exists($element, 'getFontStyle')) {
- $style = $element->getFontStyle();
- $this->registerFontItems($style);
+ $this->anythingToRegister($element);
+ }
+ }
+ }
+
+ /** @param mixed $element */
+ private function anythingToRegister($element): void
+ {
+ if (is_object($element)) {
+ if (method_exists($element, 'getFontStyle')) {
+ $style = $element->getFontStyle();
+ $this->registerFontItems($style);
+ }
+ if (method_exists($element, 'getElements')) {
+ foreach ($element->getElements() as $subElement) {
+ $this->anythingToRegister($subElement);
}
}
}
diff --git a/tests/PhpWordTests/Writer/RTF/Sample11Test.php b/tests/PhpWordTests/Writer/RTF/Sample11Test.php
index 92e6a8aa40..9fdadf8641 100644
--- a/tests/PhpWordTests/Writer/RTF/Sample11Test.php
+++ b/tests/PhpWordTests/Writer/RTF/Sample11Test.php
@@ -32,7 +32,11 @@ public function testSample11(): void
$phpWord = IOFactory::load($source);
$writer = new RTF($phpWord);
$content = $writer->getContent();
- $expected = '{\colortbl;\red255\green0\blue0;\red0\green0\blue0;\red0\green0\blue255;}';
+ $expected = '{\colortbl;\red255\green0\blue0;\red0\green0\blue0;\red0\green0\blue255;\red0\green176\blue80;\red255\green255\blue0;}';
+ self::assertStringContainsString($expected, $content);
+ $expected = '\highlight5 highlighted';
+ self::assertStringContainsString($expected, $content);
+ $expected = '\strike even ';
self::assertStringContainsString($expected, $content);
}
}
From 7cac06e563f4791e902adafca58eb444a3a4b47e Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 3 Sep 2025 09:53:01 -0700
Subject: [PATCH 048/153] Slight Coverage Improvement
---
src/PhpWord/Writer/HTML/Element/Table.php | 5 ++-
src/PhpWord/Writer/RTF/Element/Link.php | 16 +------
.../PhpWordTests/Writer/HTML/Sample11Test.php | 42 +++++++++++++++++++
3 files changed, 47 insertions(+), 16 deletions(-)
create mode 100644 tests/PhpWordTests/Writer/HTML/Sample11Test.php
diff --git a/src/PhpWord/Writer/HTML/Element/Table.php b/src/PhpWord/Writer/HTML/Element/Table.php
index 98f69e3f3f..398987e0b5 100644
--- a/src/PhpWord/Writer/HTML/Element/Table.php
+++ b/src/PhpWord/Writer/HTML/Element/Table.php
@@ -57,8 +57,11 @@ public function write()
$cellStyle = $rowCells[$j]->getStyle();
$cellStyleCss = $this->getTableStyle($cellStyle) ?: $tableCss;
$cellBgColor = $cellStyle->getBgColor();
+ if ($cellBgColor === 'auto') {
+ $cellBgColor = null;
+ }
$cellFgColor = null;
- if ($cellBgColor && $cellBgColor !== 'auto') {
+ if ($cellBgColor) {
$red = hexdec(substr($cellBgColor, 0, 2));
$green = hexdec(substr($cellBgColor, 2, 2));
$blue = hexdec(substr($cellBgColor, 4, 2));
diff --git a/src/PhpWord/Writer/RTF/Element/Link.php b/src/PhpWord/Writer/RTF/Element/Link.php
index 382099862a..1687d38bca 100644
--- a/src/PhpWord/Writer/RTF/Element/Link.php
+++ b/src/PhpWord/Writer/RTF/Element/Link.php
@@ -41,21 +41,7 @@ public function write()
$content = '';
$content .= $this->writeOpening();
$content .= '{\field {\*\fldinst {HYPERLINK "' . $this->element->getSource() . '"}}{\\fldrslt {';
- $temp = $this->writeFontStyle();
- $content .= $temp;
- if ($temp === '\cf0\f0 ') {
- $content .= '\ul ';
- $colors = $this->parentWriter->getColorTable();
- $count = count($colors);
- for ($i = 0; $i < $count; ++$i) {
- if ($colors[$i] === '0000FF') {
- $j = $i + 1;
- $content .= '\cf' . $j . ' ';
-
- break;
- }
- }
- }
+ $content .= $this->writeFontStyle();
$content .= $this->writeText($this->element->getText());
$content .= '}}}';
$content .= $this->writeClosing();
diff --git a/tests/PhpWordTests/Writer/HTML/Sample11Test.php b/tests/PhpWordTests/Writer/HTML/Sample11Test.php
new file mode 100644
index 0000000000..dde70b30bd
--- /dev/null
+++ b/tests/PhpWordTests/Writer/HTML/Sample11Test.php
@@ -0,0 +1,42 @@
+getContent();
+ $expected = 'source file';
+ self::assertStringContainsString($expected, $content);
+ $expected = 'source file';
+ self::assertStringContainsString($expected, $content);
+ $expected = 'even ';
+ self::assertStringContainsString($expected, $content);
+ }
+}
From 25905559592507134e7b105fc45fcd9e4eb18580 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Wed, 3 Sep 2025 21:17:43 -0700
Subject: [PATCH 049/153] New Tests for Coverage Purposes
---
src/PhpWord/Writer/RTF/Element/TextRun.php | 11 +++
.../Writer/HTML/Sample36RtlTest.php | 78 +++++++++++++++
.../PhpWordTests/Writer/RTF/Sample11Test.php | 94 +++++++++++++++++++
3 files changed, 183 insertions(+)
create mode 100644 tests/PhpWordTests/Writer/HTML/Sample36RtlTest.php
diff --git a/src/PhpWord/Writer/RTF/Element/TextRun.php b/src/PhpWord/Writer/RTF/Element/TextRun.php
index 6418de54a8..02816305c4 100644
--- a/src/PhpWord/Writer/RTF/Element/TextRun.php
+++ b/src/PhpWord/Writer/RTF/Element/TextRun.php
@@ -34,6 +34,17 @@ public function write()
{
$writer = new Container($this->parentWriter, $this->element);
$this->getStyles();
+ if ($this->paragraphStyle !== null && $this->paragraphStyle->hasPageBreakBefore()) {
+ $sect = $this->element->getParent();
+ if ($sect instanceof \PhpOffice\PhpWord\Element\Section) {
+ $elems = $sect->getElements();
+ if ($elems[0] === $this->element) {
+ $pStyle = clone $this->paragraphStyle;
+ $pStyle->setPageBreakBefore(false);
+ $this->paragraphStyle = $pStyle;
+ }
+ }
+ }
$content = '';
$content .= $this->writeOpening();
diff --git a/tests/PhpWordTests/Writer/HTML/Sample36RtlTest.php b/tests/PhpWordTests/Writer/HTML/Sample36RtlTest.php
new file mode 100644
index 0000000000..0acda2abdb
--- /dev/null
+++ b/tests/PhpWordTests/Writer/HTML/Sample36RtlTest.php
@@ -0,0 +1,78 @@
+addSection();
+
+ $textrun = $section->addTextRun();
+ $textrun->addText('This is a Left to Right paragraph.');
+
+ $textrun = $section->addTextRun(['alignment' => Jc::END]);
+ $textrun->addText('سلام این یک پاراگراف راست به چپ است', ['rtl' => true]);
+
+ $section->addText('Table visually presented as RTL');
+ $style = ['rtl' => true, 'size' => 12];
+ $tableStyle = [
+ 'borderSize' => 6,
+ 'borderColor' => '000000',
+ 'width' => 5000,
+ 'unit' => TblWidth::PERCENT,
+ 'bidiVisual' => true,
+ ];
+
+ $table = $section->addTable($tableStyle);
+ $cellHCentered = ['alignment' => Jc::CENTER];
+ $cellHEnd = ['alignment' => Jc::END];
+ $cellVCentered = ['valign' => VerticalJc::CENTER];
+
+ //Vidually bidirectinal table
+ $table->addRow();
+ $cell = $table->addCell(1500, $cellVCentered);
+ $textrun = $cell->addTextRun($cellHCentered);
+ $textrun->addText('ردیف', $style);
+
+ $cell = $table->addCell(2000);
+ $textrun = $cell->addTextRun($cellHEnd);
+ $textrun->addText('سوالات', $style);
+
+ $cell = $table->addCell(1000, $cellVCentered);
+ $textrun = $cell->addTextRun($cellHCentered);
+ $textrun->addText('بارم', $style);
+ $writer = new HTML($phpWord);
+ $content = $writer->getContent();
+ $expected = 'hash = spl_object_id($this);
// Open temporary storage
if ($pTemporaryStorage == self::STORAGE_MEMORY) {
$this->openMemory();
@@ -90,14 +94,34 @@ public function __destruct()
if (empty($this->tempFileName)) {
return;
}
+ // This is needed only for Php7.1/2/3.
+ if ($this->hash !== spl_object_id($this)) {
+ throw new WordException('Unserialize not permitted1'); // @codeCoverageIgnore
+ }
@unlink($this->tempFileName);
}
+ /*
public function __wakeup(): void
{
$this->tempFileName = '';
- throw new WordException('Unserialize not permitted');
+ throw new WordException('Unserialize not permitted2');
+ }
+ */
+
+ /**
+ * Unserialization can cause security exploit - don't allow it.
+ * __wakeup could also be used, but that's compile-time-deprecated for 8.5.
+ * But Php7.1/2/3 don't recognize unserialize. So, experiment for now.
+ *
+ * @param mixed[] $data
+ */
+ public function __unserialize(array $data): void
+ {
+ $this->tempFileName = '';
+
+ throw new WordException('Unserialize not permitted3');
}
/**
From 01ed5587be901db8bd4ac5e753088b6c02323193 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sat, 6 Sep 2025 18:17:28 -0700
Subject: [PATCH 053/153] Try Again
---
.github/workflows/php.yml | 2 +-
src/PhpWord/Shared/XMLWriter.php | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index 18a181541e..dde30ddddb 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -93,7 +93,7 @@ jobs:
with:
php-version: ${{ matrix.php }}
extensions: dom, gd, mbstring, xml, zip
- coverage: ${{ (matrix.php == '7.3') && 'xdebug' || 'none' }}
+ coverage: ${{ (matrix.php == '8.3') && 'xdebug' || 'none' }}
- uses: actions/checkout@v2
diff --git a/src/PhpWord/Shared/XMLWriter.php b/src/PhpWord/Shared/XMLWriter.php
index ce919676b5..46f2b1eaac 100644
--- a/src/PhpWord/Shared/XMLWriter.php
+++ b/src/PhpWord/Shared/XMLWriter.php
@@ -101,14 +101,13 @@ public function __destruct()
@unlink($this->tempFileName);
}
- /*
+ /** @codeCoverageIgnore */
public function __wakeup(): void
{
$this->tempFileName = '';
throw new WordException('Unserialize not permitted2');
}
- */
/**
* Unserialization can cause security exploit - don't allow it.
From 287fbe1ff4e7a0e7250dca17cb4592c4a7ccdd78 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sat, 6 Sep 2025 19:20:55 -0700
Subject: [PATCH 054/153] Coverage Tweaks
---
src/PhpWord/Shared/XMLReader.php | 4 +-
src/PhpWord/TemplateProcessor.php | 4 +-
src/PhpWord/Writer/HTML/Style/Paragraph.php | 55 ++++++++-------------
3 files changed, 25 insertions(+), 38 deletions(-)
diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php
index ec41f2d674..7ffe5413d4 100644
--- a/src/PhpWord/Shared/XMLReader.php
+++ b/src/PhpWord/Shared/XMLReader.php
@@ -91,12 +91,12 @@ public function getDomFromZip($zipFile, $xmlFile)
public function getDomFromString($content)
{
if (\PHP_VERSION_ID < 80000) {
- $originalLibXMLEntityValue = libxml_disable_entity_loader(true);
+ $originalLibXMLEntityValue = libxml_disable_entity_loader(true); //* @codeCoverageIgnore
}
$this->dom = new DOMDocument();
$this->dom->loadXML($content);
if (\PHP_VERSION_ID < 80000) {
- libxml_disable_entity_loader($originalLibXMLEntityValue);
+ libxml_disable_entity_loader($originalLibXMLEntityValue); //* @codeCoverageIgnore
}
return $this->dom;
diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php
index dfe91ca489..effc83f3c4 100644
--- a/src/PhpWord/TemplateProcessor.php
+++ b/src/PhpWord/TemplateProcessor.php
@@ -189,7 +189,7 @@ protected function readPartWithRels($fileName)
protected function transformSingleXml($xml, $xsltProcessor)
{
if (\PHP_VERSION_ID < 80000) {
- $orignalLibEntityLoader = libxml_disable_entity_loader(true);
+ $orignalLibEntityLoader = libxml_disable_entity_loader(true); //* @codeCoverageIgnore
}
$domDocument = new DOMDocument();
if (false === $domDocument->loadXML($xml)) {
@@ -201,7 +201,7 @@ protected function transformSingleXml($xml, $xsltProcessor)
throw new Exception('Could not transform the given XML document.');
}
if (\PHP_VERSION_ID < 80000) {
- libxml_disable_entity_loader($orignalLibEntityLoader);
+ libxml_disable_entity_loader($orignalLibEntityLoader); //* @codeCoverageIgnore
}
return $transformedXml;
diff --git a/src/PhpWord/Writer/HTML/Style/Paragraph.php b/src/PhpWord/Writer/HTML/Style/Paragraph.php
index 22267e7b18..84cfb47427 100644
--- a/src/PhpWord/Writer/HTML/Style/Paragraph.php
+++ b/src/PhpWord/Writer/HTML/Style/Paragraph.php
@@ -29,6 +29,19 @@
*/
class Paragraph extends AbstractStyle
{
+ const ALIGNMENT_MAP = [
+ Jc::CENTER => 'center',
+ Jc::MEDIUM_KASHIDA => 'right',
+ Jc::HIGH_KASHIDA => 'right',
+ Jc::LOW_KASHIDA => 'right',
+ Jc::RIGHT => 'right',
+ Jc::BOTH => 'justify',
+ Jc::DISTRIBUTE => 'justify',
+ Jc::THAI_DISTRIBUTE => 'justify',
+ Jc::JUSTIFY => 'justify',
+ Jc::LEFT => 'left',
+ ];
+
/**
* Write style.
*
@@ -43,40 +56,14 @@ public function write()
$css = [];
// Alignment
- if ('' !== $style->getAlignment()) {
- $textAlign = '';
-
- switch ($style->getAlignment()) {
- case Jc::CENTER:
- $textAlign = 'center';
-
- break;
- case Jc::END:
- $textAlign = $style->isBidi() ? 'left' : 'right';
-
- break;
- case Jc::MEDIUM_KASHIDA:
- case Jc::HIGH_KASHIDA:
- case Jc::LOW_KASHIDA:
- case Jc::RIGHT:
- $textAlign = 'right';
-
- break;
- case Jc::BOTH:
- case Jc::DISTRIBUTE:
- case Jc::THAI_DISTRIBUTE:
- case Jc::JUSTIFY:
- $textAlign = 'justify';
-
- break;
- case Jc::LEFT:
- $textAlign = 'left';
-
- break;
- default: //all others, including Jc::START
- $textAlign = $style->isBidi() ? 'right' : 'left';
-
- break;
+ $alignment = $style->getAlignment();
+ if ('' !== $alignment) {
+ if (array_key_exists($alignment, self::ALIGNMENT_MAP)) {
+ $textAlign = self::ALIGNMENT_MAP[$alignment];
+ } elseif (Jc::END === $alignment) {
+ $textAlign = $style->isBidi() ? 'left' : 'right';
+ } else { //all others, including Jc::START
+ $textAlign = $style->isBidi() ? 'right' : 'left';
}
$css['text-align'] = $textAlign;
From 21fc5abf498651a403ab38411dfd64beb8936edc Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sat, 6 Sep 2025 21:17:23 -0700
Subject: [PATCH 055/153] More Coverage Tweaks
---
src/PhpWord/Shared/XMLReader.php | 4 ++--
src/PhpWord/TemplateProcessor.php | 4 ++--
src/PhpWord/Writer/HTML/Style/Paragraph.php | 8 +++-----
src/PhpWord/Writer/RTF/Element/Table.php | 15 ++++-----------
4 files changed, 11 insertions(+), 20 deletions(-)
diff --git a/src/PhpWord/Shared/XMLReader.php b/src/PhpWord/Shared/XMLReader.php
index 7ffe5413d4..819a755286 100644
--- a/src/PhpWord/Shared/XMLReader.php
+++ b/src/PhpWord/Shared/XMLReader.php
@@ -91,12 +91,12 @@ public function getDomFromZip($zipFile, $xmlFile)
public function getDomFromString($content)
{
if (\PHP_VERSION_ID < 80000) {
- $originalLibXMLEntityValue = libxml_disable_entity_loader(true); //* @codeCoverageIgnore
+ $originalLibXMLEntityValue = libxml_disable_entity_loader(true); // @codeCoverageIgnore
}
$this->dom = new DOMDocument();
$this->dom->loadXML($content);
if (\PHP_VERSION_ID < 80000) {
- libxml_disable_entity_loader($originalLibXMLEntityValue); //* @codeCoverageIgnore
+ libxml_disable_entity_loader($originalLibXMLEntityValue); // @codeCoverageIgnore
}
return $this->dom;
diff --git a/src/PhpWord/TemplateProcessor.php b/src/PhpWord/TemplateProcessor.php
index effc83f3c4..ffd8ba8374 100644
--- a/src/PhpWord/TemplateProcessor.php
+++ b/src/PhpWord/TemplateProcessor.php
@@ -189,7 +189,7 @@ protected function readPartWithRels($fileName)
protected function transformSingleXml($xml, $xsltProcessor)
{
if (\PHP_VERSION_ID < 80000) {
- $orignalLibEntityLoader = libxml_disable_entity_loader(true); //* @codeCoverageIgnore
+ $orignalLibEntityLoader = libxml_disable_entity_loader(true); // @codeCoverageIgnore
}
$domDocument = new DOMDocument();
if (false === $domDocument->loadXML($xml)) {
@@ -201,7 +201,7 @@ protected function transformSingleXml($xml, $xsltProcessor)
throw new Exception('Could not transform the given XML document.');
}
if (\PHP_VERSION_ID < 80000) {
- libxml_disable_entity_loader($orignalLibEntityLoader); //* @codeCoverageIgnore
+ libxml_disable_entity_loader($orignalLibEntityLoader); // @codeCoverageIgnore
}
return $transformedXml;
diff --git a/src/PhpWord/Writer/HTML/Style/Paragraph.php b/src/PhpWord/Writer/HTML/Style/Paragraph.php
index 84cfb47427..4e797c8dc9 100644
--- a/src/PhpWord/Writer/HTML/Style/Paragraph.php
+++ b/src/PhpWord/Writer/HTML/Style/Paragraph.php
@@ -58,12 +58,10 @@ public function write()
// Alignment
$alignment = $style->getAlignment();
if ('' !== $alignment) {
- if (array_key_exists($alignment, self::ALIGNMENT_MAP)) {
- $textAlign = self::ALIGNMENT_MAP[$alignment];
- } elseif (Jc::END === $alignment) {
+ if (Jc::END === $alignment) {
$textAlign = $style->isBidi() ? 'left' : 'right';
- } else { //all others, including Jc::START
- $textAlign = $style->isBidi() ? 'right' : 'left';
+ } else {
+ $textAlign = self::ALIGNMENT_MAP[$alignment] ?? ($style->isBidi() ? 'right' : 'left');
}
$css['text-align'] = $textAlign;
diff --git a/src/PhpWord/Writer/RTF/Element/Table.php b/src/PhpWord/Writer/RTF/Element/Table.php
index 3b08a5db86..7610ae33e4 100644
--- a/src/PhpWord/Writer/RTF/Element/Table.php
+++ b/src/PhpWord/Writer/RTF/Element/Table.php
@@ -175,6 +175,9 @@ private function writeCellStyle(CellStyle $cell, ?TableStyle $table): string
return $content;
}
+ const MAP_BORDER_TYPE = [
+ Border::DOTTED => '\brdrdot',
+ ];
private function writeCellBorder(string $prefix, ?string $borderStyle, int $borderSize, ?string $borderColor): string
{
if ($borderSize == 0) {
@@ -211,17 +214,7 @@ private function writeCellBorder(string $prefix, ?string $borderStyle, int $bord
* \brdremboss Emboss border.
* \brdrengrave Engrave border.
*/
- switch ($borderStyle) {
- case Border::DOTTED:
- $content .= '\brdrdot';
-
- break;
- case Border::SINGLE:
- default:
- $content .= '\brdrs';
-
- break;
- }
+ $content = self::MAP_BORDER_TYPE[$borderStyle] ?? '\brdrs';
// \brdrwN N is the width in twips (1/20 pt) of the pen used to draw the paragraph border line.
// N cannot be greater than 75.
From cb162298171715f615f53b8c7300a23b07f484c1 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sat, 6 Sep 2025 22:00:44 -0700
Subject: [PATCH 056/153] Missed a Period
---
src/PhpWord/Writer/RTF/Element/Table.php | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/PhpWord/Writer/RTF/Element/Table.php b/src/PhpWord/Writer/RTF/Element/Table.php
index 7610ae33e4..8f6f134dd5 100644
--- a/src/PhpWord/Writer/RTF/Element/Table.php
+++ b/src/PhpWord/Writer/RTF/Element/Table.php
@@ -178,6 +178,7 @@ private function writeCellStyle(CellStyle $cell, ?TableStyle $table): string
const MAP_BORDER_TYPE = [
Border::DOTTED => '\brdrdot',
];
+
private function writeCellBorder(string $prefix, ?string $borderStyle, int $borderSize, ?string $borderColor): string
{
if ($borderSize == 0) {
@@ -214,7 +215,7 @@ private function writeCellBorder(string $prefix, ?string $borderStyle, int $bord
* \brdremboss Emboss border.
* \brdrengrave Engrave border.
*/
- $content = self::MAP_BORDER_TYPE[$borderStyle] ?? '\brdrs';
+ $content .= self::MAP_BORDER_TYPE[$borderStyle] ?? '\brdrs';
// \brdrwN N is the width in twips (1/20 pt) of the pen used to draw the paragraph border line.
// N cannot be greater than 75.
From e7b2e4750e1965227ecd8c716e9ed8d58b143759 Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Sat, 6 Sep 2025 23:09:45 -0700
Subject: [PATCH 057/153] Cover 1 More Statement
---
tests/PhpWordTests/Shared/Html2402Test.php | 32 ++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/tests/PhpWordTests/Shared/Html2402Test.php b/tests/PhpWordTests/Shared/Html2402Test.php
index 043aa0361e..abb2c461cd 100644
--- a/tests/PhpWordTests/Shared/Html2402Test.php
+++ b/tests/PhpWordTests/Shared/Html2402Test.php
@@ -206,4 +206,36 @@ public function testParseTableStyleBorder2px(): void
$substring2 = 'border-top-style: dotted; border-top-color: #ff0000; border-top-width: 1.75pt; border-left-style: dotted; border-left-color: #ff0000; border-left-width: 1.75pt; border-bottom-style: dotted; border-bottom-color: #ff0000; border-bottom-width: 1.75pt; border-right-style: dotted; border-right-color: #ff0000; border-right-width: 1.75pt;';
self::assertSame(1, substr_count($content, $substring2));
}
+
+ public function testParseTableStyleBorderBadType(): void
+ {
+ $html = <<
+
+
+ | header a |
+ header b |
+ header c |
+
+
+
+ | 1 | 2 |
+ | This is bold text | | 6 |
+
+
+HTML;
+ $phpWord = new PhpWord();
+ $section = $phpWord->addSection();
+ Html::addHtml($section, $html, false, false);
+ $elements = $section->getElements();
+ $table = $elements[0];
+ self::assertInstanceOf(Table::class, $table);
+ $style = $table->getStyle();
+ self::assertInstanceOf(TableStyle::class, $style);
+ self::assertNull($style->getBorderBottomStyle());
+ self::assertSame('', $style->getBorderInsideHStyle());
+ self::assertSame('', $style->getBorderInsideVStyle());
+ self::assertNull($style->getBorderBottomSize());
+ self::assertNull($style->getBorderBottomColor());
+ }
}
From 2f34bcfd0255173b6f7585744b276f4b96ff5cfa Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Mon, 8 Sep 2025 10:57:57 -0700
Subject: [PATCH 058/153] Limit Phpstan to One Run
---
.github/workflows/php.yml | 3 +-
phpstan-baseline.neon | 222 +-----------------
phpstan.neon.dist | 25 --
src/PhpWord/Escaper/Rtf.php | 1 +
src/PhpWord/Writer/ODText/Part/Content.php | 1 -
src/PhpWord/Writer/PDF/DomPDF.php | 2 +-
.../Writer/Word2007/Part/DocumentTest.php | 2 +-
tests/bootstrap.php | 1 -
8 files changed, 6 insertions(+), 251 deletions(-)
diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml
index dde30ddddb..59f9a9a9ea 100644
--- a/.github/workflows/php.yml
+++ b/.github/workflows/php.yml
@@ -48,7 +48,8 @@ jobs:
fail-fast: false
matrix:
# Disabled PHPStan in '7.1'
- php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ #php: ['7.2', '7.3', '7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
+ php: ['8.4']
steps:
- name: Setup PHP
uses: shivammathur/setup-php@v2
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 3450b78153..153af7f2db 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -10,36 +10,16 @@ parameters:
count: 1
path: src/PhpWord/Element/AbstractContainer.php
- -
- message: "#^Parameter \\#1 \\$string of function md5 expects string, int\\<0, max\\> given\\.$#"
- count: 1
- path: src/PhpWord/Element/AbstractElement.php
-
-
message: "#^Parameter \\#2 \\$styleValue of method PhpOffice\\\\PhpWord\\\\Element\\\\AbstractElement\\:\\:setNewStyle\\(\\) expects array\\|PhpOffice\\\\PhpWord\\\\Style\\|string\\|null, array\\|PhpOffice\\\\PhpWord\\\\Style\\\\Cell\\|null given\\.$#"
count: 1
path: src/PhpWord/Element/Cell.php
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\Element\\\\Field\\:\\:setOptions\\(\\) should return PhpOffice\\\\PhpWord\\\\Element\\\\Field but returns array\\.$#"
- count: 1
- path: src/PhpWord/Element/Field.php
-
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\Element\\\\Field\\:\\:setProperties\\(\\) should return PhpOffice\\\\PhpWord\\\\Element\\\\Field but returns array\\.$#"
- count: 1
- path: src/PhpWord/Element/Field.php
-
-
message: "#^Property PhpOffice\\\\PhpWord\\\\Element\\\\Field\\:\\:\\$fontStyle \\(PhpOffice\\\\PhpWord\\\\Style\\\\Font\\|string\\) does not accept null\\.$#"
count: 1
path: src/PhpWord/Element/Field.php
- -
- message: "#^Result of \\|\\| is always true\\.$#"
- count: 1
- path: src/PhpWord/Element/Field.php
-
-
message: "#^Parameter \\#2 \\$styleValue of method PhpOffice\\\\PhpWord\\\\Element\\\\AbstractElement\\:\\:setNewStyle\\(\\) expects array\\|PhpOffice\\\\PhpWord\\\\Style\\|string\\|null, array\\|PhpOffice\\\\PhpWord\\\\Style\\\\Paragraph\\|string\\|null given\\.$#"
count: 1
@@ -65,11 +45,6 @@ parameters:
count: 1
path: src/PhpWord/Element/Image.php
- -
- message: "#^Offset 'extension' does not exist on array\\{dirname\\?\\: string, basename\\: string, extension\\?\\: string, filename\\: string\\}\\.$#"
- count: 2
- path: src/PhpWord/Element/OLEObject.php
-
-
message: "#^Property PhpOffice\\\\PhpWord\\\\Element\\\\OLEObject\\:\\:\\$icon \\(string\\) does not accept string\\|false\\.$#"
count: 1
@@ -90,16 +65,6 @@ parameters:
count: 1
path: src/PhpWord/Element/Section.php
- -
- message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\|false given\\.$#"
- count: 1
- path: src/PhpWord/Element/Section.php
-
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\Escaper\\\\Rtf\\:\\:escapeAsciiCharacter\\(\\) has no return type specified\\.$#"
- count: 1
- path: src/PhpWord/Escaper/Rtf.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Escaper\\\\Rtf\\:\\:escapeAsciiCharacter\\(\\) has parameter \\$code with no type specified\\.$#"
count: 1
@@ -115,11 +80,6 @@ parameters:
count: 1
path: src/PhpWord/Escaper/Rtf.php
- -
- message: "#^Cannot instantiate interface PhpOffice\\\\PhpWord\\\\Writer\\\\WriterInterface\\.$#"
- count: 1
- path: src/PhpWord/IOFactory.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\IOFactory\\:\\:createObject\\(\\) should return PhpOffice\\\\PhpWord\\\\Reader\\\\ReaderInterface\\|PhpOffice\\\\PhpWord\\\\Writer\\\\WriterInterface but returns object\\.$#"
count: 1
@@ -145,11 +105,6 @@ parameters:
count: 1
path: src/PhpWord/PhpWord.php
- -
- message: "#^Parameter \\#1 \\$callback of function forward_static_call_array expects callable\\(\\)\\: mixed, array\\{'PhpOffice\\\\\\\\PhpWord…', string\\} given\\.$#"
- count: 1
- path: src/PhpWord/PhpWord.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Reader\\\\AbstractReader\\:\\:openFile\\(\\) should return resource but return statement is missing\\.$#"
count: 1
@@ -160,11 +115,6 @@ parameters:
count: 1
path: src/PhpWord/Reader/HTML.php
- -
- message: "#^Offset 'textNodes' on array\\{changed\\: PhpOffice\\\\PhpWord\\\\Element\\\\TrackChange, textNodes\\: DOMNodeList\\\\} in isset\\(\\) always exists and is not nullable\\.$#"
- count: 1
- path: src/PhpWord/Reader/ODText/Content.php
-
-
message: "#^Parameter \\#2 \\$contextNode of method PhpOffice\\\\PhpWord\\\\Shared\\\\XMLReader\\:\\:getElements\\(\\) expects DOMElement\\|null, DOMNode\\|null given\\.$#"
count: 2
@@ -195,21 +145,6 @@ parameters:
count: 1
path: src/PhpWord/Reader/ReaderInterface.php
- -
- message: "#^Parameter \\#1 \\$string of function substr expects string, string\\|false given\\.$#"
- count: 2
- path: src/PhpWord/Reader/Word2007.php
-
- -
- message: "#^Parameter \\#2 \\$xmlFile of method PhpOffice\\\\PhpWord\\\\Reader\\\\Word2007\\:\\:getRels\\(\\) expects string, string\\|false given\\.$#"
- count: 1
- path: src/PhpWord/Reader/Word2007.php
-
- -
- message: "#^Parameter \\#3 \\$subject of function str_replace expects array\\|string, string\\|false given\\.$#"
- count: 1
- path: src/PhpWord/Reader/Word2007.php
-
-
message: "#^Binary operation \"/\" between string\\|null and 2 results in an error\\.$#"
count: 1
@@ -385,11 +320,6 @@ parameters:
count: 1
path: src/PhpWord/Shared/Html.php
- -
- message: "#^If condition is always true\\.$#"
- count: 1
- path: src/PhpWord/Shared/Html.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:addHtml\\(\\) has parameter \\$options with no type specified\\.$#"
count: 1
@@ -430,21 +360,11 @@ parameters:
count: 1
path: src/PhpWord/Shared/Html.php
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseStyleDeclarations\\(\\) has no return type specified\\.$#"
- count: 1
- path: src/PhpWord/Shared/Html.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:recursiveParseStylesInHierarchy\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpWord/Shared/Html.php
- -
- message: "#^Parameter \\#1 \\$attribute of static method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseStyle\\(\\) expects DOMAttr, DOMNode given\\.$#"
- count: 3
- path: src/PhpWord/Shared/Html.php
-
-
message: "#^Parameter \\#2 \\$element of static method PhpOffice\\\\PhpWord\\\\Shared\\\\Html\\:\\:parseNode\\(\\) expects PhpOffice\\\\PhpWord\\\\Element\\\\AbstractContainer, PhpOffice\\\\PhpWord\\\\Element\\\\AbstractContainer\\|PhpOffice\\\\PhpWord\\\\Element\\\\Row\\|PhpOffice\\\\PhpWord\\\\Element\\\\Table given\\.$#"
count: 1
@@ -465,21 +385,6 @@ parameters:
count: 1
path: src/PhpWord/Shared/Html.php
- -
- message: "#^Result of \\|\\| is always true\\.$#"
- count: 1
- path: src/PhpWord/Shared/Html.php
-
- -
- message: "#^Right side of && is always true\\.$#"
- count: 1
- path: src/PhpWord/Shared/Html.php
-
- -
- message: "#^Variable \\$cNodes in empty\\(\\) always exists and is not falsy\\.$#"
- count: 2
- path: src/PhpWord/Shared/Html.php
-
-
message: "#^Property PhpOffice\\\\PhpWord\\\\Shared\\\\Microsoft\\\\PasswordEncoder\\:\\:\\$encryptionMatrix has no type specified\\.$#"
count: 1
@@ -540,11 +445,6 @@ parameters:
count: 3
path: src/PhpWord/Shared/ZipArchive.php
- -
- message: "#^Comparison operation \"\\!\\=\" between array and 0 results in an error\\.$#"
- count: 1
- path: src/PhpWord/Shared/ZipArchive.php
-
-
message: "#^Constant PCLZIP_OPT_ADD_PATH not found\\.$#"
count: 2
@@ -570,21 +470,11 @@ parameters:
count: 1
path: src/PhpWord/Shared/ZipArchive.php
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\ZipArchive\\:\\:getFromName\\(\\) should return string but returns string\\|false\\.$#"
- count: 1
- path: src/PhpWord/Shared/ZipArchive.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Shared\\\\ZipArchive\\:\\:open\\(\\) should return bool but returns int\\|true\\.$#"
count: 1
path: src/PhpWord/Shared/ZipArchive.php
- -
- message: "#^Offset 'dirname' does not exist on array\\{dirname\\?\\: string, basename\\: string, extension\\?\\: string, filename\\: string\\}\\.$#"
- count: 3
- path: src/PhpWord/Shared/ZipArchive.php
-
-
message: "#^PHPDoc tag @var for variable \\$zip contains unknown class PclZip\\.$#"
count: 6
@@ -595,16 +485,6 @@ parameters:
count: 1
path: src/PhpWord/Shared/ZipArchive.php
- -
- message: "#^Parameter \\#1 \\$stream of function fclose expects resource, resource\\|false given\\.$#"
- count: 1
- path: src/PhpWord/Shared/ZipArchive.php
-
- -
- message: "#^Parameter \\#1 \\$stream of function fwrite expects resource, resource\\|false given\\.$#"
- count: 1
- path: src/PhpWord/Shared/ZipArchive.php
-
-
message: "#^Property PhpOffice\\\\PhpWord\\\\Shared\\\\ZipArchive\\:\\:\\$zip has unknown class PclZip as its type\\.$#"
count: 1
@@ -655,16 +535,6 @@ parameters:
count: 1
path: src/PhpWord/Style/AbstractStyle.php
- -
- message: "#^Parameter \\#3 \\$length of function substr expects int\\|null, int\\|false given\\.$#"
- count: 1
- path: src/PhpWord/Style/AbstractStyle.php
-
- -
- message: "#^Unreachable statement \\- code above always terminates\\.$#"
- count: 1
- path: src/PhpWord/Style/AbstractStyle.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Style\\\\Border\\:\\:getBorderSize\\(\\) should return array\\ but returns array\\\\.$#"
count: 1
@@ -710,11 +580,6 @@ parameters:
count: 1
path: src/PhpWord/Style/Chart.php
- -
- message: "#^PHPDoc tag @param has invalid value \\(string\\)\\: Unexpected token \"\\\\n \\* \", expected variable at offset 250$#"
- count: 1
- path: src/PhpWord/Style/Chart.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Style\\\\Font\\:\\:setAllCaps\\(\\) should return PhpOffice\\\\PhpWord\\\\Style\\\\Font but returns PhpOffice\\\\PhpWord\\\\Style\\\\AbstractStyle\\.$#"
count: 1
@@ -770,11 +635,6 @@ parameters:
count: 1
path: src/PhpWord/Style/Paragraph.php
- -
- message: "#^Result of && is always false\\.$#"
- count: 1
- path: src/PhpWord/Style/Paragraph.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Style\\\\Section\\:\\:setSettingValue\\(\\) should return PhpOffice\\\\PhpWord\\\\Style\\\\Section but returns PhpOffice\\\\PhpWord\\\\Style\\\\AbstractStyle\\.$#"
count: 1
@@ -960,11 +820,6 @@ parameters:
count: 1
path: src/PhpWord/TemplateProcessor.php
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:indexClonedVariables\\(\\) should return string but returns array\\, string\\|null\\>\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:prepareImageAttrs\\(\\) has no return type specified\\.$#"
count: 1
@@ -980,31 +835,11 @@ parameters:
count: 1
path: src/PhpWord/TemplateProcessor.php
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:setValueForPart\\(\\) should return string but returns array\\\\|string\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
- -
- message: "#^Method PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:setValueForPart\\(\\) should return string but returns array\\\\|string\\|null\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
-
message: "#^Parameter \\#1 \\$element of method PhpOffice\\\\PhpWord\\\\Writer\\\\Word2007\\\\Part\\\\Chart\\:\\:setElement\\(\\) expects PhpOffice\\\\PhpWord\\\\Element\\\\Chart, PhpOffice\\\\PhpWord\\\\Element\\\\AbstractElement given\\.$#"
count: 1
path: src/PhpWord/TemplateProcessor.php
- -
- message: "#^Parameter \\#2 \\$array of function implode expects array\\|null, array\\\\|string given\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
- -
- message: "#^Parameter \\#2 \\$array of function implode expects array\\|null, string given\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
-
message: "#^Property PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:\\$macroClosingChars has no type specified\\.$#"
count: 1
@@ -1015,21 +850,6 @@ parameters:
count: 1
path: src/PhpWord/TemplateProcessor.php
- -
- message: "#^Property PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:\\$tempDocumentFooters \\(array\\\\) does not accept string\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
- -
- message: "#^Property PhpOffice\\\\PhpWord\\\\TemplateProcessor\\:\\:\\$tempDocumentHeaders \\(array\\\\) does not accept string\\.$#"
- count: 1
- path: src/PhpWord/TemplateProcessor.php
-
- -
- message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#"
- count: 1
- path: src/PhpWord/Writer/AbstractWriter.php
-
-
message: "#^Method PhpOffice\\\\PhpWord\\\\Writer\\\\HTML\\\\Element\\\\AbstractElement\\:\\:write\\(\\) has no return type specified\\.$#"
count: 1
@@ -1295,11 +1115,6 @@ parameters:
count: 4
path: src/PhpWord/Writer/Word2007/Part/Chart.php
- -
- message: "#^Parameter \\#1 \\$string of function md5 expects string, int\\<0, max\\> given\\.$#"
- count: 1
- path: src/PhpWord/Writer/Word2007/Part/Numbering.php
-
-
message: "#^Parameter \\#1 \\$haystack of function strpos expects string, int given\\.$#"
count: 1
@@ -1310,11 +1125,6 @@ parameters:
count: 1
path: src/PhpWord/Writer/Word2007/Style/AbstractStyle.php
- -
- message: "#^Parameter \\#1 \\$styleName of static method PhpOffice\\\\PhpWord\\\\Style\\:\\:getStyle\\(\\) expects string, PhpOffice\\\\PhpWord\\\\Style\\\\AbstractStyle\\|string given\\.$#"
- count: 1
- path: src/PhpWord/Writer/Word2007/Style/Font.php
-
-
message: "#^Call to an undefined method object\\:\\:read\\(\\)\\.$#"
count: 1
@@ -1360,11 +1170,6 @@ parameters:
count: 2
path: tests/PhpWordTests/Element/CellTest.php
- -
- message: "#^Parameter \\#1 \\$text of method PhpOffice\\\\PhpWord\\\\Element\\\\Field\\:\\:setText\\(\\) expects PhpOffice\\\\PhpWord\\\\Element\\\\TextRun\\|string\\|null, array given\\.$#"
- count: 1
- path: tests/PhpWordTests/Element/FieldTest.php
-
-
message: "#^Method PhpOffice\\\\PhpWordTests\\\\Element\\\\ImageTest\\:\\:testImages\\(\\) has parameter \\$createFunction with no type specified\\.$#"
count: 1
@@ -1400,16 +1205,6 @@ parameters:
count: 1
path: tests/PhpWordTests/Element/ImageTest.php
- -
- message: "#^Parameter \\#1 \\$string of function md5 expects string, string\\|false given\\.$#"
- count: 1
- path: tests/PhpWordTests/Element/ImageTest.php
-
- -
- message: "#^Parameter \\#1 \\$string of function ucfirst expects string, string\\|false given\\.$#"
- count: 1
- path: tests/PhpWordTests/Element/ImageTest.php
-
-
message: "#^Parameter \\#3 \\$watermark of class PhpOffice\\\\PhpWord\\\\Element\\\\Image constructor expects bool, null given\\.$#"
count: 1
@@ -1505,11 +1300,6 @@ parameters:
count: 2
path: tests/PhpWordTests/Reader/Word2007/StyleTest.php
- -
- message: "#^Else branch is unreachable because ternary operator condition is always true\\.$#"
- count: 1
- path: tests/PhpWordTests/SettingsTest.php
-
-
message: "#^Parameter \\#2 \\$libraryBaseDir of static method PhpOffice\\\\PhpWord\\\\Settings\\:\\:setPdfRenderer\\(\\) expects string, string\\|false given\\.$#"
count: 1
@@ -1585,11 +1375,6 @@ parameters:
count: 1
path: tests/PhpWordTests/Shared/XMLWriterTest.php
- -
- message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#"
- count: 1
- path: tests/PhpWordTests/Shared/ZipArchiveTest.php
-
-
message: "#^Method PhpOffice\\\\PhpWordTests\\\\Style\\\\AbstractStyleTest\\:\\:callProtectedMethod\\(\\) has no return type specified\\.$#"
count: 1
@@ -1670,11 +1455,6 @@ parameters:
count: 1
path: tests/PhpWordTests/TemplateProcessorTest.php
- -
- message: "#^Argument of an invalid type array\\\\|false supplied for foreach, only iterables are supported\\.$#"
- count: 1
- path: tests/PhpWordTests/TestHelperDOCX.php
-
-
message: "#^Method PhpOffice\\\\PhpWordTests\\\\TestableTemplateProcesor\\:\\:__construct\\(\\) has parameter \\$mainPart with no type specified\\.$#"
count: 1
@@ -1856,7 +1636,7 @@ parameters:
# https://github.com/phpstan/phpstan/issues/8770
-
message: "#^PHPDoc tag @var with type PhpOffice\\\\PhpWord\\\\Writer\\\\EPub3\\\\Style\\\\AbstractStyle is not subtype of native type[\\sA-Za-z\\@\\\\\\/0-9\\.:]+$#"
- count: 2
+ count: 1
path: tests/PhpWordTests/Writer/EPub3/Style/AbstractStyleTest.php
# https://github.com/phpstan/phpstan/issues/8770
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index cf26b6956b..46d77b13d4 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -14,32 +14,7 @@ parameters:
- src/PhpWord/Writer/PDF/MPDF.php
bootstrapFiles:
- tests/bootstrap.php
- ## <=PHP7.4
- reportUnmatchedIgnoredErrors: false
treatPhpDocTypesAsCertain: false
ignoreErrors:
-
identifier: missingType.iterableValue
-
- ## <=PHP7.4
- -
- message: '#Parameter \#1 \$argument of class ReflectionClass constructor expects class-string\|T of object, string given.#'
- path: src/PhpWord/Element/AbstractContainer.php
- -
- message: '#Parameter \#1 \$function of function call_user_func expects callable\(\): mixed, string given.#'
- path: src/PhpWord/Element/Image.php
- -
- message: '#Parameter \#1 \$argument of class ReflectionClass constructor expects class-string\|T of object, string given.#'
- path: src/PhpWord/IOFactory.php
- -
- message: '#Parameter \#1 \$function of function forward_static_call_array expects callable\(\): mixed, array{.+, string} given.#'
- path: src/PhpWord/PhpWord.php
- -
- message: '#Parameter \#1 \$function of function call_user_func_array expects callable\(\): mixed, array{\$this\(PhpOffice\\PhpWord\\Shared\\ZipArchive\)\|PclZip\|ZipArchive, mixed} given.#'
- path: src/PhpWord/Shared/ZipArchive.php
- -
- message: '#Parameter \#1 \$function of function call_user_func_array expects callable\(\): mixed, array{PhpOffice\\PhpWord\\Writer\\PDF\\AbstractRenderer, string} given.#'
- path: src/PhpWord/Writer/PDF.php
- -
- message: '#Parameter \#1 \$argument of class ReflectionClass constructor expects class-string