@@ -2,23 +2,71 @@ import 'dart:ui';
22
33import 'package:csslib/visitor.dart' as css;
44import 'package:csslib/parser.dart' as cssparser;
5+ import 'package:flutter/cupertino.dart' ;
56import 'package:flutter_html/style.dart' ;
67
78Style declarationsToStyle (Map <String , List <css.Expression >> declarations) {
89 Style style = new Style ();
910 declarations.forEach ((property, value) {
1011 switch (property) {
1112 case 'background-color' :
12- style.backgroundColor =
13- ExpressionMapping .expressionToColor (value.first);
13+ style.backgroundColor = ExpressionMapping .expressionToColor (value.first);
1414 break ;
1515 case 'color' :
1616 style.color = ExpressionMapping .expressionToColor (value.first);
1717 break ;
18+ case 'direction' :
19+ style.direction = ExpressionMapping .expressionToDirection (value.first);
20+ break ;
21+ case 'display' :
22+ style.display = ExpressionMapping .expressionToDisplay (value.first);
23+ break ;
24+ case 'line-height' :
25+ style.lineHeight = ExpressionMapping .expressionToLineHeight (value.first);
26+ break ;
27+ case 'font-family' :
28+ style.fontFamily = ExpressionMapping .expressionToFontFamily (value.first);
29+ break ;
30+ case 'font-feature-settings' :
31+ style.fontFeatureSettings = ExpressionMapping .expressionToFontFeatureSettings (value);
32+ break ;
33+ case 'font-size' :
34+ style.fontSize = ExpressionMapping .expressionToFontSize (value.first);
35+ break ;
36+ case 'font-style' :
37+ style.fontStyle = ExpressionMapping .expressionToFontStyle (value.first);
38+ break ;
39+ case 'font-weight' :
40+ style.fontWeight = ExpressionMapping .expressionToFontWeight (value.first);
41+ break ;
1842 case 'text-align' :
1943 style.textAlign = ExpressionMapping .expressionToTextAlign (value.first);
2044 break ;
21-
45+ case 'text-decoration' :
46+ List <css.LiteralTerm > textDecorationList = value.whereType< css.LiteralTerm > ().toList ();
47+ /// List<css.LiteralTerm> might include other values than the ones we want for [textDecorationList] , so make sure to remove those before passing it to [ExpressionMapping]
48+ textDecorationList.removeWhere ((element) => element.text != "none" && element.text != "overline" && element.text != "underline" && element.text != "line-through" );
49+ css.Expression textDecorationColor = value.firstWhere ((element) => element is css.HexColorTerm || element is css.FunctionTerm , orElse: null );
50+ List <css.LiteralTerm > temp = value.whereType< css.LiteralTerm > ().toList ();
51+ /// 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]
52+ temp.removeWhere ((element) => element.text != "solid" && element.text != "double" && element.text != "dashed" && element.text != "dotted" && element.text != "wavy" );
53+ css.LiteralTerm textDecorationStyle = temp.last ?? null ;
54+ style.textDecoration = ExpressionMapping .expressionToTextDecorationLine (textDecorationList);
55+ if (textDecorationColor != null ) style.textDecorationColor = ExpressionMapping .expressionToColor (textDecorationColor);
56+ if (textDecorationStyle != null ) style.textDecorationStyle = ExpressionMapping .expressionToTextDecorationStyle (textDecorationStyle);
57+ break ;
58+ case 'text-decoration-color' :
59+ style.textDecorationColor = ExpressionMapping .expressionToColor (value.first);
60+ break ;
61+ case 'text-decoration-line' :
62+ style.textDecoration = ExpressionMapping .expressionToTextDecorationLine (value);
63+ break ;
64+ case 'text-decoration-style' :
65+ style.textDecorationStyle = ExpressionMapping .expressionToTextDecorationStyle (value.first);
66+ break ;
67+ case 'text-shadow' :
68+ style.textShadow = ExpressionMapping .expressionToTextShadow (value);
69+ break ;
2270 }
2371 });
2472 return style;
@@ -70,6 +118,267 @@ class ExpressionMapping {
70118 return null ;
71119 }
72120
121+ static TextDirection expressionToDirection (css.Expression value) {
122+ if (value is css.LiteralTerm ) {
123+ switch (value.text) {
124+ case "ltr" :
125+ return TextDirection .ltr;
126+ case "rtl" :
127+ return TextDirection .rtl;
128+ }
129+ }
130+ return TextDirection .ltr;
131+ }
132+
133+ static Display expressionToDisplay (css.Expression value) {
134+ if (value is css.LiteralTerm ) {
135+ switch (value.text) {
136+ case 'block' :
137+ return Display .BLOCK ;
138+ case 'inline-block' :
139+ return Display .INLINE_BLOCK ;
140+ case 'inline' :
141+ return Display .INLINE ;
142+ case 'list-item' :
143+ return Display .LIST_ITEM ;
144+ case 'none' :
145+ return Display .NONE ;
146+ }
147+ }
148+ return Display .INLINE ;
149+ }
150+
151+ static List <FontFeature > expressionToFontFeatureSettings (List <css.Expression > value) {
152+ List <FontFeature > fontFeatures = [];
153+ for (int i = 0 ; i < value.length; i++ ) {
154+ css.Expression exp = value[i];
155+ if (exp is css.LiteralTerm ) {
156+ if (exp.text != "on" && exp.text != "off" && exp.text != "1" && exp.text != "0" ) {
157+ if (i < value.length - 1 ) {
158+ css.Expression nextExp = value[i+ 1 ];
159+ if (nextExp is css.LiteralTerm && (nextExp.text == "on" || nextExp.text == "off" || nextExp.text == "1" || nextExp.text == "0" )) {
160+ fontFeatures.add (FontFeature (exp.text, nextExp.text == "on" || nextExp.text == "1" ? 1 : 0 ));
161+ } else {
162+ fontFeatures.add (FontFeature .enable (exp.text));
163+ }
164+ } else {
165+ fontFeatures.add (FontFeature .enable (exp.text));
166+ }
167+ }
168+ }
169+ }
170+ List <FontFeature > finalFontFeatures = fontFeatures.toSet ().toList ();
171+ return finalFontFeatures;
172+ }
173+
174+ static FontSize expressionToFontSize (css.Expression value) {
175+ if (value is css.NumberTerm ) {
176+ return FontSize (double .tryParse (value.text));
177+ } else if (value is css.PercentageTerm ) {
178+ return FontSize .percent (int .tryParse (value.text));
179+ } else if (value is css.EmTerm ) {
180+ return FontSize .em (double .tryParse (value.text));
181+ } else if (value is css.RemTerm ) {
182+ return FontSize .rem (double .tryParse (value.text));
183+ } else if (value is css.LengthTerm ) {
184+ return FontSize (double .tryParse (value.text.replaceAll (new RegExp (r'\s+(\d+\.\d+)\s+' ), '' )));
185+ } else if (value is css.LiteralTerm ) {
186+ switch (value.text) {
187+ case "xx-small" :
188+ return FontSize .xxSmall;
189+ case "x-small" :
190+ return FontSize .xSmall;
191+ case "small" :
192+ return FontSize .small;
193+ case "medium" :
194+ return FontSize .medium;
195+ case "large" :
196+ return FontSize .large;
197+ case "x-large" :
198+ return FontSize .xLarge;
199+ case "xx-large" :
200+ return FontSize .xxLarge;
201+ }
202+ }
203+ return null ;
204+ }
205+
206+ static FontStyle expressionToFontStyle (css.Expression value) {
207+ if (value is css.LiteralTerm ) {
208+ switch (value.text) {
209+ case "italic" :
210+ case "oblique" :
211+ return FontStyle .italic;
212+ }
213+ return FontStyle .normal;
214+ }
215+ return FontStyle .normal;
216+ }
217+
218+ static FontWeight expressionToFontWeight (css.Expression value) {
219+ if (value is css.NumberTerm ) {
220+ switch (value.text) {
221+ case "100" :
222+ return FontWeight .w100;
223+ case "200" :
224+ return FontWeight .w200;
225+ case "300" :
226+ return FontWeight .w300;
227+ case "400" :
228+ return FontWeight .w400;
229+ case "500" :
230+ return FontWeight .w500;
231+ case "600" :
232+ return FontWeight .w600;
233+ case "700" :
234+ return FontWeight .w700;
235+ case "800" :
236+ return FontWeight .w800;
237+ case "900" :
238+ return FontWeight .w900;
239+ }
240+ } else if (value is css.LiteralTerm ) {
241+ switch (value.text) {
242+ case "bold" :
243+ return FontWeight .bold;
244+ case "bolder" :
245+ return FontWeight .w900;
246+ case "lighter" :
247+ return FontWeight .w200;
248+ }
249+ return FontWeight .normal;
250+ }
251+ return FontWeight .normal;
252+ }
253+
254+ static String expressionToFontFamily (css.Expression value) {
255+ if (value is css.LiteralTerm ) return value.text;
256+ return null ;
257+ }
258+
259+ static LineHeight expressionToLineHeight (css.Expression value) {
260+ if (value is css.NumberTerm ) {
261+ return LineHeight .number (double .tryParse (value.text));
262+ } else if (value is css.PercentageTerm ) {
263+ return LineHeight .percent (double .tryParse (value.text));
264+ } else if (value is css.EmTerm ) {
265+ return LineHeight .em (double .tryParse (value.text));
266+ } else if (value is css.RemTerm ) {
267+ return LineHeight .rem (double .tryParse (value.text));
268+ } else if (value is css.LengthTerm ) {
269+ return LineHeight (double .tryParse (value.text.replaceAll (new RegExp (r'\s+(\d+\.\d+)\s+' ), '' )), units: "length" );
270+ }
271+ return LineHeight .normal;
272+ }
273+
274+ static TextAlign expressionToTextAlign (css.Expression value) {
275+ if (value is css.LiteralTerm ) {
276+ switch (value.text) {
277+ case "center" :
278+ return TextAlign .center;
279+ case "left" :
280+ return TextAlign .left;
281+ case "right" :
282+ return TextAlign .right;
283+ case "justify" :
284+ return TextAlign .justify;
285+ case "end" :
286+ return TextAlign .end;
287+ case "start" :
288+ return TextAlign .start;
289+ }
290+ }
291+ return TextAlign .start;
292+ }
293+
294+ static TextDecoration expressionToTextDecorationLine (List <css.LiteralTerm > value) {
295+ List <TextDecoration > decorationList = [];
296+ for (css.LiteralTerm term in value) {
297+ switch (term.text) {
298+ case "overline" :
299+ decorationList.add (TextDecoration .overline);
300+ break ;
301+ case "underline" :
302+ decorationList.add (TextDecoration .underline);
303+ break ;
304+ case "line-through" :
305+ decorationList.add (TextDecoration .lineThrough);
306+ break ;
307+ default :
308+ decorationList.add (TextDecoration .none);
309+ break ;
310+ }
311+ }
312+ if (decorationList.contains (TextDecoration .none)) decorationList = [TextDecoration .none];
313+ return TextDecoration .combine (decorationList);
314+ }
315+
316+ static TextDecorationStyle expressionToTextDecorationStyle (css.LiteralTerm value) {
317+ switch (value.text) {
318+ case "wavy" :
319+ return TextDecorationStyle .wavy;
320+ case "dotted" :
321+ return TextDecorationStyle .dotted;
322+ case "dashed" :
323+ return TextDecorationStyle .dashed;
324+ case "double" :
325+ return TextDecorationStyle .double ;
326+ default :
327+ return TextDecorationStyle .solid;
328+ }
329+ }
330+
331+ static List <Shadow > expressionToTextShadow (List <css.Expression > value) {
332+ List <Shadow > shadow = [];
333+ List <int > indices = [];
334+ List <List <css.Expression >> valueList = [];
335+ for (css.Expression e in value) {
336+ if (e is css.OperatorComma ) {
337+ indices.add (value.indexOf (e));
338+ }
339+ }
340+ indices.add (value.length);
341+ int previousIndex = 0 ;
342+ for (int i in indices) {
343+ valueList.add (value.sublist (previousIndex, i));
344+ previousIndex = i + 1 ;
345+ }
346+ for (List <css.Expression > list in valueList) {
347+ css.Expression exp = list[0 ];
348+ css.Expression exp2 = list[1 ];
349+ css.LiteralTerm exp3 = list.length > 2 ? list[2 ] : null ;
350+ css.LiteralTerm exp4 = list.length > 3 ? list[3 ] : null ;
351+ RegExp nonNumberRegex = RegExp (r'\s+(\d+\.\d+)\s+' );
352+ if (exp is css.LiteralTerm && exp2 is css.LiteralTerm ) {
353+ if (exp3 != null && (exp3 is css.HexColorTerm || exp3 is css.FunctionTerm )) {
354+ shadow.add (Shadow (
355+ color: expressionToColor (exp3),
356+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' )), double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' )))
357+ ));
358+ } else if (exp3 != null && exp3 is css.LiteralTerm ) {
359+ if (exp4 != null && (exp4 is css.HexColorTerm || exp4 is css.FunctionTerm )) {
360+ shadow.add (Shadow (
361+ color: expressionToColor (exp4),
362+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' )), double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))),
363+ blurRadius: double .tryParse (exp3.text.replaceAll (nonNumberRegex, '' ))
364+ ));
365+ } else {
366+ shadow.add (Shadow (
367+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' )), double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' ))),
368+ blurRadius: double .tryParse (exp3.text.replaceAll (nonNumberRegex, '' ))
369+ ));
370+ }
371+ } else {
372+ shadow.add (Shadow (
373+ offset: Offset (double .tryParse (exp.text.replaceAll (nonNumberRegex, '' )), double .tryParse (exp2.text.replaceAll (nonNumberRegex, '' )))
374+ ));
375+ }
376+ }
377+ }
378+ List <Shadow > finalShadows = shadow.toSet ().toList ();
379+ return finalShadows;
380+ }
381+
73382 static Color stringToColor (String _text) {
74383 var text = _text.replaceFirst ('#' , '' );
75384 if (text.length == 3 )
@@ -88,7 +397,7 @@ class ExpressionMapping {
88397 final rgbaText = text.replaceAll (')' , '' ).replaceAll (' ' , '' );
89398 try {
90399 final rgbaValues =
91- rgbaText.split (',' ).map ((value) => double .parse (value)).toList ();
400+ rgbaText.split (',' ).map ((value) => double .parse (value)).toList ();
92401 if (rgbaValues.length == 4 ) {
93402 return Color .fromRGBO (
94403 rgbaValues[0 ].toInt (),
@@ -109,24 +418,4 @@ class ExpressionMapping {
109418 return null ;
110419 }
111420 }
112-
113- static TextAlign expressionToTextAlign (css.Expression value) {
114- if (value is css.LiteralTerm ) {
115- switch (value.text) {
116- case "center" :
117- return TextAlign .center;
118- case "left" :
119- return TextAlign .left;
120- case "right" :
121- return TextAlign .right;
122- case "justify" :
123- return TextAlign .justify;
124- case "end" :
125- return TextAlign .end;
126- case "start" :
127- return TextAlign .start;
128- }
129- }
130- return TextAlign .start;
131- }
132421}
0 commit comments