Skip to content

Commit 8bd4e0b

Browse files
authored
Merge pull request Sub6Resources#526 from tneotia/feature/inline-font-size
Add support for various inline styles
2 parents 048a00a + e3b95f7 commit 8bd4e0b

File tree

3 files changed

+367
-31
lines changed

3 files changed

+367
-31
lines changed

lib/html_parser.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,8 @@ class HtmlParser extends StatelessWidget {
661661
child.text.trim().isEmpty &&
662662
lastChildBlock) {
663663
toRemove.add(child);
664+
} else if (child.style.display == Display.NONE) {
665+
toRemove.add(child);
664666
} else {
665667
_removeEmptyElements(child);
666668
}

lib/src/css_parser.dart

Lines changed: 313 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,71 @@ import 'dart:ui';
22

33
import 'package:csslib/visitor.dart' as css;
44
import 'package:csslib/parser.dart' as cssparser;
5+
import 'package:flutter/cupertino.dart';
56
import 'package:flutter_html/style.dart';
67

78
Style 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

Comments
 (0)