@@ -183,6 +183,7 @@ protected static function parseNode($node, $element, $styles = array(), $data =
183
183
'img ' => array ('Image ' , $ node , $ element , $ styles , null , null , null ),
184
184
'br ' => array ('LineBreak ' , null , $ element , $ styles , null , null , null ),
185
185
'a ' => array ('Link ' , $ node , $ element , $ styles , null , null , null ),
186
+ 'hr ' => array ('HorizRule ' , $ node , $ element , $ styles , null , null , null ),
186
187
);
187
188
188
189
$ newElement = null ;
@@ -630,10 +631,27 @@ protected static function parseStyle($attribute, $styles)
630
631
}
631
632
break ;
632
633
case 'border ' :
633
- if (preg_match ('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+)\s+([a-z]+)/ ' , $ cValue , $ matches )) {
634
- $ styles ['borderSize ' ] = Converter::cssToPoint ($ matches [1 ]);
635
- $ styles ['borderColor ' ] = trim ($ matches [2 ], '# ' );
636
- $ styles ['borderStyle ' ] = self ::mapBorderStyle ($ matches [3 ]);
634
+ case 'border-top ' :
635
+ case 'border-bottom ' :
636
+ case 'border-right ' :
637
+ case 'border-left ' :
638
+ // must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid"
639
+ // Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC
640
+ if (preg_match ('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/ ' , $ cValue , $ matches )) {
641
+ if (false !== strpos ($ cKey , '- ' )){
642
+ $ which = explode ('- ' , $ cKey )[1 ];
643
+ $ which = ucfirst ($ which ); // e.g. bottom -> Bottom
644
+ }else {
645
+ $ which = '' ;
646
+ }
647
+ // 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
648
+ $ size = strtolower (trim ($ matches [1 ]));
649
+ // (!) BC change: up to ver. 0.17.0 Converter was incorrectly converting to points - Converter::cssToPoint($matches[1])
650
+ $ size = ($ size == '1px ' ) ? 1 : Converter::cssToTwip ($ size );
651
+ // valid variants may be e.g. borderSize, borderTopSize, borderLeftColor, etc ..
652
+ $ styles ["border {$ which }Size " ] = $ size ; // twips
653
+ $ styles ["border {$ which }Color " ] = trim ($ matches [2 ], '# ' );
654
+ $ styles ["border {$ which }Style " ] = self ::mapBorderStyle ($ matches [3 ]);
637
655
}
638
656
break ;
639
657
}
@@ -835,4 +853,39 @@ protected static function parseLink($node, $element, &$styles)
835
853
836
854
return $ element ->addLink ($ target , $ node ->textContent , $ styles ['font ' ], $ styles ['paragraph ' ]);
837
855
}
856
+
857
+ /**
858
+ * Render horizontal rule
859
+ * Note: Word rule is not the same as HTML's <hr> since it does not support width and thus neither alignment
860
+ *
861
+ * @param \DOMNode $node
862
+ * @param \PhpOffice\PhpWord\Element\AbstractContainer $element
863
+ */
864
+ protected static function parseHorizRule ($ node , $ element )
865
+ {
866
+ $ styles = self ::parseInlineStyle ($ node );
867
+
868
+ // <hr> is implemented as an empty paragraph - extending 100% inside the section
869
+ // Some properties may be controlled, e.g. <hr style="border-bottom: 3px #DDDDDD solid; margin-bottom: 0;">
870
+
871
+ $ fontStyle = $ styles + ['size ' => 3 ];
872
+
873
+ $ paragraphStyle = $ styles + [
874
+ 'lineHeight ' => 0.25 , // multiply default line height - e.g. 1, 1.5 etc
875
+ 'spacing ' => 0 , // twip
876
+ 'spaceBefore ' => 120 , // twip, 240/2 (default line height)
877
+ 'spaceAfter ' => 120 , // twip
878
+ 'borderBottomSize ' => empty ($ styles ['line-height ' ]) ? 1 : $ styles ['line-height ' ],
879
+ 'borderBottomColor ' => empty ($ styles ['color ' ]) ? '000000 ' : $ styles ['color ' ],
880
+ 'borderBottomStyle ' => 'single ' , // same as "solid"
881
+ ];
882
+
883
+ $ element ->addText ("" , $ fontStyle , $ paragraphStyle );
884
+
885
+ // Notes: <hr/> cannot be:
886
+ // - table - throws error "cannot be inside textruns", e.g. lists
887
+ // - line - that is a shape, has different behaviour
888
+ // - repeated text, e.g. underline "_", because of unpredictable line wrapping
889
+ }
890
+
838
891
}
0 commit comments