1
1
import 'package:collection/collection.dart' ;
2
+ import 'package:convert/convert.dart' ;
2
3
import 'package:csslib/parser.dart' as css_parser;
3
4
import 'package:csslib/visitor.dart' as css_visitor;
4
5
import 'package:flutter/foundation.dart' ;
@@ -629,6 +630,7 @@ class _KatexParser {
629
630
topEm: _takeStyleEm (inlineStyles, 'top' ),
630
631
marginLeftEm: _takeStyleEm (inlineStyles, 'margin-left' ),
631
632
marginRightEm: _takeStyleEm (inlineStyles, 'margin-right' ),
633
+ color: _takeStyleColor (inlineStyles, 'color' ),
632
634
// TODO handle more CSS properties
633
635
);
634
636
if (inlineStyles != null && inlineStyles.isNotEmpty) {
@@ -715,6 +717,54 @@ class _KatexParser {
715
717
_hasError = true ;
716
718
return null ;
717
719
}
720
+
721
+ /// Remove the given property from the given style map,
722
+ /// and parse as a color value.
723
+ ///
724
+ /// If the property is present but is not a valid CSS Hex color,
725
+ /// or is not one of the CSS named color, record an error
726
+ /// and return null.
727
+ ///
728
+ /// If the property is absent, return null with no error.
729
+ ///
730
+ /// If the map is null, treat it as empty.
731
+ ///
732
+ /// To produce the map this method expects, see [_parseInlineStyles] .
733
+ KatexSpanColor ? _takeStyleColor (Map <String , css_visitor.Expression >? styles, String property) {
734
+ final expression = styles? .remove (property);
735
+ if (expression == null ) return null ;
736
+
737
+ // `package:csslib` parser emits a HexColorTerm for the `color`
738
+ // attribute. It automatically resolves the named CSS colors to
739
+ // their hex values. The `HexColorTerm.value` is the hex
740
+ // encoded in an integer in the same sequence as the input hex
741
+ // string. But it also allows some non-conformant CSS hex color
742
+ // notations, like #f, #ff, #fffff, #fffffff.
743
+ // See:
744
+ // https://drafts.csswg.org/css-color/#hex-notation.
745
+ // https://github.com/dart-lang/tools/blob/2a2a2d611/pkgs/csslib/lib/parser.dart#L2714-L2743
746
+ //
747
+ // So, we try to parse the value of `color` attribute ourselves
748
+ // only allowing conformant CSS hex color notations, mapping
749
+ // named CSS colors to their corresponding values, generating a
750
+ // typed result (KatexSpanColor(r, g, b, a)) to be used later
751
+ // while rendering.
752
+ final valueStr = expression.span? .text;
753
+ if (valueStr != null ) {
754
+ if (valueStr.startsWith ('#' )) {
755
+ final color = parseCssHexColor (valueStr);
756
+ if (color != null ) return color;
757
+ } else {
758
+ final color = _cssNamedColorsMap[valueStr];
759
+ if (color != null ) return color;
760
+ }
761
+ }
762
+ assert (debugLog ('KaTeX: Unsupported value for CSS property $property ,'
763
+ ' expected a color: ${expression .toDebugString ()}' ));
764
+ unsupportedInlineCssProperties.add (property);
765
+ _hasError = true ;
766
+ return null ;
767
+ }
718
768
}
719
769
720
770
enum KatexSpanFontWeight {
@@ -732,6 +782,32 @@ enum KatexSpanTextAlign {
732
782
right,
733
783
}
734
784
785
+ class KatexSpanColor {
786
+ const KatexSpanColor (this .r, this .g, this .b, this .a);
787
+
788
+ final int r;
789
+ final int g;
790
+ final int b;
791
+ final int a;
792
+
793
+ @override
794
+ bool operator == (Object other) {
795
+ return other is KatexSpanColor &&
796
+ other.r == r &&
797
+ other.g == g &&
798
+ other.b == b &&
799
+ other.a == a;
800
+ }
801
+
802
+ @override
803
+ int get hashCode => Object .hash ('KatexSpanColor' , r, g, b, a);
804
+
805
+ @override
806
+ String toString () {
807
+ return '${objectRuntimeType (this , 'KatexSpanColor' )}($r , $g , $b , $a )' ;
808
+ }
809
+ }
810
+
735
811
@immutable
736
812
class KatexSpanStyles {
737
813
// TODO(#1674) does height actually appear on generic spans?
@@ -755,6 +831,8 @@ class KatexSpanStyles {
755
831
final KatexSpanFontStyle ? fontStyle;
756
832
final KatexSpanTextAlign ? textAlign;
757
833
834
+ final KatexSpanColor ? color;
835
+
758
836
const KatexSpanStyles ({
759
837
this .heightEm,
760
838
this .topEm,
@@ -765,6 +843,7 @@ class KatexSpanStyles {
765
843
this .fontWeight,
766
844
this .fontStyle,
767
845
this .textAlign,
846
+ this .color,
768
847
});
769
848
770
849
@override
@@ -779,6 +858,7 @@ class KatexSpanStyles {
779
858
fontWeight,
780
859
fontStyle,
781
860
textAlign,
861
+ color,
782
862
);
783
863
784
864
@override
@@ -792,7 +872,8 @@ class KatexSpanStyles {
792
872
other.fontSizeEm == fontSizeEm &&
793
873
other.fontWeight == fontWeight &&
794
874
other.fontStyle == fontStyle &&
795
- other.textAlign == textAlign;
875
+ other.textAlign == textAlign &&
876
+ other.color == color;
796
877
}
797
878
798
879
@override
@@ -807,6 +888,7 @@ class KatexSpanStyles {
807
888
if (fontWeight != null ) args.add ('fontWeight: $fontWeight ' );
808
889
if (fontStyle != null ) args.add ('fontStyle: $fontStyle ' );
809
890
if (textAlign != null ) args.add ('textAlign: $textAlign ' );
891
+ if (color != null ) args.add ('color: $color ' );
810
892
return '${objectRuntimeType (this , 'KatexSpanStyles' )}(${args .join (', ' )})' ;
811
893
}
812
894
@@ -821,6 +903,7 @@ class KatexSpanStyles {
821
903
bool fontWeight = true ,
822
904
bool fontStyle = true ,
823
905
bool textAlign = true ,
906
+ bool color = true ,
824
907
}) {
825
908
return KatexSpanStyles (
826
909
heightEm: heightEm ? this .heightEm : null ,
@@ -832,10 +915,201 @@ class KatexSpanStyles {
832
915
fontWeight: fontWeight ? this .fontWeight : null ,
833
916
fontStyle: fontStyle ? this .fontStyle : null ,
834
917
textAlign: textAlign ? this .textAlign : null ,
918
+ color: color ? this .color : null ,
835
919
);
836
920
}
837
921
}
838
922
923
+ final _hexColorRegExp =
924
+ RegExp (r'^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$' );
925
+
926
+ /// Parses the CSS hex color notation.
927
+ ///
928
+ /// See: https://drafts.csswg.org/css-color/#hex-notation
929
+ @visibleForTesting
930
+ KatexSpanColor ? parseCssHexColor (String hexStr) {
931
+ final match = _hexColorRegExp.firstMatch (hexStr);
932
+ if (match == null ) return null ;
933
+
934
+ String hexValue = match.group (1 )! ;
935
+ switch (hexValue.length) {
936
+ case 3 :
937
+ hexValue = '${hexValue [0 ]}${hexValue [0 ]}'
938
+ '${hexValue [1 ]}${hexValue [1 ]}'
939
+ '${hexValue [2 ]}${hexValue [2 ]}'
940
+ 'ff' ;
941
+ case 4 :
942
+ hexValue = '${hexValue [0 ]}${hexValue [0 ]}'
943
+ '${hexValue [1 ]}${hexValue [1 ]}'
944
+ '${hexValue [2 ]}${hexValue [2 ]}'
945
+ '${hexValue [3 ]}${hexValue [3 ]}' ;
946
+ case 6 :
947
+ hexValue += 'ff' ;
948
+ }
949
+
950
+ try {
951
+ final [r, g, b, a] = hex.decode (hexValue);
952
+ return KatexSpanColor (r, g, b, a);
953
+ } catch (_) {
954
+ return null ; // TODO(log)
955
+ }
956
+ }
957
+
958
+ // CSS named colors: https://drafts.csswg.org/css-color/#named-colors
959
+ // Map adapted from the following source file:
960
+ // https://github.com/w3c/csswg-drafts/blob/1942d0918/css-color-4/Overview.bs#L1562-L1859
961
+ const _cssNamedColorsMap = {
962
+ 'transparent' : KatexSpanColor (0 , 0 , 0 , 0 ), // https://drafts.csswg.org/css-color/#transparent-color
963
+ 'aliceblue' : KatexSpanColor (240 , 248 , 255 , 255 ),
964
+ 'antiquewhite' : KatexSpanColor (250 , 235 , 215 , 255 ),
965
+ 'aqua' : KatexSpanColor (0 , 255 , 255 , 255 ),
966
+ 'aquamarine' : KatexSpanColor (127 , 255 , 212 , 255 ),
967
+ 'azure' : KatexSpanColor (240 , 255 , 255 , 255 ),
968
+ 'beige' : KatexSpanColor (245 , 245 , 220 , 255 ),
969
+ 'bisque' : KatexSpanColor (255 , 228 , 196 , 255 ),
970
+ 'black' : KatexSpanColor (0 , 0 , 0 , 255 ),
971
+ 'blanchedalmond' : KatexSpanColor (255 , 235 , 205 , 255 ),
972
+ 'blue' : KatexSpanColor (0 , 0 , 255 , 255 ),
973
+ 'blueviolet' : KatexSpanColor (138 , 43 , 226 , 255 ),
974
+ 'brown' : KatexSpanColor (165 , 42 , 42 , 255 ),
975
+ 'burlywood' : KatexSpanColor (222 , 184 , 135 , 255 ),
976
+ 'cadetblue' : KatexSpanColor (95 , 158 , 160 , 255 ),
977
+ 'chartreuse' : KatexSpanColor (127 , 255 , 0 , 255 ),
978
+ 'chocolate' : KatexSpanColor (210 , 105 , 30 , 255 ),
979
+ 'coral' : KatexSpanColor (255 , 127 , 80 , 255 ),
980
+ 'cornflowerblue' : KatexSpanColor (100 , 149 , 237 , 255 ),
981
+ 'cornsilk' : KatexSpanColor (255 , 248 , 220 , 255 ),
982
+ 'crimson' : KatexSpanColor (220 , 20 , 60 , 255 ),
983
+ 'cyan' : KatexSpanColor (0 , 255 , 255 , 255 ),
984
+ 'darkblue' : KatexSpanColor (0 , 0 , 139 , 255 ),
985
+ 'darkcyan' : KatexSpanColor (0 , 139 , 139 , 255 ),
986
+ 'darkgoldenrod' : KatexSpanColor (184 , 134 , 11 , 255 ),
987
+ 'darkgray' : KatexSpanColor (169 , 169 , 169 , 255 ),
988
+ 'darkgreen' : KatexSpanColor (0 , 100 , 0 , 255 ),
989
+ 'darkgrey' : KatexSpanColor (169 , 169 , 169 , 255 ),
990
+ 'darkkhaki' : KatexSpanColor (189 , 183 , 107 , 255 ),
991
+ 'darkmagenta' : KatexSpanColor (139 , 0 , 139 , 255 ),
992
+ 'darkolivegreen' : KatexSpanColor (85 , 107 , 47 , 255 ),
993
+ 'darkorange' : KatexSpanColor (255 , 140 , 0 , 255 ),
994
+ 'darkorchid' : KatexSpanColor (153 , 50 , 204 , 255 ),
995
+ 'darkred' : KatexSpanColor (139 , 0 , 0 , 255 ),
996
+ 'darksalmon' : KatexSpanColor (233 , 150 , 122 , 255 ),
997
+ 'darkseagreen' : KatexSpanColor (143 , 188 , 143 , 255 ),
998
+ 'darkslateblue' : KatexSpanColor (72 , 61 , 139 , 255 ),
999
+ 'darkslategray' : KatexSpanColor (47 , 79 , 79 , 255 ),
1000
+ 'darkslategrey' : KatexSpanColor (47 , 79 , 79 , 255 ),
1001
+ 'darkturquoise' : KatexSpanColor (0 , 206 , 209 , 255 ),
1002
+ 'darkviolet' : KatexSpanColor (148 , 0 , 211 , 255 ),
1003
+ 'deeppink' : KatexSpanColor (255 , 20 , 147 , 255 ),
1004
+ 'deepskyblue' : KatexSpanColor (0 , 191 , 255 , 255 ),
1005
+ 'dimgray' : KatexSpanColor (105 , 105 , 105 , 255 ),
1006
+ 'dimgrey' : KatexSpanColor (105 , 105 , 105 , 255 ),
1007
+ 'dodgerblue' : KatexSpanColor (30 , 144 , 255 , 255 ),
1008
+ 'firebrick' : KatexSpanColor (178 , 34 , 34 , 255 ),
1009
+ 'floralwhite' : KatexSpanColor (255 , 250 , 240 , 255 ),
1010
+ 'forestgreen' : KatexSpanColor (34 , 139 , 34 , 255 ),
1011
+ 'fuchsia' : KatexSpanColor (255 , 0 , 255 , 255 ),
1012
+ 'gainsboro' : KatexSpanColor (220 , 220 , 220 , 255 ),
1013
+ 'ghostwhite' : KatexSpanColor (248 , 248 , 255 , 255 ),
1014
+ 'gold' : KatexSpanColor (255 , 215 , 0 , 255 ),
1015
+ 'goldenrod' : KatexSpanColor (218 , 165 , 32 , 255 ),
1016
+ 'gray' : KatexSpanColor (128 , 128 , 128 , 255 ),
1017
+ 'green' : KatexSpanColor (0 , 128 , 0 , 255 ),
1018
+ 'greenyellow' : KatexSpanColor (173 , 255 , 47 , 255 ),
1019
+ 'grey' : KatexSpanColor (128 , 128 , 128 , 255 ),
1020
+ 'honeydew' : KatexSpanColor (240 , 255 , 240 , 255 ),
1021
+ 'hotpink' : KatexSpanColor (255 , 105 , 180 , 255 ),
1022
+ 'indianred' : KatexSpanColor (205 , 92 , 92 , 255 ),
1023
+ 'indigo' : KatexSpanColor (75 , 0 , 130 , 255 ),
1024
+ 'ivory' : KatexSpanColor (255 , 255 , 240 , 255 ),
1025
+ 'khaki' : KatexSpanColor (240 , 230 , 140 , 255 ),
1026
+ 'lavender' : KatexSpanColor (230 , 230 , 250 , 255 ),
1027
+ 'lavenderblush' : KatexSpanColor (255 , 240 , 245 , 255 ),
1028
+ 'lawngreen' : KatexSpanColor (124 , 252 , 0 , 255 ),
1029
+ 'lemonchiffon' : KatexSpanColor (255 , 250 , 205 , 255 ),
1030
+ 'lightblue' : KatexSpanColor (173 , 216 , 230 , 255 ),
1031
+ 'lightcoral' : KatexSpanColor (240 , 128 , 128 , 255 ),
1032
+ 'lightcyan' : KatexSpanColor (224 , 255 , 255 , 255 ),
1033
+ 'lightgoldenrodyellow' : KatexSpanColor (250 , 250 , 210 , 255 ),
1034
+ 'lightgray' : KatexSpanColor (211 , 211 , 211 , 255 ),
1035
+ 'lightgreen' : KatexSpanColor (144 , 238 , 144 , 255 ),
1036
+ 'lightgrey' : KatexSpanColor (211 , 211 , 211 , 255 ),
1037
+ 'lightpink' : KatexSpanColor (255 , 182 , 193 , 255 ),
1038
+ 'lightsalmon' : KatexSpanColor (255 , 160 , 122 , 255 ),
1039
+ 'lightseagreen' : KatexSpanColor (32 , 178 , 170 , 255 ),
1040
+ 'lightskyblue' : KatexSpanColor (135 , 206 , 250 , 255 ),
1041
+ 'lightslategray' : KatexSpanColor (119 , 136 , 153 , 255 ),
1042
+ 'lightslategrey' : KatexSpanColor (119 , 136 , 153 , 255 ),
1043
+ 'lightsteelblue' : KatexSpanColor (176 , 196 , 222 , 255 ),
1044
+ 'lightyellow' : KatexSpanColor (255 , 255 , 224 , 255 ),
1045
+ 'lime' : KatexSpanColor (0 , 255 , 0 , 255 ),
1046
+ 'limegreen' : KatexSpanColor (50 , 205 , 50 , 255 ),
1047
+ 'linen' : KatexSpanColor (250 , 240 , 230 , 255 ),
1048
+ 'magenta' : KatexSpanColor (255 , 0 , 255 , 255 ),
1049
+ 'maroon' : KatexSpanColor (128 , 0 , 0 , 255 ),
1050
+ 'mediumaquamarine' : KatexSpanColor (102 , 205 , 170 , 255 ),
1051
+ 'mediumblue' : KatexSpanColor (0 , 0 , 205 , 255 ),
1052
+ 'mediumorchid' : KatexSpanColor (186 , 85 , 211 , 255 ),
1053
+ 'mediumpurple' : KatexSpanColor (147 , 112 , 219 , 255 ),
1054
+ 'mediumseagreen' : KatexSpanColor (60 , 179 , 113 , 255 ),
1055
+ 'mediumslateblue' : KatexSpanColor (123 , 104 , 238 , 255 ),
1056
+ 'mediumspringgreen' : KatexSpanColor (0 , 250 , 154 , 255 ),
1057
+ 'mediumturquoise' : KatexSpanColor (72 , 209 , 204 , 255 ),
1058
+ 'mediumvioletred' : KatexSpanColor (199 , 21 , 133 , 255 ),
1059
+ 'midnightblue' : KatexSpanColor (25 , 25 , 112 , 255 ),
1060
+ 'mintcream' : KatexSpanColor (245 , 255 , 250 , 255 ),
1061
+ 'mistyrose' : KatexSpanColor (255 , 228 , 225 , 255 ),
1062
+ 'moccasin' : KatexSpanColor (255 , 228 , 181 , 255 ),
1063
+ 'navajowhite' : KatexSpanColor (255 , 222 , 173 , 255 ),
1064
+ 'navy' : KatexSpanColor (0 , 0 , 128 , 255 ),
1065
+ 'oldlace' : KatexSpanColor (253 , 245 , 230 , 255 ),
1066
+ 'olive' : KatexSpanColor (128 , 128 , 0 , 255 ),
1067
+ 'olivedrab' : KatexSpanColor (107 , 142 , 35 , 255 ),
1068
+ 'orange' : KatexSpanColor (255 , 165 , 0 , 255 ),
1069
+ 'orangered' : KatexSpanColor (255 , 69 , 0 , 255 ),
1070
+ 'orchid' : KatexSpanColor (218 , 112 , 214 , 255 ),
1071
+ 'palegoldenrod' : KatexSpanColor (238 , 232 , 170 , 255 ),
1072
+ 'palegreen' : KatexSpanColor (152 , 251 , 152 , 255 ),
1073
+ 'paleturquoise' : KatexSpanColor (175 , 238 , 238 , 255 ),
1074
+ 'palevioletred' : KatexSpanColor (219 , 112 , 147 , 255 ),
1075
+ 'papayawhip' : KatexSpanColor (255 , 239 , 213 , 255 ),
1076
+ 'peachpuff' : KatexSpanColor (255 , 218 , 185 , 255 ),
1077
+ 'peru' : KatexSpanColor (205 , 133 , 63 , 255 ),
1078
+ 'pink' : KatexSpanColor (255 , 192 , 203 , 255 ),
1079
+ 'plum' : KatexSpanColor (221 , 160 , 221 , 255 ),
1080
+ 'powderblue' : KatexSpanColor (176 , 224 , 230 , 255 ),
1081
+ 'purple' : KatexSpanColor (128 , 0 , 128 , 255 ),
1082
+ 'rebeccapurple' : KatexSpanColor (102 , 51 , 153 , 255 ),
1083
+ 'red' : KatexSpanColor (255 , 0 , 0 , 255 ),
1084
+ 'rosybrown' : KatexSpanColor (188 , 143 , 143 , 255 ),
1085
+ 'royalblue' : KatexSpanColor (65 , 105 , 225 , 255 ),
1086
+ 'saddlebrown' : KatexSpanColor (139 , 69 , 19 , 255 ),
1087
+ 'salmon' : KatexSpanColor (250 , 128 , 114 , 255 ),
1088
+ 'sandybrown' : KatexSpanColor (244 , 164 , 96 , 255 ),
1089
+ 'seagreen' : KatexSpanColor (46 , 139 , 87 , 255 ),
1090
+ 'seashell' : KatexSpanColor (255 , 245 , 238 , 255 ),
1091
+ 'sienna' : KatexSpanColor (160 , 82 , 45 , 255 ),
1092
+ 'silver' : KatexSpanColor (192 , 192 , 192 , 255 ),
1093
+ 'skyblue' : KatexSpanColor (135 , 206 , 235 , 255 ),
1094
+ 'slateblue' : KatexSpanColor (106 , 90 , 205 , 255 ),
1095
+ 'slategray' : KatexSpanColor (112 , 128 , 144 , 255 ),
1096
+ 'slategrey' : KatexSpanColor (112 , 128 , 144 , 255 ),
1097
+ 'snow' : KatexSpanColor (255 , 250 , 250 , 255 ),
1098
+ 'springgreen' : KatexSpanColor (0 , 255 , 127 , 255 ),
1099
+ 'steelblue' : KatexSpanColor (70 , 130 , 180 , 255 ),
1100
+ 'tan' : KatexSpanColor (210 , 180 , 140 , 255 ),
1101
+ 'teal' : KatexSpanColor (0 , 128 , 128 , 255 ),
1102
+ 'thistle' : KatexSpanColor (216 , 191 , 216 , 255 ),
1103
+ 'tomato' : KatexSpanColor (255 , 99 , 71 , 255 ),
1104
+ 'turquoise' : KatexSpanColor (64 , 224 , 208 , 255 ),
1105
+ 'violet' : KatexSpanColor (238 , 130 , 238 , 255 ),
1106
+ 'wheat' : KatexSpanColor (245 , 222 , 179 , 255 ),
1107
+ 'white' : KatexSpanColor (255 , 255 , 255 , 255 ),
1108
+ 'whitesmoke' : KatexSpanColor (245 , 245 , 245 , 255 ),
1109
+ 'yellow' : KatexSpanColor (255 , 255 , 0 , 255 ),
1110
+ 'yellowgreen' : KatexSpanColor (154 , 205 , 50 , 255 ),
1111
+ };
1112
+
839
1113
class _KatexHtmlParseError extends Error {
840
1114
final String ? message;
841
1115
0 commit comments