مرحبا here كلمة انجليزي.
'; +SharedHtml::addHtml($section, $htmlContent, false, false); + +// Save file +echo write($phpWord, basename(__FILE__, '.php'), $writers); +if (!CLI) { + include_once 'Sample_Footer.php'; +} +Settings::setDefaultRtl(false); diff --git a/src/PhpWord/PhpWord.php b/src/PhpWord/PhpWord.php index c3200fe857..11e678a1ff 100644 --- a/src/PhpWord/PhpWord.php +++ b/src/PhpWord/PhpWord.php @@ -21,6 +21,7 @@ use BadMethodCallException; use PhpOffice\PhpWord\Element\Section; use PhpOffice\PhpWord\Exception\Exception; +use PhpOffice\PhpWord\Style\Font; /** * PHPWord main class. @@ -318,9 +319,9 @@ public function setDefaultFontSize($fontSize): void * * @return Style\Paragraph */ - public function setDefaultParagraphStyle($styles) + public function setDefaultParagraphStyle($styles, ?Font $fontStyles = null) { - return Style::setDefaultParagraphStyle($styles); + return Style::setDefaultParagraphStyle($styles, $fontStyles); } /** diff --git a/src/PhpWord/Reader/Word2007/AbstractPart.php b/src/PhpWord/Reader/Word2007/AbstractPart.php index 9d49573d69..3fe94f6c9d 100644 --- a/src/PhpWord/Reader/Word2007/AbstractPart.php +++ b/src/PhpWord/Reader/Word2007/AbstractPart.php @@ -793,35 +793,46 @@ protected function readTableStyle(XMLReader $xmlReader, DOMElement $domNode) $borders = array_merge($margins, ['insideH', 'insideV']); if ($xmlReader->elementExists('w:tblPr', $domNode)) { + $tblStyleName = ''; if ($xmlReader->elementExists('w:tblPr/w:tblStyle', $domNode)) { - $style = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle'); - } else { - $styleNode = $xmlReader->getElement('w:tblPr', $domNode); - $styleDefs = []; - foreach ($margins as $side) { - $ucfSide = ucfirst($side); - $styleDefs["cellMargin$ucfSide"] = [self::READ_VALUE, "w:tblCellMar/w:$side", 'w:w']; - } - foreach ($borders as $side) { - $ucfSide = ucfirst($side); - $styleDefs["border{$ucfSide}Size"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz']; - $styleDefs["border{$ucfSide}Color"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:color']; - $styleDefs["border{$ucfSide}Style"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:val']; - } - $styleDefs['layout'] = [self::READ_VALUE, 'w:tblLayout', 'w:type']; - $styleDefs['bidiVisual'] = [self::READ_TRUE, 'w:bidiVisual']; - $styleDefs['cellSpacing'] = [self::READ_VALUE, 'w:tblCellSpacing', 'w:w']; - $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs); - - $tablePositionNode = $xmlReader->getElement('w:tblpPr', $styleNode); - if ($tablePositionNode !== null) { - $style['position'] = $this->readTablePosition($xmlReader, $tablePositionNode); - } + $tblStyleName = $xmlReader->getAttribute('w:val', $domNode, 'w:tblPr/w:tblStyle'); + } + $styleNode = $xmlReader->getElement('w:tblPr', $domNode); + $styleDefs = []; - $indentNode = $xmlReader->getElement('w:tblInd', $styleNode); - if ($indentNode !== null) { - $style['indent'] = $this->readTableIndent($xmlReader, $indentNode); - } + foreach ($margins as $side) { + $ucfSide = ucfirst($side); + $styleDefs["cellMargin$ucfSide"] = [self::READ_VALUE, "w:tblCellMar/w:$side", 'w:w']; + } + foreach ($borders as $side) { + $ucfSide = ucfirst($side); + $styleDefs["border{$ucfSide}Size"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:sz']; + $styleDefs["border{$ucfSide}Color"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:color']; + $styleDefs["border{$ucfSide}Style"] = [self::READ_VALUE, "w:tblBorders/w:$side", 'w:val']; + } + $styleDefs['layout'] = [self::READ_VALUE, 'w:tblLayout', 'w:type']; + $styleDefs['bidiVisual'] = [self::READ_TRUE, 'w:bidiVisual']; + $styleDefs['cellSpacing'] = [self::READ_VALUE, 'w:tblCellSpacing', 'w:w']; + $style = $this->readStyleDefs($xmlReader, $styleNode, $styleDefs); + + $tablePositionNode = $xmlReader->getElement('w:tblpPr', $styleNode); + if ($tablePositionNode !== null) { + $style['position'] = $this->readTablePosition($xmlReader, $tablePositionNode); + } + + $indentNode = $xmlReader->getElement('w:tblInd', $styleNode); + if ($indentNode !== null) { + $style['indent'] = $this->readTableIndent($xmlReader, $indentNode); + } + if ($xmlReader->elementExists('w:basedOn', $domNode)) { + $style['basedOn'] = $xmlReader->getAttribute('w:val', $domNode, 'w:basedOn'); + } + if ($tblStyleName !== '') { + $style['tblStyle'] = $tblStyleName; + } + // this may be unneeded + if ($xmlReader->elementExists('w:name', $domNode)) { + $style['styleName'] = $xmlReader->getAttribute('w:val', $domNode, 'w:name'); } } diff --git a/src/PhpWord/Reader/Word2007/Styles.php b/src/PhpWord/Reader/Word2007/Styles.php index d0777c3026..81aaf203c1 100644 --- a/src/PhpWord/Reader/Word2007/Styles.php +++ b/src/PhpWord/Reader/Word2007/Styles.php @@ -69,8 +69,9 @@ public function read(PhpWord $phpWord): void foreach ($nodes as $node) { $type = $xmlReader->getAttribute('w:type', $node); $name = $xmlReader->getAttribute('w:val', $node, 'w:name'); + $styleId = $xmlReader->getAttribute('w:styleId', $node); if (null === $name) { - $name = $xmlReader->getAttribute('w:styleId', $node); + $name = $styleId; } $headingMatches = []; preg_match('/Heading\s*(\d)/i', $name, $headingMatches); @@ -102,7 +103,8 @@ public function read(PhpWord $phpWord): void case 'table': $tStyle = $this->readTableStyle($xmlReader, $node); if (!empty($tStyle)) { - $phpWord->addTableStyle($name, $tStyle); + $newTable = $phpWord->addTableStyle($styleId, $tStyle); + $newTable->setStyleName($name); } break; diff --git a/src/PhpWord/Settings.php b/src/PhpWord/Settings.php index 16f49166fa..c4b7c38dfc 100644 --- a/src/PhpWord/Settings.php +++ b/src/PhpWord/Settings.php @@ -16,6 +16,8 @@ namespace PhpOffice\PhpWord; +use PhpOffice\PhpWord\SimpleType\TextDirection; + /** * PHPWord settings class. * @@ -453,6 +455,9 @@ public static function setDefaultFontSize($value): bool public static function setDefaultRtl(?bool $defaultRtl): void { self::$defaultRtl = $defaultRtl; + if ($defaultRtl === true && Style::getStyle('Normal') === null) { + Style::setDefaultParagraphStyle(['bidi' => true, 'textDirection' => TextDirection::RLTB], ['rtl' => true]); + } } public static function isDefaultRtl(): ?bool 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 170dc5dff3..9cc3abe5b7 100644 --- a/src/PhpWord/Shared/Html.php +++ b/src/PhpWord/Shared/Html.php @@ -28,10 +28,15 @@ use PhpOffice\PhpWord\Element\Row; use PhpOffice\PhpWord\Element\Table; use PhpOffice\PhpWord\Element\TextRun; +use PhpOffice\PhpWord\Metadata\DocInfo; +use PhpOffice\PhpWord\PhpWord; use PhpOffice\PhpWord\Settings; +use PhpOffice\PhpWord\SimpleType\Border; use PhpOffice\PhpWord\SimpleType\Jc; use PhpOffice\PhpWord\SimpleType\NumberFormat; +use PhpOffice\PhpWord\SimpleType\TextDirection; use PhpOffice\PhpWord\Style\Paragraph; +use Throwable; /** * Common Html functions. @@ -40,14 +45,24 @@ */ class Html { + private const SPECIAL_BORDER_WIDTHS = ['thin' => '0.5pt', 'thick' => '3.5pt', 'medium' => '2.0pt']; + 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; protected static $options; + /** @var ?DocInfo */ + protected static $docInfo; + + /** @var bool */ + private static $addbody = false; + /** * @var Css */ @@ -72,35 +87,88 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit * which could be applied when such an element occurs in the parseNode function. */ static::$options = $options; + static::$docInfo = null; + if (method_exists($element, 'getPhpWord')) { + /** @var ?PhpWord */ + $phpWord = $element->getPhpWord(); + if ($phpWord !== null) { + static::$docInfo = $phpWord->getDocInfo(); + } + } - // 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 if (\PHP_VERSION_ID < 80000) { - $orignalLibEntityLoader = libxml_disable_entity_loader(true); + $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 (Throwable $e) { + $result = false; + $exceptionMessage = $e->getMessage(); + } + if ($result === false) { + throw new Exception($exceptionMessage); + } + self::removeAnnoyingWhitespaceTextNodes($dom); static::$xpath = new DOMXPath($dom); - $node = $dom->getElementsByTagName('body'); + $node = $dom->getElementsByTagName('html'); + if (count($node) === 0 || $node->item(0) === null) { + $node = $dom->getElementsByTagName('body'); // @codeCoverageIgnore + } static::parseNode($node->item(0), $element); if (\PHP_VERSION_ID < 80000) { - libxml_disable_entity_loader($orignalLibEntityLoader); + libxml_disable_entity_loader($orignalLibEntityLoader); // @codeCoverageIgnore + } + } + + // 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. * @@ -109,14 +177,21 @@ public static function addHtml($element, $html, $fullHTML = false, $preserveWhit * * @return array */ - protected static function parseInlineStyle($node, $styles = []) + protected static function parseInlineStyle($node, &$styles) { if (XML_ELEMENT_NODE == $node->nodeType) { $attributes = $node->attributes; // get all the attributes(eg: id, class) - $attributeDir = $attributes->getNamedItem('dir'); - $attributeDirValue = $attributeDir ? $attributeDir->nodeValue : ''; - $bidi = $attributeDirValue === 'rtl'; + $bidi = false; + $attrDir = $attributes->getNamedItem('dir'); + $direction = isset($attrDir) ? $attrDir->nodeValue : ''; + if ($direction === 'rtl') { + $bidi = $styles['bidi'] = $styles['rtl'] = true; + $styles['textDirection'] = TextDirection::RLTB; + } elseif ($direction === 'ltr') { + $bidi = $styles['bidi'] = $styles['rtl'] = false; + $styles['textDirection'] = TextDirection::LRTB; + } foreach ($attributes as $attribute) { $val = $attribute->value; switch (strtolower($attribute->name)) { @@ -149,7 +224,7 @@ protected static function parseInlineStyle($node, $styles = []) break; case 'bgcolor': // tables, rows, cells e.g.header a | +header b | +header c | +
---|---|---|
1 | 2 | |
This is bold text | 6 |
header a | +header b | +header c | +
---|---|---|
1 | 2 | |
This is bold text | 6 |
20 | ' + . '20 30 | ' + . '
20 30 40 | ' + . '20 30 40 50 | ' + . '
This is bold text.
+ + +EOF; + Html::addHtml($section, $htmlContent, true, true); + self::assertSame('Testing Head Section', $phpWord->getDocInfo()->getTitle()); + self::assertSame('PhpWord Test', $phpWord->getDocInfo()->getCreator()); + self::assertSame('testing html read including meta tags', $phpWord->getDocInfo()->getDescription()); + $elements = $section->getElements(); + self::assertCount(1, $elements); + $element = $elements[0]; + self::assertInstanceOf(TextRun::class, $element); + $textElements = $element->getElements(); + self::assertCount(3, $textElements); + + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + $style = $textElement->getFontStyle(); + self::assertInstanceOf(Font::class, $style); + self::assertNotTrue($style->isBold()); + self::assertSame('This is ', $textElement->getText()); + + $textElement = $textElements[1]; + self::assertInstanceOf(Text::class, $textElement); + $style = $textElement->getFontStyle(); + self::assertInstanceOf(Font::class, $style); + self::assertTrue($style->isBold()); + self::assertSame('bold', $textElement->getText()); + + $textElement = $textElements[2]; + self::assertInstanceOf(Text::class, $textElement); + $style = $textElement->getFontStyle(); + self::assertInstanceOf(Font::class, $style); + self::assertNotTrue($style->isBold()); + self::assertSame(' text.', $textElement->getText()); + } +} diff --git a/tests/PhpWordTests/Shared/HtmlHeadingsTest.php b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php new file mode 100644 index 0000000000..331935fbae --- /dev/null +++ b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php @@ -0,0 +1,76 @@ +addTitleStyle(1, ['size' => 20]); + $section = $originalDoc->addSection(); + $expectedStrings = []; + $section->addTitle('Title 1', 1); + $expectedStrings[] = 'test1.
'; + $html .= 'test2.
'; + $html .= 'test3.
'; + Html::addHtml($section, $html); + $elements = $section->getElements(); + self::assertCount(3, $elements); + + $index = 0; + $element = $elements[$index]; + self::assertInstanceOf(TextRun::class, $element); + $paragraphStyle = $element->getParagraphStyle(); + self::assertInstanceOf(Paragraph::class, $paragraphStyle); + self::assertTrue($paragraphStyle->isBidi()); + self::assertSame('tbRl', $paragraphStyle->getTextDirection()); + $textElements = $element->getElements(); + self::assertCount(1, $textElements); + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + self::assertInstanceOf(Font::class, $textElement->getFontStyle()); + self::assertTrue($textElement->getFontStyle()->isRtl()); + + $index = 1; + $element = $elements[$index]; + self::assertInstanceOf(TextRun::class, $element); + $paragraphStyle = $element->getParagraphStyle(); + self::assertInstanceOf(Paragraph::class, $paragraphStyle); + self::assertFalse($paragraphStyle->isBidi()); + self::assertSame('lrTb', $paragraphStyle->getTextDirection()); + $textElements = $element->getElements(); + self::assertCount(1, $textElements); + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + self::assertInstanceOf(Font::class, $textElement->getFontStyle()); + self::assertFalse($textElement->getFontStyle()->isRtl()); + + $index = 2; + $element = $elements[$index]; + self::assertInstanceOf(TextRun::class, $element); + $paragraphStyle = $element->getParagraphStyle(); + self::assertInstanceOf(Paragraph::class, $paragraphStyle); + self::assertNull($paragraphStyle->isBidi()); + self::assertSame('', $paragraphStyle->getTextDirection()); + $textElements = $element->getElements(); + self::assertCount(1, $textElements); + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + self::assertInstanceOf(Font::class, $textElement->getFontStyle()); + self::assertNull($textElement->getFontStyle()->isRtl()); + } + + public function testParseHtmlDir(): void + { + $phpWord = new PhpWord(); + $section = $phpWord->addSection(); + $html = 'test1.
'; + $html .= 'test2.
'; + $html .= 'test3.
'; + Html::addHtml($section, $html); + $elements = $section->getElements(); + self::assertCount(3, $elements); + + $index = 0; + $element = $elements[$index]; + self::assertInstanceOf(TextRun::class, $element); + $paragraphStyle = $element->getParagraphStyle(); + self::assertInstanceOf(Paragraph::class, $paragraphStyle); + self::assertTrue($paragraphStyle->isBidi()); + self::assertSame('tbRl', $paragraphStyle->getTextDirection()); + $textElements = $element->getElements(); + self::assertCount(1, $textElements); + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + self::assertInstanceOf(Font::class, $textElement->getFontStyle()); + self::assertTrue($textElement->getFontStyle()->isRtl()); + + $index = 1; + $element = $elements[$index]; + self::assertInstanceOf(TextRun::class, $element); + $paragraphStyle = $element->getParagraphStyle(); + self::assertInstanceOf(Paragraph::class, $paragraphStyle); + self::assertFalse($paragraphStyle->isBidi()); + self::assertSame('lrTb', $paragraphStyle->getTextDirection()); + $textElements = $element->getElements(); + self::assertCount(1, $textElements); + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + self::assertInstanceOf(Font::class, $textElement->getFontStyle()); + self::assertFalse($textElement->getFontStyle()->isRtl()); + + $index = 2; + $element = $elements[$index]; + self::assertInstanceOf(TextRun::class, $element); + $paragraphStyle = $element->getParagraphStyle(); + self::assertInstanceOf(Paragraph::class, $paragraphStyle); + self::assertNull($paragraphStyle->isBidi()); + self::assertSame('', $paragraphStyle->getTextDirection()); + $textElements = $element->getElements(); + self::assertCount(1, $textElements); + $textElement = $textElements[0]; + self::assertInstanceOf(Text::class, $textElement); + self::assertInstanceOf(Font::class, $textElement->getFontStyle()); + self::assertNull($textElement->getFontStyle()->isRtl()); + } + + public function testCssClassNameOnPElement(): void + { + $phpWord = new PhpWord(); + $phpWord->addFontStyle('customClass', ['bold' => true], ['borderBottomSize' => 3, 'borderBottomColor' => '#00ff00', 'textDirection' => 'tbRl']); + $section = $phpWord->addSection(); + $html = 'test1.
'; + Html::addHtml($section, $html); + $doc = TestHelperDOCX::getDocument($phpWord); + $path = '/w:document/w:body/w:p'; + $paragraphPath = $path . '/w:pPr'; + $element = $doc->getElement($paragraphPath . '/w:pStyle'); + self::assertSame('customClass', $element->getAttribute('w:val')); + $textPath = $path . '/w:r/w:t'; + self::assertSame('test1.', $doc->getElement($textPath)->nodeValue); + self::assertSame('customClass', $doc->getElement($path . '/w:r/w:rPr/w:rStyle')->getAttribute('w:val')); + + // Styles + $file = 'word/styles.xml'; + $path = '/w:styles/w:style[@w:styleId="customClass"]'; + $paragraphPath = $path . '/w:pPr'; + $element = $doc->getElement($paragraphPath . '/w:pBdr/w:bottom', $file); + self::assertSame('#00ff00', $element->getAttribute('w:color')); + $element = $doc->getElement($paragraphPath . '/w:textDirection', $file); + self::assertSame('tbRl', $element->getAttribute('w:val')); + $fontPath = $path . '/w:rPr'; + $element = $doc->getElement($fontPath . '/w:b', $file); + self::assertSame('1', $element->getAttribute('w:val')); + } +} diff --git a/tests/PhpWordTests/Shared/HtmlTest.php b/tests/PhpWordTests/Shared/HtmlTest.php index 42d8aa598b..50f1e699a6 100644 --- a/tests/PhpWordTests/Shared/HtmlTest.php +++ b/tests/PhpWordTests/Shared/HtmlTest.php @@ -24,12 +24,15 @@ 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; 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\Style\Paragraph; use PhpOffice\PhpWordTests\AbstractWebServerEmbedded; @@ -55,6 +58,7 @@ protected function tearDown(): void */ public function testAddHtml(): void { + Settings::setOutputEscapingEnabled(true); $content = ''; // Default @@ -83,16 +87,31 @@ public function testAddHtml(): void // Other parts $section = $phpWord->addSection(); $content = ''; + $expectd = ''; $content .= 'Header | Content |
---|
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(); + self::assertCount(3, $textElements); + + $text = $textElements[0]; + self::assertInstanceOf(Text::class, $text); + self::assertInstanceOf(Font::class, $text->getFontStyle()); + self::assertNotTrue($text->getFontStyle()->isBold()); + + $text = $textElements[1]; + self::assertInstanceOf(Text::class, $text); + self::assertSame('boldtext', $text->getFontStyle()); + $style = Style::getStyle('boldtext'); + self::assertInstanceOf(Font::class, $style); + self::assertTrue($style->isBold()); + + $text = $textElements[2]; + self::assertInstanceOf(Text::class, $text); + self::assertInstanceOf(Font::class, $text->getFontStyle()); + self::assertNotTrue($text->getFontStyle()->isBold()); + } + /** * Test text-decoration style. */ @@ -248,7 +303,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')); } @@ -558,7 +614,7 @@ public function testParseTableAndCellWidth(): void { $phpWord = new PhpWord(); $section = $phpWord->addSection([ - 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE, + 'orientation' => Style\Section::ORIENTATION_LANDSCAPE, ]); // borders & backgrounds are here just for better visual comparison @@ -627,7 +683,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 @@ -749,7 +805,7 @@ public function testParseTableStyleAttributeInlineStyle(): void { $phpWord = new PhpWord(); $section = $phpWord->addSection([ - 'orientation' => \PhpOffice\PhpWord\Style\Section::ORIENTATION_LANDSCAPE, + 'orientation' => Style\Section::ORIENTATION_LANDSCAPE, ]); $html = '