diff --git a/docs/changes/2.x/2.0.0.md b/docs/changes/2.x/2.0.0.md
index c5fbaf4b52..6f10bb171e 100644
--- a/docs/changes/2.x/2.0.0.md
+++ b/docs/changes/2.x/2.0.0.md
@@ -9,6 +9,7 @@
### Bug fixes
- MsDoc Reader : Correct Font Size Calculation by [@oleibman](https://github.com/oleibman) fixing [#2526](https://github.com/PHPOffice/PHPWord/issues/2526) in [#2531](https://github.com/PHPOffice/PHPWord/pull/2531)
+- Html Reader : Process Titles as Headings not Paragraphs [@0b10011](https://github.com/0b10011) and [@oleibman](https://github.com/oleibman) Issue [#1692](https://github.com/PHPOffice/PHPWord/issues/1692) PR [#2533](https://github.com/PHPOffice/PHPWord/pull/2533)
- TemplateProcessor Persist File After Destruct [@oleibman](https://github.com/oleibman) fixing [#2539](https://github.com/PHPOffice/PHPWord/issues/2539) in [#2545](https://github.com/PHPOffice/PHPWord/pull/2545)
- bug: TemplateProcessor fix multiline values [@gimler](https://github.com/gimler) fixing [#268](https://github.com/PHPOffice/PHPWord/issues/268), [#2323](https://github.com/PHPOffice/PHPWord/issues/2323) and [#2486](https://github.com/PHPOffice/PHPWord/issues/2486) in [#2522](https://github.com/PHPOffice/PHPWord/pull/2522)
diff --git a/src/PhpWord/Shared/Html.php b/src/PhpWord/Shared/Html.php
index 2022f7da09..6be54b37a7 100644
--- a/src/PhpWord/Shared/Html.php
+++ b/src/PhpWord/Shared/Html.php
@@ -25,6 +25,7 @@
use PhpOffice\PhpWord\Element\AbstractContainer;
use PhpOffice\PhpWord\Element\Row;
use PhpOffice\PhpWord\Element\Table;
+use PhpOffice\PhpWord\Element\TextRun;
use PhpOffice\PhpWord\Settings;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\SimpleType\NumberFormat;
@@ -208,12 +209,12 @@ protected static function parseNode($node, $element, $styles = [], $data = []):
$nodes = [
// $method $node $element $styles $data $argument1 $argument2
'p' => ['Paragraph', $node, $element, $styles, null, null, null],
- 'h1' => ['Heading', null, $element, $styles, null, 'Heading1', null],
- 'h2' => ['Heading', null, $element, $styles, null, 'Heading2', null],
- 'h3' => ['Heading', null, $element, $styles, null, 'Heading3', null],
- 'h4' => ['Heading', null, $element, $styles, null, 'Heading4', null],
- 'h5' => ['Heading', null, $element, $styles, null, 'Heading5', null],
- 'h6' => ['Heading', null, $element, $styles, null, 'Heading6', null],
+ 'h1' => ['Heading', $node, $element, $styles, null, 'Heading1', null],
+ 'h2' => ['Heading', $node, $element, $styles, null, 'Heading2', null],
+ 'h3' => ['Heading', $node, $element, $styles, null, 'Heading3', null],
+ 'h4' => ['Heading', $node, $element, $styles, null, 'Heading4', null],
+ 'h5' => ['Heading', $node, $element, $styles, null, 'Heading5', null],
+ 'h6' => ['Heading', $node, $element, $styles, null, 'Heading6', null],
'#text' => ['Text', $node, $element, $styles, null, null, null],
'strong' => ['Property', null, null, $styles, null, 'bold', true],
'b' => ['Property', null, null, $styles, null, 'bold', true],
@@ -339,21 +340,22 @@ protected static function parseInput($node, $element, &$styles): void
/**
* Parse heading node.
*
- * @param \PhpOffice\PhpWord\Element\AbstractContainer $element
- * @param array &$styles
- * @param string $argument1 Name of heading style
- *
- * @return \PhpOffice\PhpWord\Element\TextRun
- *
* @todo Think of a clever way of defining header styles, now it is only based on the assumption, that
* Heading1 - Heading6 are already defined somewhere
*/
- protected static function parseHeading($element, &$styles, $argument1)
+ protected static function parseHeading(DOMNode $node, AbstractContainer $element, array &$styles, string $headingStyle): TextRun
{
- $styles['paragraph'] = $argument1;
- $newElement = $element->addTextRun($styles['paragraph']);
+ self::parseInlineStyle($node, $styles['font']);
+ // Create a TextRun to hold styles and text
+ $styles['paragraph'] = $headingStyle;
+ $textRun = new TextRun($styles['paragraph']);
- return $newElement;
+ // Create a title with level corresponding to number in heading style
+ // (Eg, Heading1 = 1)
+ $element->addTitle($textRun, (int) ltrim($headingStyle, 'Heading'));
+
+ // Return TextRun so children are parsed
+ return $textRun;
}
/**
diff --git a/src/PhpWord/Writer/HTML/Element/Title.php b/src/PhpWord/Writer/HTML/Element/Title.php
index 65e6cb090b..6454b45cf9 100644
--- a/src/PhpWord/Writer/HTML/Element/Title.php
+++ b/src/PhpWord/Writer/HTML/Element/Title.php
@@ -17,7 +17,10 @@
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;
/**
* TextRun element HTML writer.
@@ -33,7 +36,7 @@ class Title extends AbstractElement
*/
public function write()
{
- if (!$this->element instanceof \PhpOffice\PhpWord\Element\Title) {
+ if (!$this->element instanceof PhpWordTitle) {
return '';
}
@@ -46,8 +49,14 @@ public function write()
$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() . '"';
+ }
- $content = "<{$tag}>{$text}{$tag}>" . PHP_EOL;
+ $content = "<{$tag}{$css}>{$text}{$tag}>" . PHP_EOL;
return $content;
}
diff --git a/src/PhpWord/Writer/HTML/Part/Head.php b/src/PhpWord/Writer/HTML/Part/Head.php
index 0f3f86e3d2..17530d1e82 100644
--- a/src/PhpWord/Writer/HTML/Part/Head.php
+++ b/src/PhpWord/Writer/HTML/Part/Head.php
@@ -90,17 +90,16 @@ private function writeStyles(): string
'font-family' => $this->getFontFamily(Settings::getDefaultFontName(), $this->getParentWriter()->getDefaultGenericFont()),
'font-size' => Settings::getDefaultFontSize() . 'pt',
];
- // Mpdf sometimes needs separate tag for body; doesn't harm others.
- $bodyarray = $astarray;
$defaultWhiteSpace = $this->getParentWriter()->getDefaultWhiteSpace();
if ($defaultWhiteSpace) {
$astarray['white-space'] = $defaultWhiteSpace;
}
+ $bodyarray = $astarray;
foreach ([
'body' => $bodyarray,
- '*' => $astarray,
+ //'*' => $astarray,
'a.NoteRef' => [
'text-decoration' => 'none',
],
@@ -137,8 +136,8 @@ private function writeStyles(): string
$style = $styleParagraph;
} else {
$name = '.' . $name;
+ $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
}
- $css .= "{$name} {" . $styleWriter->write() . '}' . PHP_EOL;
}
if ($style instanceof Paragraph) {
$styleWriter = new ParagraphStyleWriter($style);
diff --git a/tests/PhpWordTests/Shared/HtmlHeadingsTest.php b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
new file mode 100644
index 0000000000..4704e8b3e1
--- /dev/null
+++ b/tests/PhpWordTests/Shared/HtmlHeadingsTest.php
@@ -0,0 +1,66 @@
+addTitleStyle(1, ['size' => 20]);
+ $section = $originalDoc->addSection();
+ $expectedStrings = [];
+ $section->addTitle('Title 1', 1);
+ $expectedStrings[] = '
Title 1
';
+ for ($i = 2; $i <= 6; ++$i) {
+ $textRun = new TextRun();
+ $textRun->addText('Title ');
+ $textRun->addText("$i", ['italic' => true]);
+ $section->addTitle($textRun, $i);
+ $expectedStrings[] = "Title $i";
+ }
+ $writer = new HtmlWriter($originalDoc);
+ $content = $writer->getContent();
+ foreach ($expectedStrings as $expectedString) {
+ self::assertStringContainsString($expectedString, $content);
+ }
+
+ $newDoc = new PhpWord();
+ $newSection = $newDoc->addSection();
+ 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));
+ }
+}
diff --git a/tests/PhpWordTests/Writer/HTML/FontTest.php b/tests/PhpWordTests/Writer/HTML/FontTest.php
index 442c2639c9..08a8fca6a4 100644
--- a/tests/PhpWordTests/Writer/HTML/FontTest.php
+++ b/tests/PhpWordTests/Writer/HTML/FontTest.php
@@ -84,23 +84,23 @@ public function testFontNames1(): void
self::assertEquals('style5', Helper::getTextContent($xpath, '/html/body/div/p[6]/span', 'class'));
$style = Helper::getTextContent($xpath, '/html/head/style');
- $prg = preg_match('/^[*][^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
- self::assertEquals('* {font-family: \'Courier New\'; font-size: 12pt;}', $matches[0]);
+ $prg = preg_match('/^body[^\\r\\n]*/m', $style, $matches);
+ self::assertSame(1, $prg);
+ self::assertEquals('body {font-family: \'Courier New\'; font-size: 12pt;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style2 {font-family: \'Arial\'; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style5 {font-family: \'MingLiU-ExtB\'; font-size: 10pt; font-weight: bold;}', $matches[0]);
}
@@ -134,20 +134,20 @@ public function testFontNames2(): void
self::assertEquals('style4', Helper::getTextContent($xpath, '/html/body/div/p[5]/span', 'class'));
$style = Helper::getTextContent($xpath, '/html/head/style');
- $prg = preg_match('/^[*][^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
- self::assertEquals('* {font-family: \'Courier New\'; font-size: 12pt;}', $matches[0]);
+ $prg = preg_match('/^body[^\\r\\n]*/m', $style, $matches);
+ self::assertSame(1, $prg);
+ self::assertEquals('body {font-family: \'Courier New\'; font-size: 12pt;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style2 {font-family: \'Arial\', sans-serif; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style4 {font-family: \'Arial\'; font-size: 10pt;}', $matches[0]);
}
@@ -181,20 +181,20 @@ public function testFontNames3(): void
self::assertEquals('style4', Helper::getTextContent($xpath, '/html/body/div/p[5]/span', 'class'));
$style = Helper::getTextContent($xpath, '/html/head/style');
- $prg = preg_match('/^[*][^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
- self::assertEquals('* {font-family: \'Courier New\', monospace; font-size: 12pt;}', $matches[0]);
+ $prg = preg_match('/^body[^\\r\\n]*/m', $style, $matches);
+ self::assertSame(1, $prg);
+ self::assertEquals('body {font-family: \'Courier New\', monospace; font-size: 12pt;}', $matches[0]);
$prg = preg_match('/^[.]style1[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style2 {font-family: \'Arial\', sans-serif; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style4 {font-family: \'Arial\'; font-size: 10pt;}', $matches[0]);
}
@@ -221,19 +221,19 @@ public function testWhiteSpace(): void
$xpath = new DOMXPath($dom);
$style = Helper::getTextContent($xpath, '/html/head/style');
- self::assertNotFalse(preg_match('/^[*][^\\r\\n]*/m', $style, $matches));
- self::assertEquals('* {font-family: \'Arial\'; font-size: 12pt; white-space: pre-wrap;}', $matches[0]);
+ self::assertNotFalse(preg_match('/^body[^\\r\\n]*/m', $style, $matches));
+ 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::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style2 {font-family: \'Courier New\'; font-size: 10pt;}', $matches[0]);
$prg = preg_match('/^[.]style3[^\\r\\n]*/m', $style, $matches);
- self::assertNotFalse($prg);
+ self::assertSame(1, $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::assertNotFalse($prg);
+ self::assertSame(1, $prg);
self::assertEquals('.style4 {font-family: \'Courier New\'; font-size: 10pt;}', $matches[0]);
}
diff --git a/tests/PhpWordTests/Writer/HTML/Helper.php b/tests/PhpWordTests/Writer/HTML/Helper.php
index b777d4be14..555145d0d6 100644
--- a/tests/PhpWordTests/Writer/HTML/Helper.php
+++ b/tests/PhpWordTests/Writer/HTML/Helper.php
@@ -64,7 +64,7 @@ public static function getNamedItem(DOMXPath $xpath, string $query, string $name
if ($item2 === null) {
self::fail('Unexpected null return requesting item');
} else {
- $returnValue = $item2->attributes->getNamedItem($namedItem);
+ $returnVal = $item2->attributes->getNamedItem($namedItem);
}
}
@@ -94,4 +94,13 @@ public static function getAsHTML(PhpWord $phpWord, string $defaultWhiteSpace = '
return $dom;
}
+
+ public static function getHtmlString(PhpWord $phpWord, string $defaultWhiteSpace = '', string $defaultGenericFont = ''): string
+ {
+ $htmlWriter = new HTML($phpWord);
+ $htmlWriter->setDefaultWhiteSpace($defaultWhiteSpace);
+ $htmlWriter->setDefaultGenericFont($defaultGenericFont);
+
+ return $htmlWriter->getContent();
+ }
}
diff --git a/tests/PhpWordTests/Writer/HTML/PartTest.php b/tests/PhpWordTests/Writer/HTML/PartTest.php
index 9515932ac8..e919b80b5b 100644
--- a/tests/PhpWordTests/Writer/HTML/PartTest.php
+++ b/tests/PhpWordTests/Writer/HTML/PartTest.php
@@ -178,11 +178,17 @@ 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);
}
}