Skip to content

[new-parser]Support ruby element. #178

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ const htmlData = """
dog.<br />
The quick brown fox jumped over the lazy dog.
</p>
<p>
<ruby>
漢<rt>かん</rt>
字<rt>じ</rt>
</ruby>
&nbsp;is Japanese Kanji
</p>
<table>
<tr><th>One</th><th>Two</th><th>Three</th></tr>
<tr><td>Data</td><td>Data</td><td>Data</td></tr>
Expand Down
2 changes: 1 addition & 1 deletion lib/html_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class HtmlParser extends StatelessWidget {
return TextSpan(text: tree.text);
} else {
return WidgetSpan(
alignment: PlaceholderAlignment.aboveBaseline,
alignment: tree.alignment,
baseline: TextBaseline.alphabetic,
child: tree.toWidget(context),
);
Expand Down
6 changes: 3 additions & 3 deletions lib/src/html_elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ const STYLED_ELEMENTS = [
"kbd",
"mark",
"q",
"rp",
"rt",
"ruby",
"s",
"samp",
"small",
Expand Down Expand Up @@ -85,6 +82,9 @@ const REPLACED_ELEMENTS = [
"svg",
"template",
"video",
"rp",
"rt",
"ruby",
];

const LAYOUT_ELEMENTS = [
Expand Down
82 changes: 73 additions & 9 deletions lib/src/replaced_element.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:convert';
import 'dart:math';

import 'package:chewie/chewie.dart';
import 'package:chewie_audio/chewie_audio.dart';
Expand All @@ -19,14 +20,19 @@ import 'package:html/dom.dart' as dom;
/// A [ReplacedElement] may use its children nodes to determine relevant information
/// (e.g. <video>'s <source> tags), but the children nodes will not be saved as [children].
abstract class ReplacedElement extends StyledElement {
ReplacedElement({
String name,
Style style,
dom.Element node,
}) : super(name: name, children: null, style: style, node: node);
PlaceholderAlignment alignment;

ReplacedElement(
{String name,
Style style,
dom.Element node,
this.alignment = PlaceholderAlignment.aboveBaseline})
: super(name: name, children: null, style: style, node: node);

static List<String> parseMediaSources(List<dom.Element> elements) {
return elements.where((element) => element.localName == 'source').map((element) {
return elements
.where((element) => element.localName == 'source')
.map((element) {
return element.attributes['src'];
}).toList();
}
Expand Down Expand Up @@ -68,7 +74,8 @@ class ImageContentElement extends ReplacedElement {

@override
Widget toWidget(RenderContext context) {
if (src == null) return Text(alt ?? "", style: context.style.generateTextStyle());
if (src == null)
return Text(alt ?? "", style: context.style.generateTextStyle());
if (src.startsWith("data:image") && src.contains("base64,")) {
return Image.memory(base64.decode(src.split("base64,")[1].trim()));
} else {
Expand Down Expand Up @@ -110,7 +117,9 @@ class IframeContentElement extends ReplacedElement {
child: WebView(
initialUrl: src,
javascriptMode: JavascriptMode.unrestricted,
gestureRecognizers: {Factory(() => PlatformViewVerticalGestureRecognizer())},
gestureRecognizers: {
Factory(() => PlatformViewVerticalGestureRecognizer())
},
),
);
}
Expand Down Expand Up @@ -189,7 +198,9 @@ class VideoContentElement extends ReplacedElement {
videoPlayerController: VideoPlayerController.network(
src.first ?? "",
),
placeholder: poster != null ? Image.network(poster) : Container(color: Colors.black),
placeholder: poster != null
? Image.network(poster)
: Container(color: Colors.black),
autoPlay: autoplay,
looping: loop,
showControls: showControls,
Expand Down Expand Up @@ -229,6 +240,55 @@ class EmptyContentElement extends ReplacedElement {
Widget toWidget(_) => null;
}

class RubyElement extends ReplacedElement {
dom.Element element;

RubyElement({@required this.element, String name = "ruby"})
: super(name: name, alignment: PlaceholderAlignment.middle);

@override
Widget toWidget(RenderContext context) {
dom.Node textNode = null;
List<Widget> widgets = List<Widget>();
final rubySize = max(9.0, context.style.fontSize / 2);
final rubyYPos = rubySize + 2;
element.nodes.forEach((c) {
if (c.nodeType == dom.Node.TEXT_NODE) {
textNode = c;
}
if (c is dom.Element) {
if (c.localName == "rt" && textNode != null) {
final widget = Stack(
alignment: Alignment.center,
children: <Widget>[
Container(
alignment: Alignment.bottomCenter,
child: Center(
child: Transform(
transform:
Matrix4.translationValues(0, -(rubyYPos), 0),
child: Text(c.innerHtml,
style: context.style
.generateTextStyle()
.copyWith(fontSize: rubySize))))),
Container(
child: Text(textNode.text.trim(),
style: context.style.generateTextStyle())),
],
);
widgets.add(widget);
}
}
});
return Row(
crossAxisAlignment: CrossAxisAlignment.end,
textBaseline: TextBaseline.alphabetic,
mainAxisSize: MainAxisSize.min,
children: widgets,
);
}
}

ReplacedElement parseReplacedElement(dom.Element element) {
switch (element.localName) {
case "audio":
Expand Down Expand Up @@ -287,6 +347,10 @@ ReplacedElement parseReplacedElement(dom.Element element) {
width: double.tryParse(element.attributes['width'] ?? ""),
height: double.tryParse(element.attributes['height'] ?? ""),
);
case "ruby":
return RubyElement(
element: element,
);
default:
return EmptyContentElement(name: element.localName);
}
Expand Down