@@ -3,6 +3,8 @@ import 'dart:ui';
3
3
import 'package:csslib/visitor.dart' as css;
4
4
import 'package:csslib/parser.dart' as cssparser;
5
5
import 'package:flutter/cupertino.dart' ;
6
+ import 'package:flutter/material.dart' ;
7
+ import 'package:flutter_html/src/utils.dart' ;
6
8
import 'package:flutter_html/style.dart' ;
7
9
8
10
Style declarationsToStyle (Map <String ?, List <css.Expression >> declarations) {
@@ -13,6 +15,24 @@ Style declarationsToStyle(Map<String?, List<css.Expression>> declarations) {
13
15
case 'background-color' :
14
16
style.backgroundColor = ExpressionMapping .expressionToColor (value.first) ?? style.backgroundColor;
15
17
break ;
18
+ case 'border' :
19
+ List <css.LiteralTerm ?>? borderWidths = value.whereType< css.LiteralTerm > ().toList ();
20
+ /// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.width] , so make sure to remove those before passing it to [ExpressionMapping]
21
+ borderWidths.removeWhere ((element) => element != null && element.text != "thin"
22
+ && element.text != "medium" && element.text != "thick"
23
+ && ! (element is css.LengthTerm ) && ! (element is css.PercentageTerm )
24
+ && ! (element is css.EmTerm ) && ! (element is css.RemTerm )
25
+ && ! (element is css.NumberTerm )
26
+ );
27
+ List <css.Expression ?>? borderColors = value.where ((element) => ExpressionMapping .expressionToColor (element) != null ).toList ();
28
+ List <css.LiteralTerm ?>? potentialStyles = value.whereType< css.LiteralTerm > ().toList ();
29
+ /// Currently doesn't matter, as Flutter only supports "solid" or "none", but may support more in the future.
30
+ List <String > possibleBorderValues = ["dotted" , "dashed" , "solid" , "double" , "groove" , "ridge" , "inset" , "outset" , "none" , "hidden" ];
31
+ /// List<css.LiteralTerm> might include other values than the ones we want for [BorderSide.style] , so make sure to remove those before passing it to [ExpressionMapping]
32
+ potentialStyles.removeWhere ((element) => element != null && ! possibleBorderValues.contains (element.text));
33
+ List <css.LiteralTerm ?>? borderStyles = potentialStyles;
34
+ style.border = ExpressionMapping .expressionToBorder (borderWidths, borderStyles, borderColors);
35
+ break ;
16
36
case 'color' :
17
37
style.color = ExpressionMapping .expressionToColor (value.first) ?? style.color;
18
38
break ;
@@ -49,8 +69,15 @@ Style declarationsToStyle(Map<String?, List<css.Expression>> declarations) {
49
69
textDecorationList.removeWhere ((element) => element != null && element.text != "none"
50
70
&& element.text != "overline" && element.text != "underline" && element.text != "line-through" );
51
71
List <css.Expression ?>? nullableList = value;
52
- css.Expression ? textDecorationColor = nullableList.firstWhere (
53
- (css.Expression ? element) => element is css.HexColorTerm || element is css.FunctionTerm , orElse: () => null );
72
+ css.Expression ? textDecorationColor;
73
+ /// orElse: will not allow me to return null (even if the compiler says its okay, it errors on runtime).
74
+ /// try/catch is a workaround for this.
75
+ try {
76
+ textDecorationColor = nullableList.firstWhere (
77
+ (css.Expression ? element) => element is css.HexColorTerm || element is css.FunctionTerm );
78
+ } catch (e) {
79
+ textDecorationColor = null ;
80
+ }
54
81
List <css.LiteralTerm ?>? potentialStyles = value.whereType< css.LiteralTerm > ().toList ();
55
82
/// List<css.LiteralTerm> might include other values than the ones we want for [textDecorationStyle] , so make sure to remove those before passing it to [ExpressionMapping]
56
83
potentialStyles.removeWhere ((element) => element != null && element.text != "solid"
@@ -112,14 +139,132 @@ class DeclarationVisitor extends css.Visitor {
112
139
113
140
//Mapping functions
114
141
class ExpressionMapping {
115
- static Color ? expressionToColor (css.Expression value) {
116
- if (value is css.HexColorTerm ) {
117
- return stringToColor (value.text);
118
- } else if (value is css.FunctionTerm ) {
119
- if (value.text == 'rgba' ) {
120
- return rgbOrRgbaToColor (value.span! .text);
121
- } else if (value.text == 'rgb' ) {
122
- return rgbOrRgbaToColor (value.span! .text);
142
+
143
+ static Border expressionToBorder (List <css.Expression ?>? borderWidths, List <css.LiteralTerm ?>? borderStyles, List <css.Expression ?>? borderColors) {
144
+ CustomBorderSide left = CustomBorderSide ();
145
+ CustomBorderSide top = CustomBorderSide ();
146
+ CustomBorderSide right = CustomBorderSide ();
147
+ CustomBorderSide bottom = CustomBorderSide ();
148
+ if (borderWidths != null && borderWidths.isNotEmpty) {
149
+ top.width = expressionToBorderWidth (borderWidths.first);
150
+ if (borderWidths.length == 4 ) {
151
+ right.width = expressionToBorderWidth (borderWidths[1 ]);
152
+ bottom.width = expressionToBorderWidth (borderWidths[2 ]);
153
+ left.width = expressionToBorderWidth (borderWidths.last);
154
+ }
155
+ if (borderWidths.length == 3 ) {
156
+ left.width = expressionToBorderWidth (borderWidths[1 ]);
157
+ right.width = expressionToBorderWidth (borderWidths[1 ]);
158
+ bottom.width = expressionToBorderWidth (borderWidths.last);
159
+ }
160
+ if (borderWidths.length == 2 ) {
161
+ bottom.width = expressionToBorderWidth (borderWidths.first);
162
+ left.width = expressionToBorderWidth (borderWidths.last);
163
+ right.width = expressionToBorderWidth (borderWidths.last);
164
+ }
165
+ if (borderWidths.length == 1 ) {
166
+ bottom.width = expressionToBorderWidth (borderWidths.first);
167
+ left.width = expressionToBorderWidth (borderWidths.first);
168
+ right.width = expressionToBorderWidth (borderWidths.first);
169
+ }
170
+ }
171
+ if (borderStyles != null && borderStyles.isNotEmpty) {
172
+ top.style = expressionToBorderStyle (borderStyles.first);
173
+ if (borderStyles.length == 4 ) {
174
+ right.style = expressionToBorderStyle (borderStyles[1 ]);
175
+ bottom.style = expressionToBorderStyle (borderStyles[2 ]);
176
+ left.style = expressionToBorderStyle (borderStyles.last);
177
+ }
178
+ if (borderStyles.length == 3 ) {
179
+ left.style = expressionToBorderStyle (borderStyles[1 ]);
180
+ right.style = expressionToBorderStyle (borderStyles[1 ]);
181
+ bottom.style = expressionToBorderStyle (borderStyles.last);
182
+ }
183
+ if (borderStyles.length == 2 ) {
184
+ bottom.style = expressionToBorderStyle (borderStyles.first);
185
+ left.style = expressionToBorderStyle (borderStyles.last);
186
+ right.style = expressionToBorderStyle (borderStyles.last);
187
+ }
188
+ if (borderStyles.length == 1 ) {
189
+ bottom.style = expressionToBorderStyle (borderStyles.first);
190
+ left.style = expressionToBorderStyle (borderStyles.first);
191
+ right.style = expressionToBorderStyle (borderStyles.first);
192
+ }
193
+ }
194
+ if (borderColors != null && borderColors.isNotEmpty) {
195
+ top.color = expressionToColor (borderColors.first);
196
+ if (borderColors.length == 4 ) {
197
+ right.color = expressionToColor (borderColors[1 ]);
198
+ bottom.color = expressionToColor (borderColors[2 ]);
199
+ left.color = expressionToColor (borderColors.last);
200
+ }
201
+ if (borderColors.length == 3 ) {
202
+ left.color = expressionToColor (borderColors[1 ]);
203
+ right.color = expressionToColor (borderColors[1 ]);
204
+ bottom.color = expressionToColor (borderColors.last);
205
+ }
206
+ if (borderColors.length == 2 ) {
207
+ bottom.color = expressionToColor (borderColors.first);
208
+ left.color = expressionToColor (borderColors.last);
209
+ right.color = expressionToColor (borderColors.last);
210
+ }
211
+ if (borderColors.length == 1 ) {
212
+ bottom.color = expressionToColor (borderColors.first);
213
+ left.color = expressionToColor (borderColors.first);
214
+ right.color = expressionToColor (borderColors.first);
215
+ }
216
+ }
217
+ return Border (
218
+ top: BorderSide (width: top.width, color: top.color ?? Colors .black, style: top.style),
219
+ right: BorderSide (width: right.width, color: right.color ?? Colors .black, style: right.style),
220
+ bottom: BorderSide (width: bottom.width, color: bottom.color ?? Colors .black, style: bottom.style),
221
+ left: BorderSide (width: left.width, color: left.color ?? Colors .black, style: left.style)
222
+ );
223
+ }
224
+
225
+ static double expressionToBorderWidth (css.Expression ? value) {
226
+ if (value is css.NumberTerm ) {
227
+ return double .tryParse (value.text) ?? 1.0 ;
228
+ } else if (value is css.PercentageTerm ) {
229
+ return (double .tryParse (value.text) ?? 400 ) / 100 ;
230
+ } else if (value is css.EmTerm ) {
231
+ return double .tryParse (value.text) ?? 1.0 ;
232
+ } else if (value is css.RemTerm ) {
233
+ return double .tryParse (value.text) ?? 1.0 ;
234
+ } else if (value is css.LengthTerm ) {
235
+ return double .tryParse (value.text.replaceAll (new RegExp (r'\s+(\d+\.\d+)\s+' ), '' )) ?? 1.0 ;
236
+ } else if (value is css.LiteralTerm ) {
237
+ switch (value.text) {
238
+ case "thin" :
239
+ return 2.0 ;
240
+ case "medium" :
241
+ return 4.0 ;
242
+ case "thick" :
243
+ return 6.0 ;
244
+ }
245
+ }
246
+ return 4.0 ;
247
+ }
248
+
249
+ static BorderStyle expressionToBorderStyle (css.LiteralTerm ? value) {
250
+ if (value != null && value.text != "none" && value.text != "hidden" ) {
251
+ return BorderStyle .solid;
252
+ }
253
+ return BorderStyle .none;
254
+ }
255
+
256
+ static Color ? expressionToColor (css.Expression ? value) {
257
+ if (value != null ) {
258
+ if (value is css.HexColorTerm ) {
259
+ return stringToColor (value.text);
260
+ } else if (value is css.FunctionTerm ) {
261
+ if (value.text == 'rgba' || value.text == 'rgb' ) {
262
+ return rgbOrRgbaToColor (value.span! .text);
263
+ } else if (value.text == 'hsla' || value.text == 'hsl' ) {
264
+ return hslToRgbToColor (value.span! .text);
265
+ }
266
+ } else if (value is css.LiteralTerm ) {
267
+ return namedColorToColor (value.text);
123
268
}
124
269
}
125
270
return null ;
@@ -359,21 +504,21 @@ class ExpressionMapping {
359
504
css.LiteralTerm ? exp4 = list.length > 3 ? list[3 ] as css.LiteralTerm ? : null ;
360
505
RegExp nonNumberRegex = RegExp (r'\s+(\d+\.\d+)\s+' );
361
506
if (exp is css.LiteralTerm && exp2 is css.LiteralTerm ) {
362
- if (exp3 != null && (exp3 is css. HexColorTerm || exp3 is css. FunctionTerm ) ) {
507
+ if (exp3 != null && ExpressionMapping . expressionToColor (exp3) != null ) {
363
508
shadow.add (Shadow (
364
- color: expressionToColor (exp3)! ,
509
+ color: expressionToColor (exp3)! ,
365
510
offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! )
366
511
));
367
512
} else if (exp3 != null && exp3 is css.LiteralTerm ) {
368
- if (exp4 != null && (exp4 is css. HexColorTerm || exp4 is css. FunctionTerm ) ) {
513
+ if (exp4 != null && ExpressionMapping . expressionToColor (exp4) != null ) {
369
514
shadow.add (Shadow (
370
- color: expressionToColor (exp4)! ,
371
- offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
515
+ color: expressionToColor (exp4)! ,
516
+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
372
517
blurRadius: double .tryParse (exp3.text.replaceAll (nonNumberRegex, '' ))!
373
518
));
374
519
} else {
375
520
shadow.add (Shadow (
376
- offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
521
+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' ))! , double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))! ),
377
522
blurRadius: double .tryParse (exp3.text.replaceAll (nonNumberRegex, '' ))!
378
523
));
379
524
}
@@ -427,4 +572,33 @@ class ExpressionMapping {
427
572
return null ;
428
573
}
429
574
}
575
+
576
+ static Color hslToRgbToColor (String text) {
577
+ final hslText = text.replaceAll (')' , '' ).replaceAll (' ' , '' );
578
+ final hslValues = hslText.split (',' ).toList ();
579
+ List <double ?> parsedHsl = [];
580
+ hslValues.forEach ((element) {
581
+ if (element.contains ("%" ) && double .tryParse (element.replaceAll ("%" , "" )) != null ) {
582
+ parsedHsl.add (double .tryParse (element.replaceAll ("%" , "" ))! * 0.01 );
583
+ } else {
584
+ if (element != hslValues.first && (double .tryParse (element) == null || double .tryParse (element)! > 1 )) {
585
+ parsedHsl.add (null );
586
+ } else {
587
+ parsedHsl.add (double .tryParse (element));
588
+ }
589
+ }
590
+ });
591
+ if (parsedHsl.length == 4 && ! parsedHsl.contains (null )) {
592
+ return HSLColor .fromAHSL (parsedHsl.last! , parsedHsl.first! , parsedHsl[1 ]! , parsedHsl[2 ]! ).toColor ();
593
+ } else if (parsedHsl.length == 3 && ! parsedHsl.contains (null )) {
594
+ return HSLColor .fromAHSL (1.0 , parsedHsl.first! , parsedHsl[1 ]! , parsedHsl.last! ).toColor ();
595
+ } else return Colors .black;
596
+ }
597
+
598
+ static Color ? namedColorToColor (String text) {
599
+ String namedColor = namedColors.keys.firstWhere ((element) => element.toLowerCase () == text.toLowerCase (), orElse: () => "" );
600
+ if (namedColor != "" ) {
601
+ return stringToColor (namedColors[namedColor]! );
602
+ } else return null ;
603
+ }
430
604
}
0 commit comments