diff --git a/.github/flutter_html_screenshot.png b/.github/flutter_html_screenshot.png
index d8ecc1ac28..3d1b893b61 100644
Binary files a/.github/flutter_html_screenshot.png and b/.github/flutter_html_screenshot.png differ
diff --git a/.github/flutter_html_screenshot2.png b/.github/flutter_html_screenshot2.png
index dae0da9143..eb47b0e146 100644
Binary files a/.github/flutter_html_screenshot2.png and b/.github/flutter_html_screenshot2.png differ
diff --git a/example/lib/main.dart b/example/lib/main.dart
index b16831ba73..26c7173873 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -46,6 +46,16 @@ const htmlData = """
Support for sub
/sup
Solve for xn: log2(x2+n) = 93
One of the most common equations in all of physics is
E=mc2.
+ Inline Styles:
+ The should be BLUE style='color: blue;'
+ The should be RED style='color: red;'
+ The should be BLACK with 10% alpha style='color: rgba(0, 0, 0, 0.10);
+ The should be GREEN style='color: rgb(0, 97, 0);
+ The should be GREEN style='color: rgb(0, 97, 0);
+ blasdafjklasdlkjfkl
+ blasdafjklasdlkjfkl
+ blasdafjklasdlkjfkl
+ blasdafjklasdlkjfkl
Table support (with custom styling!):
Famous quote...
diff --git a/lib/html_parser.dart b/lib/html_parser.dart
index da6601b887..f4787b14f6 100644
--- a/lib/html_parser.dart
+++ b/lib/html_parser.dart
@@ -5,6 +5,7 @@ import 'package:csslib/parser.dart' as cssparser;
import 'package:csslib/visitor.dart' as css;
import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';
+import 'package:flutter_html/src/css_parser.dart';
import 'package:flutter_html/src/html_elements.dart';
import 'package:flutter_html/src/layout_element.dart';
import 'package:flutter_html/src/utils.dart';
@@ -152,10 +153,12 @@ class HtmlParser extends StatelessWidget {
return tree;
}
- ///TODO document
static StyledElement applyInlineStyles(StyledElement tree) {
- //TODO
+ if (tree.attributes.containsKey("style")) {
+ tree.style = tree.style.merge(inlineCSSToStyle(tree.attributes['style']));
+ }
+ tree.children?.forEach(applyInlineStyles);
return tree;
}
diff --git a/lib/src/css_parser.dart b/lib/src/css_parser.dart
new file mode 100644
index 0000000000..7182ac17d6
--- /dev/null
+++ b/lib/src/css_parser.dart
@@ -0,0 +1,132 @@
+import 'dart:ui';
+
+import 'package:csslib/visitor.dart' as css;
+import 'package:csslib/parser.dart' as cssparser;
+import 'package:flutter_html/style.dart';
+
+Style declarationsToStyle(Map> declarations) {
+ Style style = new Style();
+ declarations.forEach((property, value) {
+ switch (property) {
+ case 'background-color':
+ style.backgroundColor =
+ ExpressionMapping.expressionToColor(value.first);
+ break;
+ case 'color':
+ style.color = ExpressionMapping.expressionToColor(value.first);
+ break;
+ case 'text-align':
+ style.textAlign = ExpressionMapping.expressionToTextAlign(value.first);
+ break;
+
+ }
+ });
+ return style;
+}
+
+Style inlineCSSToStyle(String inlineStyle) {
+ final sheet = cssparser.parse("*{$inlineStyle}");
+ final declarations = DeclarationVisitor().getDeclarations(sheet);
+ return declarationsToStyle(declarations);
+}
+
+class DeclarationVisitor extends css.Visitor {
+ Map> _result;
+ String _currentProperty;
+
+ Map> getDeclarations(css.StyleSheet sheet) {
+ _result = new Map>();
+ sheet.visit(this);
+ return _result;
+ }
+
+ @override
+ void visitDeclaration(css.Declaration node) {
+ _currentProperty = node.property;
+ _result[_currentProperty] = new List();
+ node.expression.visit(this);
+ }
+
+ @override
+ void visitExpressions(css.Expressions node) {
+ node.expressions.forEach((expression) {
+ _result[_currentProperty].add(expression);
+ });
+ }
+}
+
+//Mapping functions
+class ExpressionMapping {
+ static Color expressionToColor(css.Expression value) {
+ if (value is css.HexColorTerm) {
+ return stringToColor(value.text);
+ } else if (value is css.FunctionTerm) {
+ if (value.text == 'rgba') {
+ return rgbOrRgbaToColor(value.span.text);
+ } else if (value.text == 'rgb') {
+ return rgbOrRgbaToColor(value.span.text);
+ }
+ }
+ return null;
+ }
+
+ static Color stringToColor(String _text) {
+ var text = _text.replaceFirst('#', '');
+ if (text.length == 3)
+ text = text.replaceAllMapped(
+ RegExp(r"[a-f]|\d"), (match) => '${match.group(0)}${match.group(0)}');
+ int color = int.parse(text, radix: 16);
+
+ if (color <= 0xffffff) {
+ return new Color(color).withAlpha(255);
+ } else {
+ return new Color(color);
+ }
+ }
+
+ static Color rgbOrRgbaToColor(String text) {
+ final rgbaText = text.replaceAll(')', '').replaceAll(' ', '');
+ try {
+ final rgbaValues =
+ rgbaText.split(',').map((value) => double.parse(value)).toList();
+ if (rgbaValues.length == 4) {
+ return Color.fromRGBO(
+ rgbaValues[0].toInt(),
+ rgbaValues[1].toInt(),
+ rgbaValues[2].toInt(),
+ rgbaValues[3],
+ );
+ } else if (rgbaValues.length == 3) {
+ return Color.fromRGBO(
+ rgbaValues[0].toInt(),
+ rgbaValues[1].toInt(),
+ rgbaValues[2].toInt(),
+ 1.0,
+ );
+ }
+ return null;
+ } catch (e) {
+ return null;
+ }
+ }
+
+ static TextAlign expressionToTextAlign(css.Expression value) {
+ if (value is css.LiteralTerm) {
+ switch(value.text) {
+ case "center":
+ return TextAlign.center;
+ case "left":
+ return TextAlign.left;
+ case "right":
+ return TextAlign.right;
+ case "justify":
+ return TextAlign.justify;
+ case "end":
+ return TextAlign.end;
+ case "start":
+ return TextAlign.start;
+ }
+ }
+ return TextAlign.start;
+ }
+}
diff --git a/lib/src/styled_element.dart b/lib/src/styled_element.dart
index aaa0dc90e9..508b72fe3d 100644
--- a/lib/src/styled_element.dart
+++ b/lib/src/styled_element.dart
@@ -25,9 +25,11 @@ class StyledElement {
bool matchesSelector(String selector) =>
_node != null && matches(_node, selector);
- Map get attributes => _node.attributes.map((key, value) {
+ Map get attributes =>
+ _node?.attributes?.map((key, value) {
return MapEntry(key, value);
- });
+ }) ??
+ Map();
dom.Element get element => _node;
diff --git a/test/goldens/p-with-inline-css-text-align-center.png b/test/goldens/p-with-inline-css-text-align-center.png
new file mode 100644
index 0000000000..e1a5195435
Binary files /dev/null and b/test/goldens/p-with-inline-css-text-align-center.png differ
diff --git a/test/goldens/p-with-inline-css-text-align-end.png b/test/goldens/p-with-inline-css-text-align-end.png
new file mode 100644
index 0000000000..f262fac575
Binary files /dev/null and b/test/goldens/p-with-inline-css-text-align-end.png differ
diff --git a/test/goldens/p-with-inline-css-text-align-justify.png b/test/goldens/p-with-inline-css-text-align-justify.png
new file mode 100644
index 0000000000..29d8b14aad
Binary files /dev/null and b/test/goldens/p-with-inline-css-text-align-justify.png differ
diff --git a/test/goldens/p-with-inline-css-text-align-left.png b/test/goldens/p-with-inline-css-text-align-left.png
new file mode 100644
index 0000000000..29d8b14aad
Binary files /dev/null and b/test/goldens/p-with-inline-css-text-align-left.png differ
diff --git a/test/goldens/p-with-inline-css-text-align-right.png b/test/goldens/p-with-inline-css-text-align-right.png
new file mode 100644
index 0000000000..f262fac575
Binary files /dev/null and b/test/goldens/p-with-inline-css-text-align-right.png differ
diff --git a/test/goldens/p-with-inline-css-text-align-start.png b/test/goldens/p-with-inline-css-text-align-start.png
new file mode 100644
index 0000000000..29d8b14aad
Binary files /dev/null and b/test/goldens/p-with-inline-css-text-align-start.png differ
diff --git a/test/goldens/span-with-inline-css-backgroundcolor-rgb.png b/test/goldens/span-with-inline-css-backgroundcolor-rgb.png
new file mode 100644
index 0000000000..e895a3d1c3
Binary files /dev/null and b/test/goldens/span-with-inline-css-backgroundcolor-rgb.png differ
diff --git a/test/goldens/span-with-inline-css-backgroundcolor-rgba.png b/test/goldens/span-with-inline-css-backgroundcolor-rgba.png
new file mode 100644
index 0000000000..21451008d1
Binary files /dev/null and b/test/goldens/span-with-inline-css-backgroundcolor-rgba.png differ
diff --git a/test/goldens/span-with-inline-css-backgroundcolor.png b/test/goldens/span-with-inline-css-backgroundcolor.png
new file mode 100644
index 0000000000..e9d25a0a2b
Binary files /dev/null and b/test/goldens/span-with-inline-css-backgroundcolor.png differ
diff --git a/test/goldens/span-with-inline-css-color-rgb.png b/test/goldens/span-with-inline-css-color-rgb.png
new file mode 100644
index 0000000000..24d5c73cf4
Binary files /dev/null and b/test/goldens/span-with-inline-css-color-rgb.png differ
diff --git a/test/goldens/span-with-inline-css-color-rgba.png b/test/goldens/span-with-inline-css-color-rgba.png
new file mode 100644
index 0000000000..1e6846e9e4
Binary files /dev/null and b/test/goldens/span-with-inline-css-color-rgba.png differ
diff --git a/test/goldens/span-with-inline-css-color.png b/test/goldens/span-with-inline-css-color.png
new file mode 100644
index 0000000000..aa8b473034
Binary files /dev/null and b/test/goldens/span-with-inline-css-color.png differ
diff --git a/test/test_data.dart b/test/test_data.dart
index a23d7c43b5..c2fc16a601 100644
--- a/test/test_data.dart
+++ b/test/test_data.dart
@@ -49,6 +49,12 @@ const testData = {
'nav': '',
'noscript': '',
'p': 'Hello, World!
',
+ 'p-with-inline-css-text-align-center': 'Hello, World!
',
+ 'p-with-inline-css-text-align-right': 'Hello, World!
',
+ 'p-with-inline-css-text-align-left': 'Hello, World!
',
+ 'p-with-inline-css-text-align-justify': 'Hello, World!
',
+ 'p-with-inline-css-text-align-end': 'Hello, World!
',
+ 'p-with-inline-css-text-align-start': 'Hello, World!
',
'pre': 'Hello, World!
',
'q': 'Hello, World!
',
'rp': '漢 ',
@@ -59,6 +65,12 @@ const testData = {
'section': '',
'small': 'Hello, World!',
'span': 'Hello, World!',
+ 'span-with-inline-css-color': 'Hello, World!
',
+ 'span-with-inline-css-color-rgb': 'Hello, World!
',
+ 'span-with-inline-css-color-rgba': 'Hello, World!
',
+ 'span-with-inline-css-backgroundcolor': 'Hello, World!
',
+ 'span-with-inline-css-backgroundcolor-rgb': 'Hello, World!
',
+ 'span-with-inline-css-backgroundcolor-rgba': 'Hello, World!
',
'strike': 'Hello, World!',
'strong': 'Hello, World!',
'sub': 'Hello, World!',