@@ -43,6 +43,11 @@ class Html
43
43
44
44
protected static $ options ;
45
45
46
+ /**
47
+ * @var Css
48
+ */
49
+ protected static $ css ;
50
+
46
51
/**
47
52
* Add HTML parts.
48
53
*
@@ -149,6 +154,16 @@ protected static function parseInlineStyle($node, $styles = [])
149
154
}
150
155
}
151
156
157
+ $ attributeIdentifier = $ attributes ->getNamedItem ('id ' );
158
+ if ($ attributeIdentifier && self ::$ css ) {
159
+ $ styles = self ::parseStyleDeclarations (self ::$ css ->getStyle ('# ' . $ attributeIdentifier ->value ), $ styles );
160
+ }
161
+
162
+ $ attributeClass = $ attributes ->getNamedItem ('class ' );
163
+ if ($ attributeClass && self ::$ css ) {
164
+ $ styles = self ::parseStyleDeclarations (self ::$ css ->getStyle ('. ' . $ attributeClass ->value ), $ styles );
165
+ }
166
+
152
167
$ attributeStyle = $ attributes ->getNamedItem ('style ' );
153
168
if ($ attributeStyle ) {
154
169
$ styles = self ::parseStyle ($ attributeStyle , $ styles );
@@ -168,6 +183,13 @@ protected static function parseInlineStyle($node, $styles = [])
168
183
*/
169
184
protected static function parseNode ($ node , $ element , $ styles = [], $ data = []): void
170
185
{
186
+ if ($ node ->nodeName == 'style ' ) {
187
+ self ::$ css = new Css ($ node ->textContent );
188
+ self ::$ css ->process ();
189
+
190
+ return ;
191
+ }
192
+
171
193
// Populate styles array
172
194
$ styleTypes = ['font ' , 'paragraph ' , 'list ' , 'table ' , 'row ' , 'cell ' ];
173
195
foreach ($ styleTypes as $ styleType ) {
@@ -635,63 +657,71 @@ protected static function parseStyle($attribute, $styles)
635
657
{
636
658
$ properties = explode ('; ' , trim ($ attribute ->value , " \t\n\r\0\x0B; " ));
637
659
660
+ $ selectors = [];
638
661
foreach ($ properties as $ property ) {
639
662
[$ cKey , $ cValue ] = array_pad (explode (': ' , $ property , 2 ), 2 , null );
640
- $ cValue = trim ($ cValue ?? '' );
641
- $ cKey = strtolower (trim ($ cKey ));
642
- switch ($ cKey ) {
663
+ $ selectors [strtolower (trim ($ cKey ))] = trim ($ cValue ?? '' );
664
+ }
665
+
666
+ return self ::parseStyleDeclarations ($ selectors , $ styles );
667
+ }
668
+
669
+ protected static function parseStyleDeclarations (array $ selectors , array $ styles )
670
+ {
671
+ foreach ($ selectors as $ property => $ value ) {
672
+ switch ($ property ) {
643
673
case 'text-decoration ' :
644
- switch ($ cValue ) {
674
+ switch ($ value ) {
645
675
case 'underline ' :
646
676
$ styles ['underline ' ] = 'single ' ;
647
-
677
+
648
678
break ;
649
679
case 'line-through ' :
650
680
$ styles ['strikethrough ' ] = true ;
651
-
681
+
652
682
break ;
653
683
}
654
-
684
+
655
685
break ;
656
686
case 'text-align ' :
657
- $ styles ['alignment ' ] = self ::mapAlign ($ cValue );
658
-
687
+ $ styles ['alignment ' ] = self ::mapAlign ($ value );
688
+
659
689
break ;
660
690
case 'display ' :
661
- $ styles ['hidden ' ] = $ cValue === 'none ' || $ cValue === 'hidden ' ;
662
-
691
+ $ styles ['hidden ' ] = $ value === 'none ' || $ value === 'hidden ' ;
692
+
663
693
break ;
664
694
case 'direction ' :
665
- $ styles ['rtl ' ] = $ cValue === 'rtl ' ;
666
-
695
+ $ styles ['rtl ' ] = $ value === 'rtl ' ;
696
+
667
697
break ;
668
698
case 'font-size ' :
669
- $ styles ['size ' ] = Converter::cssToPoint ($ cValue );
670
-
699
+ $ styles ['size ' ] = Converter::cssToPoint ($ value );
700
+
671
701
break ;
672
702
case 'font-family ' :
673
- $ cValue = array_map ('trim ' , explode (', ' , $ cValue ));
674
- $ styles ['name ' ] = ucwords ($ cValue [0 ]);
675
-
703
+ $ value = array_map ('trim ' , explode (', ' , $ value ));
704
+ $ styles ['name ' ] = ucwords ($ value [0 ]);
705
+
676
706
break ;
677
707
case 'color ' :
678
- $ styles ['color ' ] = trim ($ cValue , '# ' );
679
-
708
+ $ styles ['color ' ] = trim ($ value , '# ' );
709
+
680
710
break ;
681
711
case 'background-color ' :
682
- $ styles ['bgColor ' ] = trim ($ cValue , '# ' );
683
-
712
+ $ styles ['bgColor ' ] = trim ($ value , '# ' );
713
+
684
714
break ;
685
715
case 'line-height ' :
686
716
$ matches = [];
687
- if ($ cValue === 'normal ' ) {
717
+ if ($ value === 'normal ' ) {
688
718
$ spacingLineRule = \PhpOffice \PhpWord \SimpleType \LineSpacingRule::AUTO ;
689
719
$ spacing = 0 ;
690
- } elseif (preg_match ('/([0-9]+\.?[0-9]*[a-z]+)/ ' , $ cValue , $ matches )) {
720
+ } elseif (preg_match ('/([0-9]+\.?[0-9]*[a-z]+)/ ' , $ value , $ matches )) {
691
721
//matches number with a unit, e.g. 12px, 15pt, 20mm, ...
692
722
$ spacingLineRule = \PhpOffice \PhpWord \SimpleType \LineSpacingRule::EXACT ;
693
723
$ spacing = Converter::cssToTwip ($ matches [1 ]);
694
- } elseif (preg_match ('/([0-9]+)%/ ' , $ cValue , $ matches )) {
724
+ } elseif (preg_match ('/([0-9]+)%/ ' , $ value , $ matches )) {
695
725
//matches percentages
696
726
$ spacingLineRule = \PhpOffice \PhpWord \SimpleType \LineSpacingRule::AUTO ;
697
727
//we are subtracting 1 line height because the Spacing writer is adding one line
@@ -700,76 +730,76 @@ protected static function parseStyle($attribute, $styles)
700
730
//any other, wich is a multiplier. E.g. 1.2
701
731
$ spacingLineRule = \PhpOffice \PhpWord \SimpleType \LineSpacingRule::AUTO ;
702
732
//we are subtracting 1 line height because the Spacing writer is adding one line
703
- $ spacing = ($ cValue * Paragraph::LINE_HEIGHT ) - Paragraph::LINE_HEIGHT ;
733
+ $ spacing = ($ value * Paragraph::LINE_HEIGHT ) - Paragraph::LINE_HEIGHT ;
704
734
}
705
735
$ styles ['spacingLineRule ' ] = $ spacingLineRule ;
706
736
$ styles ['line-spacing ' ] = $ spacing ;
707
-
737
+
708
738
break ;
709
739
case 'letter-spacing ' :
710
- $ styles ['letter-spacing ' ] = Converter::cssToTwip ($ cValue );
711
-
740
+ $ styles ['letter-spacing ' ] = Converter::cssToTwip ($ value );
741
+
712
742
break ;
713
743
case 'text-indent ' :
714
- $ styles ['indentation ' ]['firstLine ' ] = Converter::cssToTwip ($ cValue );
715
-
744
+ $ styles ['indentation ' ]['firstLine ' ] = Converter::cssToTwip ($ value );
745
+
716
746
break ;
717
747
case 'font-weight ' :
718
748
$ tValue = false ;
719
- if (preg_match ('#bold# ' , $ cValue )) {
749
+ if (preg_match ('#bold# ' , $ value )) {
720
750
$ tValue = true ; // also match bolder
721
751
}
722
752
$ styles ['bold ' ] = $ tValue ;
723
-
753
+
724
754
break ;
725
755
case 'font-style ' :
726
756
$ tValue = false ;
727
- if (preg_match ('#(?:italic|oblique)# ' , $ cValue )) {
757
+ if (preg_match ('#(?:italic|oblique)# ' , $ value )) {
728
758
$ tValue = true ;
729
759
}
730
760
$ styles ['italic ' ] = $ tValue ;
731
-
761
+
732
762
break ;
733
763
case 'margin ' :
734
- $ cValue = Converter::cssToTwip ($ cValue );
735
- $ styles ['spaceBefore ' ] = $ cValue ;
736
- $ styles ['spaceAfter ' ] = $ cValue ;
737
-
764
+ $ value = Converter::cssToTwip ($ value );
765
+ $ styles ['spaceBefore ' ] = $ value ;
766
+ $ styles ['spaceAfter ' ] = $ value ;
767
+
738
768
break ;
739
769
case 'margin-top ' :
740
- // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue )
741
- $ styles ['spaceBefore ' ] = Converter::cssToTwip ($ cValue );
742
-
770
+ // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($value )
771
+ $ styles ['spaceBefore ' ] = Converter::cssToTwip ($ value );
772
+
743
773
break ;
744
774
case 'margin-bottom ' :
745
- // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($cValue )
746
- $ styles ['spaceAfter ' ] = Converter::cssToTwip ($ cValue );
747
-
775
+ // BC change: up to ver. 0.17.0 incorrectly converted to points - Converter::cssToPoint($value )
776
+ $ styles ['spaceAfter ' ] = Converter::cssToTwip ($ value );
777
+
748
778
break ;
749
779
case 'border-color ' :
750
- self ::mapBorderColor ($ styles , $ cValue );
751
-
780
+ self ::mapBorderColor ($ styles , $ value );
781
+
752
782
break ;
753
783
case 'border-width ' :
754
- $ styles ['borderSize ' ] = Converter::cssToPoint ($ cValue );
755
-
784
+ $ styles ['borderSize ' ] = Converter::cssToPoint ($ value );
785
+
756
786
break ;
757
787
case 'border-style ' :
758
- $ styles ['borderStyle ' ] = self ::mapBorderStyle ($ cValue );
759
-
788
+ $ styles ['borderStyle ' ] = self ::mapBorderStyle ($ value );
789
+
760
790
break ;
761
791
case 'width ' :
762
- if (preg_match ('/([0-9]+[a-z]+)/ ' , $ cValue , $ matches )) {
792
+ if (preg_match ('/([0-9]+[a-z]+)/ ' , $ value , $ matches )) {
763
793
$ styles ['width ' ] = Converter::cssToTwip ($ matches [1 ]);
764
794
$ styles ['unit ' ] = \PhpOffice \PhpWord \SimpleType \TblWidth::TWIP ;
765
- } elseif (preg_match ('/([0-9]+)%/ ' , $ cValue , $ matches )) {
795
+ } elseif (preg_match ('/([0-9]+)%/ ' , $ value , $ matches )) {
766
796
$ styles ['width ' ] = $ matches [1 ] * 50 ;
767
797
$ styles ['unit ' ] = \PhpOffice \PhpWord \SimpleType \TblWidth::PERCENT ;
768
- } elseif (preg_match ('/([0-9]+)/ ' , $ cValue , $ matches )) {
798
+ } elseif (preg_match ('/([0-9]+)/ ' , $ value , $ matches )) {
769
799
$ styles ['width ' ] = $ matches [1 ];
770
800
$ styles ['unit ' ] = \PhpOffice \PhpWord \SimpleType \TblWidth::AUTO ;
771
801
}
772
-
802
+
773
803
break ;
774
804
case 'border ' :
775
805
case 'border-top ' :
@@ -778,9 +808,9 @@ protected static function parseStyle($attribute, $styles)
778
808
case 'border-left ' :
779
809
// must have exact order [width color style], e.g. "1px #0011CC solid" or "2pt green solid"
780
810
// Word does not accept shortened hex colors e.g. #CCC, only full e.g. #CCCCCC
781
- if (preg_match ('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/ ' , $ cValue , $ matches )) {
782
- if (false !== strpos ($ cKey , '- ' )) {
783
- $ tmp = explode ('- ' , $ cKey );
811
+ if (preg_match ('/([0-9]+[^0-9]*)\s+(\#[a-fA-F0-9]+|[a-zA-Z]+)\s+([a-z]+)/ ' , $ value , $ matches )) {
812
+ if (false !== strpos ($ property , '- ' )) {
813
+ $ tmp = explode ('- ' , $ property );
784
814
$ which = $ tmp [1 ];
785
815
$ which = ucfirst ($ which ); // e.g. bottom -> Bottom
786
816
} else {
@@ -799,20 +829,20 @@ protected static function parseStyle($attribute, $styles)
799
829
$ styles ["border {$ which }Color " ] = trim ($ matches [2 ], '# ' );
800
830
$ styles ["border {$ which }Style " ] = self ::mapBorderStyle ($ matches [3 ]);
801
831
}
802
-
832
+
803
833
break ;
804
834
case 'vertical-align ' :
805
835
// https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align
806
- if (preg_match ('#(?:top|bottom|middle|sub|baseline)#i ' , $ cValue , $ matches )) {
836
+ if (preg_match ('#(?:top|bottom|middle|sub|baseline)#i ' , $ value , $ matches )) {
807
837
$ styles ['valign ' ] = self ::mapAlignVertical ($ matches [0 ]);
808
838
}
809
-
839
+
810
840
break ;
811
841
case 'page-break-after ' :
812
- if ($ cValue == 'always ' ) {
842
+ if ($ value == 'always ' ) {
813
843
$ styles ['isPageBreak ' ] = true ;
814
844
}
815
-
845
+
816
846
break ;
817
847
}
818
848
}
0 commit comments