Html parser (addHtml) - support horizontal rule <hr/>

This commit is contained in:
lubosdz 2020-07-11 15:42:28 +02:00
parent e180cfe456
commit ca5f081302
2 changed files with 101 additions and 8 deletions

View File

@ -183,6 +183,7 @@ class Html
'img' => array('Image', $node, $element, $styles, null, null, null),
'br' => array('LineBreak', null, $element, $styles, null, null, null),
'a' => array('Link', $node, $element, $styles, null, null, null),
'hr' => array('HorizRule', $node, $element, $styles, null, null, null),
);
$newElement = null;
@ -630,10 +631,27 @@ class Html
}
break;
case 'border':
if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+)\s+([a-z]+)/', $cValue, $matches)) {
$styles['borderSize'] = Converter::cssToPoint($matches[1]);
$styles['borderColor'] = trim($matches[2], '#');
$styles['borderStyle'] = self::mapBorderStyle($matches[3]);
case 'border-top':
case 'border-bottom':
case 'border-right':
case 'border-left':
// must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid"
// Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC
if (preg_match('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/', $cValue, $matches)) {
if(false !== strpos($cKey, '-')){
$which = explode('-', $cKey)[1];
$which = ucfirst($which); // e.g. bottom -> Bottom
}else{
$which = '';
}
// normalization: in HTML 1px means tinest possible line width, so we cannot convert 1px -> 15 twips, coz line'd be bold, we use smallest twip instead
$size = strtolower(trim($matches[1]));
// (!) BC change: up to ver. 0.17.0 Converter was incorrectly converting to points - Converter::cssToPoint($matches[1])
$size = ($size == '1px') ? 1 : Converter::cssToTwip($size);
// valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc ..
$styles["border{$which}Size"] = $size; // twips
$styles["border{$which}Color"] = trim($matches[2], '#');
$styles["border{$which}Style"] = self::mapBorderStyle($matches[3]);
}
break;
}
@ -835,4 +853,39 @@ class Html
return $element->addLink($target, $node->textContent, $styles['font'], $styles['paragraph']);
}
/**
* Render horizontal rule
* Note: Word rule is not the same as HTML's <hr> since it does not support width and thus neither alignment
*
* @param \DOMNode $node
* @param \PhpOffice\PhpWord\Element\AbstractContainer $element
*/
protected static function parseHorizRule($node, $element)
{
$styles = self::parseInlineStyle($node);
// <hr> is implemented as an empty paragraph - extending 100% inside the section
// Some properties may be controlled, e.g. <hr style="border-bottom: 3px #DDDDDD solid; margin-bottom: 0;">
$fontStyle = $styles + ['size' => 3];
$paragraphStyle = $styles + [
'lineHeight' => 0.25, // multiply default line height - e.g. 1, 1.5 etc
'spacing' => 0, // twip
'spaceBefore' => 120, // twip, 240/2 (default line height)
'spaceAfter' => 120, // twip
'borderBottomSize' => empty($styles['line-height']) ? 1 : $styles['line-height'],
'borderBottomColor' => empty($styles['color']) ? '000000' : $styles['color'],
'borderBottomStyle' => 'single', // same as "solid"
];
$element->addText("", $fontStyle, $paragraphStyle);
// Notes: <hr/> cannot be:
// - table - throws error "cannot be inside textruns", e.g. lists
// - line - that is a shape, has different behaviour
// - repeated text, e.g. underline "_", because of unpredictable line wrapping
}
}

View File

@ -702,6 +702,9 @@ HTML;
$this->assertEquals('dxa', $doc->getElement($xpath)->getAttribute('w:type'));
}
/**
* Test parsing background color for table rows and table cellspacing
*/
public function testParseCellspacingRowBgColor()
{
$phpWord = new \PhpOffice\PhpWord\PhpWord();
@ -726,10 +729,6 @@ HTML;
Html::addHtml($section, $html);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
// uncomment to see results
file_put_contents('./table_src.html', $html);
file_put_contents('./table_result_'.time().'.docx', file_get_contents( TestHelperDOCX::getFile() ) );
$xpath = '/w:document/w:body/w:tbl/w:tblPr/w:tblCellSpacing';
$this->assertTrue($doc->elementExists($xpath));
$this->assertEquals(3 * 15, $doc->getElement($xpath)->getAttribute('w:w'));
@ -744,4 +743,45 @@ HTML;
$this->assertEquals('FF0000', $doc->getElement($xpath)->getAttribute('w:fill'));
}
/**
* Parse horizontal rule
*/
public function testParseHorizRule()
{
$phpWord = new \PhpOffice\PhpWord\PhpWord();
$section = $phpWord->addSection();
// borders & backgrounds are here just for better visual comparison
$html = <<<HTML
<p>Simple default rule:</p>
<hr/>
<p>Custom style rule:</p>
<hr style="margin-top: 30px; margin-bottom: 0; border-bottom: 5px lightblue solid;" />
<p>END</p>
HTML;
Html::addHtml($section, $html);
$doc = TestHelperDOCX::getDocument($phpWord, 'Word2007');
// default rule
$xpath = '/w:document/w:body/w:p[2]/w:pPr/w:pBdr/w:bottom';
$this->assertTrue($doc->elementExists($xpath));
$this->assertEquals('single', $doc->getElement($xpath)->getAttribute('w:val')); // solid
$this->assertEquals('1', $doc->getElement($xpath)->getAttribute('w:sz')); // 1 twip
$this->assertEquals('000000', $doc->getElement($xpath)->getAttribute('w:color')); // black
// custom style rule
$xpath = '/w:document/w:body/w:p[4]/w:pPr/w:pBdr/w:bottom';
$this->assertTrue($doc->elementExists($xpath));
$this->assertEquals('single', $doc->getElement($xpath)->getAttribute('w:val'));
$this->assertEquals(5 * 15, $doc->getElement($xpath)->getAttribute('w:sz'));
$this->assertEquals('lightblue', $doc->getElement($xpath)->getAttribute('w:color'));
$xpath = '/w:document/w:body/w:p[4]/w:pPr/w:spacing';
$this->assertTrue($doc->elementExists($xpath));
$this->assertEquals(22.5, $doc->getElement($xpath)->getAttribute('w:before'));
$this->assertEquals(0, $doc->getElement($xpath)->getAttribute('w:after'));
$this->assertEquals(240, $doc->getElement($xpath)->getAttribute('w:line'));
}
}