1
+ import 'dart:ui' ;
2
+
3
+ import 'package:convert/convert.dart' ;
1
4
import 'package:csslib/parser.dart' as css_parser;
2
5
import 'package:csslib/visitor.dart' as css_visitor;
3
6
import 'package:flutter/foundation.dart' ;
4
- import 'package:flutter/widgets.dart' ;
5
7
import 'package:html/dom.dart' as dom;
6
8
7
9
import '../log.dart' ;
@@ -494,6 +496,7 @@ class _KatexParser {
494
496
double ? verticalAlignEm;
495
497
double ? marginRightEm;
496
498
double ? marginLeftEm;
499
+ KatexSpanColor ? color;
497
500
498
501
for (final declaration in rule.declarationGroup.declarations) {
499
502
if (declaration case css_visitor.Declaration (
@@ -523,6 +526,38 @@ class _KatexParser {
523
526
if (marginLeftEm < 0 ) throw _KatexHtmlParseError ();
524
527
continue ;
525
528
}
529
+
530
+ case 'color' :
531
+ // `package:csslib` parser emits a HexColorTerm for the `color`
532
+ // attribute. It automatically resolves the named CSS colors to
533
+ // their hex values. The `HexColorTerm.value` is the hex
534
+ // encoded in an integer in the same sequence as the input hex
535
+ // string. But it also allows some non-conformant CSS hex color
536
+ // notations, like #f, #ff, #fffff, #fffffff.
537
+ // See:
538
+ // https://drafts.csswg.org/css-color/#hex-notation.
539
+ // https://github.com/dart-lang/tools/blob/2a2a2d611/pkgs/csslib/lib/parser.dart#L2714-L2743
540
+ //
541
+ // Also the generated integer value will be in 0xRRGGBBAA
542
+ // sequence (CSS notation), whereas `dart:ui` Color
543
+ // requires 0xAARRGGBB.
544
+ //
545
+ // So, we try to parse the value of `color` attribute ourselves
546
+ // only allowing conformant CSS hex color notations, mapping
547
+ // named CSS colors to their corresponding values, generating a
548
+ // typed result (KatexSpanColor(r, g, b, a)) to be used later
549
+ // while rendering.
550
+
551
+ final valueStr = _getRawValue (expression);
552
+ if (valueStr != null ) {
553
+ if (valueStr.startsWith ('#' )) {
554
+ color = parseCssHexColor (valueStr);
555
+ if (color != null ) continue ;
556
+ }
557
+
558
+ color = _cssNamedColorsMap[valueStr];
559
+ if (color != null ) continue ;
560
+ }
526
561
}
527
562
528
563
// TODO handle more CSS properties
@@ -540,6 +575,7 @@ class _KatexParser {
540
575
verticalAlignEm: verticalAlignEm,
541
576
marginRightEm: marginRightEm,
542
577
marginLeftEm: marginLeftEm,
578
+ color: color,
543
579
);
544
580
} else {
545
581
throw _KatexHtmlParseError ();
@@ -556,6 +592,10 @@ class _KatexParser {
556
592
}
557
593
return null ;
558
594
}
595
+
596
+ String ? _getRawValue (css_visitor.Expression expression) {
597
+ return expression.span? .text;
598
+ }
559
599
}
560
600
561
601
enum KatexSpanFontWeight {
@@ -573,6 +613,32 @@ enum KatexSpanTextAlign {
573
613
right,
574
614
}
575
615
616
+ class KatexSpanColor {
617
+ const KatexSpanColor (this .r, this .g, this .b, this .a);
618
+
619
+ final int r;
620
+ final int g;
621
+ final int b;
622
+ final int a;
623
+
624
+ @override
625
+ bool operator == (Object other) {
626
+ return other is KatexSpanColor &&
627
+ other.r == r &&
628
+ other.g == g &&
629
+ other.b == b &&
630
+ other.a == a;
631
+ }
632
+
633
+ @override
634
+ int get hashCode => Object .hash ('KatexSpanColor' , r, g, b, a);
635
+
636
+ @override
637
+ String toString () {
638
+ return '${objectRuntimeType (this , 'KatexSpanColor' )}($r , $g , $b , $a )' ;
639
+ }
640
+ }
641
+
576
642
@immutable
577
643
class KatexSpanStyles {
578
644
final double ? heightEm;
@@ -587,6 +653,8 @@ class KatexSpanStyles {
587
653
final KatexSpanFontStyle ? fontStyle;
588
654
final KatexSpanTextAlign ? textAlign;
589
655
656
+ final KatexSpanColor ? color;
657
+
590
658
const KatexSpanStyles ({
591
659
this .heightEm,
592
660
this .verticalAlignEm,
@@ -597,6 +665,7 @@ class KatexSpanStyles {
597
665
this .fontWeight,
598
666
this .fontStyle,
599
667
this .textAlign,
668
+ this .color,
600
669
});
601
670
602
671
@override
@@ -611,6 +680,7 @@ class KatexSpanStyles {
611
680
fontWeight,
612
681
fontStyle,
613
682
textAlign,
683
+ color,
614
684
);
615
685
616
686
@override
@@ -624,7 +694,8 @@ class KatexSpanStyles {
624
694
other.fontSizeEm == fontSizeEm &&
625
695
other.fontWeight == fontWeight &&
626
696
other.fontStyle == fontStyle &&
627
- other.textAlign == textAlign;
697
+ other.textAlign == textAlign &&
698
+ other.color == color;
628
699
}
629
700
630
701
@override
@@ -639,6 +710,7 @@ class KatexSpanStyles {
639
710
if (fontWeight != null ) args.add ('fontWeight: $fontWeight ' );
640
711
if (fontStyle != null ) args.add ('fontStyle: $fontStyle ' );
641
712
if (textAlign != null ) args.add ('textAlign: $textAlign ' );
713
+ if (color != null ) args.add ('color: $color ' );
642
714
return '${objectRuntimeType (this , 'KatexSpanStyles' )}(${args .join (', ' )})' ;
643
715
}
644
716
@@ -660,6 +732,7 @@ class KatexSpanStyles {
660
732
fontStyle: other.fontStyle ?? fontStyle,
661
733
fontWeight: other.fontWeight ?? fontWeight,
662
734
textAlign: other.textAlign ?? textAlign,
735
+ color: other.color ?? color,
663
736
);
664
737
}
665
738
@@ -688,6 +761,196 @@ class KatexSpanStyles {
688
761
}
689
762
}
690
763
764
+ final _hexColorRegExp =
765
+ RegExp (r'^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$' );
766
+
767
+ /// Parses the CSS hex color notation.
768
+ ///
769
+ /// See: https://drafts.csswg.org/css-color/#hex-notation
770
+ KatexSpanColor ? parseCssHexColor (String hexStr) {
771
+ final match = _hexColorRegExp.firstMatch (hexStr);
772
+ if (match == null ) return null ;
773
+
774
+ String hexValue = match.group (1 )! ;
775
+ hexValue = hexValue.toLowerCase ();
776
+ switch (hexValue.length) {
777
+ case 3 :
778
+ hexValue = '${hexValue [0 ]}${hexValue [0 ]}'
779
+ '${hexValue [1 ]}${hexValue [1 ]}'
780
+ '${hexValue [2 ]}${hexValue [2 ]}'
781
+ 'ff' ;
782
+ case 4 :
783
+ hexValue = '${hexValue [0 ]}${hexValue [0 ]}'
784
+ '${hexValue [1 ]}${hexValue [1 ]}'
785
+ '${hexValue [2 ]}${hexValue [2 ]}'
786
+ '${hexValue [3 ]}${hexValue [3 ]}' ;
787
+ case 6 :
788
+ hexValue += 'ff' ;
789
+ }
790
+
791
+ try {
792
+ final [r, g, b, a] = hex.decode (hexValue);
793
+ return KatexSpanColor (r, g, b, a);
794
+ } catch (_) {
795
+ return null ; // TODO(log)
796
+ }
797
+ }
798
+
799
+ // CSS named colors: https://drafts.csswg.org/css-color/#named-colors
800
+ // Map adapted from the following source file:
801
+ // https://github.com/w3c/csswg-drafts/blob/1942d0918/css-color-4/Overview.bs#L1562-L1859
802
+ const _cssNamedColorsMap = {
803
+ 'transparent' : KatexSpanColor (0 , 0 , 0 , 0 ), // https://drafts.csswg.org/css-color/#transparent-color
804
+ 'aliceblue' : KatexSpanColor (240 , 248 , 255 , 255 ),
805
+ 'antiquewhite' : KatexSpanColor (250 , 235 , 215 , 255 ),
806
+ 'aqua' : KatexSpanColor (0 , 255 , 255 , 255 ),
807
+ 'aquamarine' : KatexSpanColor (127 , 255 , 212 , 255 ),
808
+ 'azure' : KatexSpanColor (240 , 255 , 255 , 255 ),
809
+ 'beige' : KatexSpanColor (245 , 245 , 220 , 255 ),
810
+ 'bisque' : KatexSpanColor (255 , 228 , 196 , 255 ),
811
+ 'black' : KatexSpanColor (0 , 0 , 0 , 255 ),
812
+ 'blanchedalmond' : KatexSpanColor (255 , 235 , 205 , 255 ),
813
+ 'blue' : KatexSpanColor (0 , 0 , 255 , 255 ),
814
+ 'blueviolet' : KatexSpanColor (138 , 43 , 226 , 255 ),
815
+ 'brown' : KatexSpanColor (165 , 42 , 42 , 255 ),
816
+ 'burlywood' : KatexSpanColor (222 , 184 , 135 , 255 ),
817
+ 'cadetblue' : KatexSpanColor (95 , 158 , 160 , 255 ),
818
+ 'chartreuse' : KatexSpanColor (127 , 255 , 0 , 255 ),
819
+ 'chocolate' : KatexSpanColor (210 , 105 , 30 , 255 ),
820
+ 'coral' : KatexSpanColor (255 , 127 , 80 , 255 ),
821
+ 'cornflowerblue' : KatexSpanColor (100 , 149 , 237 , 255 ),
822
+ 'cornsilk' : KatexSpanColor (255 , 248 , 220 , 255 ),
823
+ 'crimson' : KatexSpanColor (220 , 20 , 60 , 255 ),
824
+ 'cyan' : KatexSpanColor (0 , 255 , 255 , 255 ),
825
+ 'darkblue' : KatexSpanColor (0 , 0 , 139 , 255 ),
826
+ 'darkcyan' : KatexSpanColor (0 , 139 , 139 , 255 ),
827
+ 'darkgoldenrod' : KatexSpanColor (184 , 134 , 11 , 255 ),
828
+ 'darkgray' : KatexSpanColor (169 , 169 , 169 , 255 ),
829
+ 'darkgreen' : KatexSpanColor (0 , 100 , 0 , 255 ),
830
+ 'darkgrey' : KatexSpanColor (169 , 169 , 169 , 255 ),
831
+ 'darkkhaki' : KatexSpanColor (189 , 183 , 107 , 255 ),
832
+ 'darkmagenta' : KatexSpanColor (139 , 0 , 139 , 255 ),
833
+ 'darkolivegreen' : KatexSpanColor (85 , 107 , 47 , 255 ),
834
+ 'darkorange' : KatexSpanColor (255 , 140 , 0 , 255 ),
835
+ 'darkorchid' : KatexSpanColor (153 , 50 , 204 , 255 ),
836
+ 'darkred' : KatexSpanColor (139 , 0 , 0 , 255 ),
837
+ 'darksalmon' : KatexSpanColor (233 , 150 , 122 , 255 ),
838
+ 'darkseagreen' : KatexSpanColor (143 , 188 , 143 , 255 ),
839
+ 'darkslateblue' : KatexSpanColor (72 , 61 , 139 , 255 ),
840
+ 'darkslategray' : KatexSpanColor (47 , 79 , 79 , 255 ),
841
+ 'darkslategrey' : KatexSpanColor (47 , 79 , 79 , 255 ),
842
+ 'darkturquoise' : KatexSpanColor (0 , 206 , 209 , 255 ),
843
+ 'darkviolet' : KatexSpanColor (148 , 0 , 211 , 255 ),
844
+ 'deeppink' : KatexSpanColor (255 , 20 , 147 , 255 ),
845
+ 'deepskyblue' : KatexSpanColor (0 , 191 , 255 , 255 ),
846
+ 'dimgray' : KatexSpanColor (105 , 105 , 105 , 255 ),
847
+ 'dimgrey' : KatexSpanColor (105 , 105 , 105 , 255 ),
848
+ 'dodgerblue' : KatexSpanColor (30 , 144 , 255 , 255 ),
849
+ 'firebrick' : KatexSpanColor (178 , 34 , 34 , 255 ),
850
+ 'floralwhite' : KatexSpanColor (255 , 250 , 240 , 255 ),
851
+ 'forestgreen' : KatexSpanColor (34 , 139 , 34 , 255 ),
852
+ 'fuchsia' : KatexSpanColor (255 , 0 , 255 , 255 ),
853
+ 'gainsboro' : KatexSpanColor (220 , 220 , 220 , 255 ),
854
+ 'ghostwhite' : KatexSpanColor (248 , 248 , 255 , 255 ),
855
+ 'gold' : KatexSpanColor (255 , 215 , 0 , 255 ),
856
+ 'goldenrod' : KatexSpanColor (218 , 165 , 32 , 255 ),
857
+ 'gray' : KatexSpanColor (128 , 128 , 128 , 255 ),
858
+ 'green' : KatexSpanColor (0 , 128 , 0 , 255 ),
859
+ 'greenyellow' : KatexSpanColor (173 , 255 , 47 , 255 ),
860
+ 'grey' : KatexSpanColor (128 , 128 , 128 , 255 ),
861
+ 'honeydew' : KatexSpanColor (240 , 255 , 240 , 255 ),
862
+ 'hotpink' : KatexSpanColor (255 , 105 , 180 , 255 ),
863
+ 'indianred' : KatexSpanColor (205 , 92 , 92 , 255 ),
864
+ 'indigo' : KatexSpanColor (75 , 0 , 130 , 255 ),
865
+ 'ivory' : KatexSpanColor (255 , 255 , 240 , 255 ),
866
+ 'khaki' : KatexSpanColor (240 , 230 , 140 , 255 ),
867
+ 'lavender' : KatexSpanColor (230 , 230 , 250 , 255 ),
868
+ 'lavenderblush' : KatexSpanColor (255 , 240 , 245 , 255 ),
869
+ 'lawngreen' : KatexSpanColor (124 , 252 , 0 , 255 ),
870
+ 'lemonchiffon' : KatexSpanColor (255 , 250 , 205 , 255 ),
871
+ 'lightblue' : KatexSpanColor (173 , 216 , 230 , 255 ),
872
+ 'lightcoral' : KatexSpanColor (240 , 128 , 128 , 255 ),
873
+ 'lightcyan' : KatexSpanColor (224 , 255 , 255 , 255 ),
874
+ 'lightgoldenrodyellow' : KatexSpanColor (250 , 250 , 210 , 255 ),
875
+ 'lightgray' : KatexSpanColor (211 , 211 , 211 , 255 ),
876
+ 'lightgreen' : KatexSpanColor (144 , 238 , 144 , 255 ),
877
+ 'lightgrey' : KatexSpanColor (211 , 211 , 211 , 255 ),
878
+ 'lightpink' : KatexSpanColor (255 , 182 , 193 , 255 ),
879
+ 'lightsalmon' : KatexSpanColor (255 , 160 , 122 , 255 ),
880
+ 'lightseagreen' : KatexSpanColor (32 , 178 , 170 , 255 ),
881
+ 'lightskyblue' : KatexSpanColor (135 , 206 , 250 , 255 ),
882
+ 'lightslategray' : KatexSpanColor (119 , 136 , 153 , 255 ),
883
+ 'lightslategrey' : KatexSpanColor (119 , 136 , 153 , 255 ),
884
+ 'lightsteelblue' : KatexSpanColor (176 , 196 , 222 , 255 ),
885
+ 'lightyellow' : KatexSpanColor (255 , 255 , 224 , 255 ),
886
+ 'lime' : KatexSpanColor (0 , 255 , 0 , 255 ),
887
+ 'limegreen' : KatexSpanColor (50 , 205 , 50 , 255 ),
888
+ 'linen' : KatexSpanColor (250 , 240 , 230 , 255 ),
889
+ 'magenta' : KatexSpanColor (255 , 0 , 255 , 255 ),
890
+ 'maroon' : KatexSpanColor (128 , 0 , 0 , 255 ),
891
+ 'mediumaquamarine' : KatexSpanColor (102 , 205 , 170 , 255 ),
892
+ 'mediumblue' : KatexSpanColor (0 , 0 , 205 , 255 ),
893
+ 'mediumorchid' : KatexSpanColor (186 , 85 , 211 , 255 ),
894
+ 'mediumpurple' : KatexSpanColor (147 , 112 , 219 , 255 ),
895
+ 'mediumseagreen' : KatexSpanColor (60 , 179 , 113 , 255 ),
896
+ 'mediumslateblue' : KatexSpanColor (123 , 104 , 238 , 255 ),
897
+ 'mediumspringgreen' : KatexSpanColor (0 , 250 , 154 , 255 ),
898
+ 'mediumturquoise' : KatexSpanColor (72 , 209 , 204 , 255 ),
899
+ 'mediumvioletred' : KatexSpanColor (199 , 21 , 133 , 255 ),
900
+ 'midnightblue' : KatexSpanColor (25 , 25 , 112 , 255 ),
901
+ 'mintcream' : KatexSpanColor (245 , 255 , 250 , 255 ),
902
+ 'mistyrose' : KatexSpanColor (255 , 228 , 225 , 255 ),
903
+ 'moccasin' : KatexSpanColor (255 , 228 , 181 , 255 ),
904
+ 'navajowhite' : KatexSpanColor (255 , 222 , 173 , 255 ),
905
+ 'navy' : KatexSpanColor (0 , 0 , 128 , 255 ),
906
+ 'oldlace' : KatexSpanColor (253 , 245 , 230 , 255 ),
907
+ 'olive' : KatexSpanColor (128 , 128 , 0 , 255 ),
908
+ 'olivedrab' : KatexSpanColor (107 , 142 , 35 , 255 ),
909
+ 'orange' : KatexSpanColor (255 , 165 , 0 , 255 ),
910
+ 'orangered' : KatexSpanColor (255 , 69 , 0 , 255 ),
911
+ 'orchid' : KatexSpanColor (218 , 112 , 214 , 255 ),
912
+ 'palegoldenrod' : KatexSpanColor (238 , 232 , 170 , 255 ),
913
+ 'palegreen' : KatexSpanColor (152 , 251 , 152 , 255 ),
914
+ 'paleturquoise' : KatexSpanColor (175 , 238 , 238 , 255 ),
915
+ 'palevioletred' : KatexSpanColor (219 , 112 , 147 , 255 ),
916
+ 'papayawhip' : KatexSpanColor (255 , 239 , 213 , 255 ),
917
+ 'peachpuff' : KatexSpanColor (255 , 218 , 185 , 255 ),
918
+ 'peru' : KatexSpanColor (205 , 133 , 63 , 255 ),
919
+ 'pink' : KatexSpanColor (255 , 192 , 203 , 255 ),
920
+ 'plum' : KatexSpanColor (221 , 160 , 221 , 255 ),
921
+ 'powderblue' : KatexSpanColor (176 , 224 , 230 , 255 ),
922
+ 'purple' : KatexSpanColor (128 , 0 , 128 , 255 ),
923
+ 'rebeccapurple' : KatexSpanColor (102 , 51 , 153 , 255 ),
924
+ 'red' : KatexSpanColor (255 , 0 , 0 , 255 ),
925
+ 'rosybrown' : KatexSpanColor (188 , 143 , 143 , 255 ),
926
+ 'royalblue' : KatexSpanColor (65 , 105 , 225 , 255 ),
927
+ 'saddlebrown' : KatexSpanColor (139 , 69 , 19 , 255 ),
928
+ 'salmon' : KatexSpanColor (250 , 128 , 114 , 255 ),
929
+ 'sandybrown' : KatexSpanColor (244 , 164 , 96 , 255 ),
930
+ 'seagreen' : KatexSpanColor (46 , 139 , 87 , 255 ),
931
+ 'seashell' : KatexSpanColor (255 , 245 , 238 , 255 ),
932
+ 'sienna' : KatexSpanColor (160 , 82 , 45 , 255 ),
933
+ 'silver' : KatexSpanColor (192 , 192 , 192 , 255 ),
934
+ 'skyblue' : KatexSpanColor (135 , 206 , 235 , 255 ),
935
+ 'slateblue' : KatexSpanColor (106 , 90 , 205 , 255 ),
936
+ 'slategray' : KatexSpanColor (112 , 128 , 144 , 255 ),
937
+ 'slategrey' : KatexSpanColor (112 , 128 , 144 , 255 ),
938
+ 'snow' : KatexSpanColor (255 , 250 , 250 , 255 ),
939
+ 'springgreen' : KatexSpanColor (0 , 255 , 127 , 255 ),
940
+ 'steelblue' : KatexSpanColor (70 , 130 , 180 , 255 ),
941
+ 'tan' : KatexSpanColor (210 , 180 , 140 , 255 ),
942
+ 'teal' : KatexSpanColor (0 , 128 , 128 , 255 ),
943
+ 'thistle' : KatexSpanColor (216 , 191 , 216 , 255 ),
944
+ 'tomato' : KatexSpanColor (255 , 99 , 71 , 255 ),
945
+ 'turquoise' : KatexSpanColor (64 , 224 , 208 , 255 ),
946
+ 'violet' : KatexSpanColor (238 , 130 , 238 , 255 ),
947
+ 'wheat' : KatexSpanColor (245 , 222 , 179 , 255 ),
948
+ 'white' : KatexSpanColor (255 , 255 , 255 , 255 ),
949
+ 'whitesmoke' : KatexSpanColor (245 , 245 , 245 , 255 ),
950
+ 'yellow' : KatexSpanColor (255 , 255 , 0 , 255 ),
951
+ 'yellowgreen' : KatexSpanColor (154 , 205 , 50 , 255 ),
952
+ };
953
+
691
954
class _KatexHtmlParseError extends Error {
692
955
final String ? message;
693
956
0 commit comments