Skip to content

Commit 31eeeff

Browse files
committed
Merge remote-tracking branch 'upstream/master' into feature/multiplatform-support
# Conflicts: # lib/src/widgets/iframe_mobile.dart
2 parents 1a1b331 + 9d2888b commit 31eeeff

File tree

7 files changed

+176
-41
lines changed

7 files changed

+176
-41
lines changed

lib/html_parser.dart

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -420,11 +420,7 @@ class HtmlParser extends StatelessWidget {
420420
tree.style.listStylePosition == ListStylePosition.OUTSIDE ?
421421
Padding(
422422
padding: tree.style.padding ?? EdgeInsets.only(left: tree.style.direction != TextDirection.rtl ? 10.0 : 0.0, right: tree.style.direction == TextDirection.rtl ? 10.0 : 0.0),
423-
child: Text(
424-
"${newContext.style.markerContent}",
425-
textAlign: TextAlign.right,
426-
style: newContext.style.generateTextStyle()
427-
),
423+
child: newContext.style.markerContent
428424
) : Container(height: 0, width: 0),
429425
Text("\t", textAlign: TextAlign.right),
430426
Expanded(
@@ -433,11 +429,10 @@ class HtmlParser extends StatelessWidget {
433429
EdgeInsets.only(left: tree.style.direction != TextDirection.rtl ? 10.0 : 0.0, right: tree.style.direction == TextDirection.rtl ? 10.0 : 0.0) : EdgeInsets.zero,
434430
child: StyledText(
435431
textSpan: TextSpan(
436-
text: (tree.style.listStylePosition ==
437-
ListStylePosition.INSIDE)
438-
? '${newContext.style.markerContent}'
439-
: null,
440-
children: getChildren(tree),
432+
children: getChildren(tree)..insertAll(0, tree.style.listStylePosition == ListStylePosition.INSIDE ?
433+
[
434+
WidgetSpan(alignment: PlaceholderAlignment.middle, child: newContext.style.markerContent ?? Container(height: 0, width: 0))
435+
] : []),
441436
style: newContext.style.generateTextStyle(),
442437
),
443438
style: newContext.style,
@@ -451,12 +446,12 @@ class HtmlParser extends StatelessWidget {
451446
);
452447
} else if (tree is ReplacedElement) {
453448
if (tree is TextContentElement) {
454-
return TextSpan(text: tree.text);
449+
return TextSpan(text: tree.text?.transformed(tree.style.textTransform));
455450
} else {
456451
return WidgetSpan(
457452
alignment: tree.alignment,
458453
baseline: TextBaseline.alphabetic,
459-
child: tree.toWidget(context)!,
454+
child: tree.toWidget(newContext)!,
460455
);
461456
}
462457
} else if (tree is InteractableElement) {
@@ -708,7 +703,10 @@ class HtmlParser extends StatelessWidget {
708703
/// bullet all list items according to the [ListStyleType] they have been given.
709704
static StyledElement _processListCharactersRecursive(
710705
StyledElement tree, ListQueue<Context> olStack) {
711-
if (tree.name == 'ol' && tree.style.listStyleType != null) {
706+
if (tree.style.listStylePosition == null) {
707+
tree.style.listStylePosition = ListStylePosition.OUTSIDE;
708+
}
709+
if (tree.name == 'ol' && tree.style.listStyleType != null && tree.style.listStyleType!.type == "marker") {
712710
switch (tree.style.listStyleType!) {
713711
case ListStyleType.LOWER_LATIN:
714712
case ListStyleType.LOWER_ALPHA:
@@ -728,26 +726,31 @@ class HtmlParser extends StatelessWidget {
728726
olStack.add(Context<int>((tree.attributes['start'] != null ? int.tryParse(tree.attributes['start'] ?? "") ?? 1 : 1) - 1));
729727
break;
730728
}
729+
} else if (tree.style.display == Display.LIST_ITEM && tree.style.listStyleType != null && tree.style.listStyleType!.type == "widget") {
730+
tree.style.markerContent = tree.style.listStyleType!.widget!;
731+
} else if (tree.style.display == Display.LIST_ITEM && tree.style.listStyleType != null && tree.style.listStyleType!.type == "image") {
732+
tree.style.markerContent = Image.network(tree.style.listStyleType!.text);
731733
} else if (tree.style.display == Display.LIST_ITEM && tree.style.listStyleType != null) {
734+
String marker = "";
732735
switch (tree.style.listStyleType!) {
733736
case ListStyleType.NONE:
734737
tree.style.markerContent = '';
735738
break;
736739
case ListStyleType.CIRCLE:
737-
tree.style.markerContent = '○';
740+
marker = '○';
738741
break;
739742
case ListStyleType.SQUARE:
740-
tree.style.markerContent = '■';
743+
marker = '■';
741744
break;
742745
case ListStyleType.DISC:
743-
tree.style.markerContent = '•';
746+
marker = '•';
744747
break;
745748
case ListStyleType.DECIMAL:
746749
if (olStack.isEmpty) {
747750
olStack.add(Context<int>((tree.attributes['start'] != null ? int.tryParse(tree.attributes['start'] ?? "") ?? 1 : 1) - 1));
748751
}
749752
olStack.last.data += 1;
750-
tree.style.markerContent = '${olStack.last.data}.';
753+
marker = '${olStack.last.data}.';
751754
break;
752755
case ListStyleType.LOWER_LATIN:
753756
case ListStyleType.LOWER_ALPHA:
@@ -762,7 +765,7 @@ class HtmlParser extends StatelessWidget {
762765
}
763766
}
764767
}
765-
tree.style.markerContent = olStack.last.data.toString() + ".";
768+
marker = olStack.last.data.toString() + ".";
766769
olStack.last.data = olStack.last.data.toString().nextLetter();
767770
break;
768771
case ListStyleType.UPPER_LATIN:
@@ -778,7 +781,7 @@ class HtmlParser extends StatelessWidget {
778781
}
779782
}
780783
}
781-
tree.style.markerContent = olStack.last.data.toString().toUpperCase() + ".";
784+
marker = olStack.last.data.toString().toUpperCase() + ".";
782785
olStack.last.data = olStack.last.data.toString().nextLetter();
783786
break;
784787
case ListStyleType.LOWER_ROMAN:
@@ -787,9 +790,9 @@ class HtmlParser extends StatelessWidget {
787790
}
788791
olStack.last.data += 1;
789792
if (olStack.last.data <= 0) {
790-
tree.style.markerContent = '${olStack.last.data}.';
793+
marker = '${olStack.last.data}.';
791794
} else {
792-
tree.style.markerContent = (olStack.last.data as int).toRomanNumeralString()!.toLowerCase() + ".";
795+
marker = (olStack.last.data as int).toRomanNumeralString()!.toLowerCase() + ".";
793796
}
794797
break;
795798
case ListStyleType.UPPER_ROMAN:
@@ -798,12 +801,16 @@ class HtmlParser extends StatelessWidget {
798801
}
799802
olStack.last.data += 1;
800803
if (olStack.last.data <= 0) {
801-
tree.style.markerContent = '${olStack.last.data}.';
804+
marker = '${olStack.last.data}.';
802805
} else {
803-
tree.style.markerContent = (olStack.last.data as int).toRomanNumeralString()! + ".";
806+
marker = (olStack.last.data as int).toRomanNumeralString()! + ".";
804807
}
805808
break;
806809
}
810+
tree.style.markerContent = Text(
811+
marker,
812+
textAlign: TextAlign.right,
813+
);
807814
}
808815

809816
tree.children.forEach((e) => _processListCharactersRecursive(e, olStack));

lib/src/css_parser.dart

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
150150
List<String> possibleBorderValues = ["dotted", "dashed", "solid", "double", "groove", "ridge", "inset", "outset", "none", "hidden"];
151151
/// 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]
152152
potentialStyles.removeWhere((element) => element == null || !possibleBorderValues.contains(element.text));
153-
css.LiteralTerm borderStyle = potentialStyles.first!;
153+
css.LiteralTerm? borderStyle = potentialStyles.firstOrNull;
154154
Border newBorder = Border(
155155
left: style.border?.left ?? BorderSide.none,
156156
right: style.border?.right ?? BorderSide.none,
@@ -194,6 +194,43 @@ Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
194194
case 'font-weight':
195195
style.fontWeight = ExpressionMapping.expressionToFontWeight(value.first);
196196
break;
197+
case 'list-style':
198+
css.LiteralTerm? position = value.firstWhereOrNull((e) => e is css.LiteralTerm && (e.text == "outside" || e.text == "inside")) as css.LiteralTerm?;
199+
css.UriTerm? image = value.firstWhereOrNull((e) => e is css.UriTerm) as css.UriTerm?;
200+
css.LiteralTerm? type = value.firstWhereOrNull((e) => e is css.LiteralTerm && e.text != "outside" && e.text != "inside") as css.LiteralTerm?;
201+
if (position != null) {
202+
switch (position.text) {
203+
case 'outside':
204+
style.listStylePosition = ListStylePosition.OUTSIDE;
205+
break;
206+
case 'inside':
207+
style.listStylePosition = ListStylePosition.INSIDE;
208+
break;
209+
}
210+
}
211+
if (image != null) {
212+
style.listStyleType = ExpressionMapping.expressionToListStyleType(image) ?? style.listStyleType;
213+
} else if (type != null) {
214+
style.listStyleType = ExpressionMapping.expressionToListStyleType(type) ?? style.listStyleType;
215+
}
216+
break;
217+
case 'list-style-image':
218+
if (value.first is css.UriTerm) {
219+
style.listStyleType = ExpressionMapping.expressionToListStyleType(value.first as css.UriTerm) ?? style.listStyleType;
220+
}
221+
break;
222+
case 'list-style-position':
223+
if (value.first is css.LiteralTerm) {
224+
switch ((value.first as css.LiteralTerm).text) {
225+
case 'outside':
226+
style.listStylePosition = ListStylePosition.OUTSIDE;
227+
break;
228+
case 'inside':
229+
style.listStylePosition = ListStylePosition.INSIDE;
230+
break;
231+
}
232+
}
233+
break;
197234
case 'height':
198235
style.height = ExpressionMapping.expressionToPaddingLength(value.first) ?? style.height;
199236
break;
@@ -301,6 +338,18 @@ Style declarationsToStyle(Map<String, List<css.Expression>> declarations) {
301338
case 'text-shadow':
302339
style.textShadow = ExpressionMapping.expressionToTextShadow(value);
303340
break;
341+
case 'text-transform':
342+
final val = (value.first as css.LiteralTerm).text;
343+
if (val == 'uppercase') {
344+
style.textTransform = TextTransform.uppercase;
345+
} else if (val == 'lowercase') {
346+
style.textTransform = TextTransform.lowercase;
347+
} else if (val == 'capitalize') {
348+
style.textTransform = TextTransform.capitalize;
349+
} else {
350+
style.textTransform = TextTransform.none;
351+
}
352+
break;
304353
case 'width':
305354
style.width = ExpressionMapping.expressionToPaddingLength(value.first) ?? style.width;
306355
break;
@@ -671,6 +720,9 @@ class ExpressionMapping {
671720
}
672721

673722
static ListStyleType? expressionToListStyleType(css.LiteralTerm value) {
723+
if (value is css.UriTerm) {
724+
return ListStyleType.fromImage(value.text);
725+
}
674726
switch (value.text) {
675727
case 'disc':
676728
return ListStyleType.DISC;

lib/src/styled_element.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ StyledElement parseStyledElement(
188188
ExpressionMapping.namedColorToColor(element.attributes['color']!) :
189189
null,
190190
fontFamily: element.attributes['face']?.split(",").first,
191-
fontSize: numberToFontSize(element.attributes['size'] ?? ''),
191+
fontSize: element.attributes['size'] != null ? numberToFontSize(element.attributes['size']!) : null,
192192
);
193193
break;
194194
case "h1":

lib/src/utils.dart

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import 'dart:convert';
22
import 'dart:math';
33

4-
import 'package:flutter/gestures.dart';
54
import 'package:flutter/material.dart';
5+
import 'package:flutter_html/style.dart';
66

77
Map<String, String> namedColors = {
88
"White": "#FFFFFF",
@@ -81,4 +81,34 @@ String getRandString(int len) {
8181
var random = Random.secure();
8282
var values = List<int>.generate(len, (i) => random.nextInt(255));
8383
return base64UrlEncode(values);
84+
}
85+
86+
extension TextTransformUtil on String? {
87+
String? transformed(TextTransform? transform) {
88+
if (this == null) return null;
89+
if (transform == TextTransform.uppercase) {
90+
return this!.toUpperCase();
91+
} else if (transform == TextTransform.lowercase) {
92+
return this!.toLowerCase();
93+
} else if (transform == TextTransform.capitalize) {
94+
final stringBuffer = StringBuffer();
95+
96+
var capitalizeNext = true;
97+
for (final letter in this!.toLowerCase().codeUnits) {
98+
// UTF-16: A-Z => 65-90, a-z => 97-122.
99+
if (capitalizeNext && letter >= 97 && letter <= 122) {
100+
stringBuffer.writeCharCode(letter - 32);
101+
capitalizeNext = false;
102+
} else {
103+
// UTF-16: 32 == space, 46 == period
104+
if (letter == 32 || letter == 46) capitalizeNext = true;
105+
stringBuffer.writeCharCode(letter);
106+
}
107+
}
108+
109+
return stringBuffer.toString();
110+
} else {
111+
return this;
112+
}
113+
}
84114
}

lib/src/widgets/iframe_mobile.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ class IframeContentElement extends ReplacedElement {
3131
return Container(
3232
width: width ?? (height ?? 150) * 2,
3333
height: height ?? (width ?? 300) / 2,
34-
child: webview.WebView(
35-
initialUrl: src,
36-
key: key,
37-
javascriptMode: sandboxMode == null || sandboxMode == "allow-scripts"
34+
child: ContainerSpan(
35+
style: context.style,
36+
newContext: context,
37+
child: webview.WebView(
38+
initialUrl: src,
39+
key: key,
40+
javascriptMode: sandboxMode == null || sandboxMode == "allow-scripts"
3841
? webview.JavascriptMode.unrestricted
3942
: webview.JavascriptMode.disabled,
4043
navigationDelegate: (request) async {
@@ -48,9 +51,10 @@ class IframeContentElement extends ReplacedElement {
4851
return webview.NavigationDecision.navigate;
4952
}
5053
},
51-
gestureRecognizers: {
52-
Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer())
53-
},
54+
gestureRecognizers: {
55+
Factory<VerticalDragGestureRecognizer>(() => VerticalDragGestureRecognizer())
56+
},
57+
),
5458
),
5559
);
5660
}

lib/src/widgets/iframe_web.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,15 @@ class IframeContentElement extends ReplacedElement {
3939
return Container(
4040
width: width ?? (height ?? 150) * 2,
4141
height: height ?? (width ?? 300) / 2,
42-
child: Directionality(
43-
textDirection: TextDirection.ltr,
44-
child: HtmlElementView(
45-
viewType: createdViewId,
46-
)
42+
child: ContainerSpan(
43+
style: context.style,
44+
newContext: context,
45+
child: Directionality(
46+
textDirection: TextDirection.ltr,
47+
child: HtmlElementView(
48+
viewType: createdViewId,
49+
)
50+
),
4751
)
4852
);
4953
}

0 commit comments

Comments
 (0)