Skip to content

Commit ad237b6

Browse files
authored
Merge pull request Sub6Resources#577 from tneotia/master
Add preliminary support for MathML
2 parents 1fa33d8 + ee0c641 commit ad237b6

File tree

8 files changed

+286
-10
lines changed

8 files changed

+286
-10
lines changed

README.md

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
4646
- [customRender](#customrender)
4747

4848
- [onImageError](#onimageerror)
49+
50+
- [onMathError](#onmatherror)
4951

5052
- [onImageTap](#onimagetap)
5153

@@ -75,6 +77,10 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
7577

7678
- [SVG](#svg)
7779

80+
- [MathML](#mathml)
81+
82+
- [Tex](#tex)
83+
7884
- [Table](#table)
7985

8086
- [Notes](#notes)
@@ -100,8 +106,8 @@ Add the following to your `pubspec.yaml` file:
100106
| `mark` | `nav` | `noscript`|`ol` | `p` | `pre` | `q` | `rp` | `rt` | `ruby` | `s` |
101107
| `samp` | `section` | `small` | `span`| `strike` | `strong`| `sub` | `sup` | `summary` | `svg`| `table`|
102108
| `tbody` | `td` | `template` | `tfoot` | `th` | `thead` |`time` | `tr` | `tt` | `u` | `ul` |
103-
| `var` | `video` | | | | | | | | | |
104-
109+
| `var` | `video` | `math`: | `mrow` | `msup` | `msub` | `mover` | `munder` | `msubsup` | `moverunder` | `mfrac` |
110+
| `mlongdiv` | `msqrt` | `mroot` | `mi` | `mn` | `mo` | | | | | |
105111

106112

107113
## Currently Supported CSS Attributes:
@@ -152,6 +158,7 @@ If you would like to modify or sanitize the HTML before rendering it, then `Html
152158
| `onLinkTap` | A function that defines what the widget should do when a link is tapped. The function exposes the `src` of the link as a `String` to use in your implementation. |
153159
| `customRender` | A powerful API that allows you to customize everything when rendering a specific HTML tag. |
154160
| `onImageError` | A function that defines what the widget should do when an image fails to load. The function exposes the exception `Object` and `StackTrace` to use in your implementation. |
161+
| `omMathError` | A function that defines what the widget should do when a math fails to render. The function exposes the parsed Tex `String`, as well as the error and error with type from `flutter_math` as a `String`. |
155162
| `shrinkWrap` | A `bool` used while rendering different widgets to specify whether they should be shrink-wrapped or not, like `ContainerSpan` |
156163
| `onImageTap` | A function that defines what the widget should do when an image is tapped. The function exposes the `src` of the image as a `String` to use in your implementation. |
157164
| `blacklistedElements` | A list of elements the `Html` widget should not render. The list should contain the tags of the HTML elements you wish to blacklist. |
@@ -335,6 +342,24 @@ Widget html = Html(
335342
);
336343
```
337344

345+
### onMathError:
346+
347+
A function that defines what the widget should do when a math fails to render. The function exposes the parsed Tex `String`, as well as the error and error with type from `flutter_math` as a `String`.
348+
349+
#### Example Usage - onMathError:
350+
351+
```dart
352+
Widget html = Html(
353+
data: """<!-- Some MathML string that fails to parse -->""",
354+
onMathError: (String parsedTex, String error, String errorWithType) {
355+
//your logic here. A Widget must be returned from this function:
356+
return Text(error);
357+
//you can also try and fix the parsing yourself:
358+
return Math.tex(correctedParsedTex);
359+
},
360+
);
361+
```
362+
338363
### onImageTap:
339364

340365
A function that defines what the widget should do when an image is tapped.
@@ -679,6 +704,41 @@ This package renders svg elements using the [`flutter_svg`](https://pub.dev/pack
679704

680705
When rendering SVGs, the package takes the SVG data within the `<svg>` tag and passes it to `flutter_svg`. The `width` and `height` attributes are considered while rendering, if given.
681706

707+
### MathML
708+
709+
This package renders MathML elements using the [`flutter_math`](https://pub.dev/packages/flutter_math) plugin.
710+
711+
When rendering MathML, the package takes the MathML data within the `<math>` tag and tries to parse it to Tex. Then, it will pass the parsed string to `flutter_math`.
712+
713+
Because this package is parsing MathML to Tex, it may not support some functionalities. The current list of supported tags can be found [above](#currently-supported-html-tags), but some of these only have partial support at the moment.
714+
715+
If the parsing errors, you can use the [onMathError](#onmatherror) API to catch the error and potentially fix it on your end - you can analyze the error and the parsed string, and finally return a new instance of `Math.tex()` with the corrected Tex string.
716+
717+
If you'd like to see more MathML features, feel free to create a PR or file a feature request!
718+
719+
### Tex
720+
721+
If you have a Tex string you'd like to render inside your HTML you can do that using the same [`flutter_math`](https://pub.dev/packages/flutter_math) plugin.
722+
723+
Use a custom tag inside your HTML (an example could be `<tex>`), and place your **raw** Tex string inside.
724+
725+
Then, use the `customRender` parameter to add the widget to render Tex. It could look like this:
726+
727+
```dart
728+
Widget htmlWidget = Html(
729+
data: r"""<tex>i\hbar\frac{\partial}{\partial t}\Psi(\vec x,t) = -\frac{\hbar}{2m}\nabla^2\Psi(\vec x,t)+ V(\vec x)\Psi(\vec x,t)</tex>""",
730+
customRender: {
731+
"tex": (_, __, ___, element) => Math.tex(
732+
element.text,
733+
onErrorFallback: (FlutterMathException e) {
734+
//return your error widget here e.g.
735+
return Text(e.message);
736+
},
737+
),
738+
}
739+
);
740+
```
741+
682742
### Table
683743

684744
This package renders table elements using the [`flutter_layout_grid`](https://pub.dev/packages/flutter_layout_grid) plugin.

example/lib/main.dart

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ class MyHomePage extends StatefulWidget {
2727
_MyHomePageState createState() => new _MyHomePageState();
2828
}
2929

30-
const htmlData = """
31-
<h1>Header 1</h1>
32-
<h2>Header 2</h2>
33-
<h3>Header 3</h3>
34-
<h4>Header 4</h4>
35-
<h5>Header 5</h5>
36-
<h6>Header 6</h6>
37-
<h3>Ruby Support:</h3>
30+
const htmlData = r"""
31+
<h1>Header 1</h1>
32+
<h2>Header 2</h2>
33+
<h3>Header 3</h3>
34+
<h4>Header 4</h4>
35+
<h5>Header 5</h5>
36+
<h6>Header 6</h6>
37+
<h3>Ruby Support:</h3>
3838
<p>
3939
<ruby>
4040
漢<rt>かん</rt>
@@ -138,6 +138,100 @@ const htmlData = """
138138
<img alt='Empty source' src='' />
139139
<h3>Broken network image</h3>
140140
<img alt='Broken image' src='https://www.notgoogle.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />
141+
<h3>MathML Support:</h3>
142+
<math>
143+
<mrow>
144+
<mi>x</mi>
145+
<mo>=</mo>
146+
<mfrac>
147+
<mrow>
148+
<mrow>
149+
<mo>-</mo>
150+
<mi>b</mi>
151+
</mrow>
152+
<mo>&PlusMinus;</mo>
153+
<msqrt>
154+
<mrow>
155+
<msup>
156+
<mi>b</mi>
157+
<mn>2</mn>
158+
</msup>
159+
<mo>-</mo>
160+
<mrow>
161+
<mn>4</mn>
162+
<mo>&InvisibleTimes;</mo>
163+
<mi>a</mi>
164+
<mo>&InvisibleTimes;</mo>
165+
<mi>c</mi>
166+
</mrow>
167+
</mrow>
168+
</msqrt>
169+
</mrow>
170+
<mrow>
171+
<mn>2</mn>
172+
<mo>&InvisibleTimes;</mo>
173+
<mi>a</mi>
174+
</mrow>
175+
</mfrac>
176+
</mrow>
177+
</math>
178+
<math>
179+
<munderover >
180+
<mo> &int; </mo>
181+
<mn> 0 </mn>
182+
<mi> 5 </mi>
183+
</munderover>
184+
<msup>
185+
<mi>x</mi>
186+
<mn>2</mn>
187+
</msup>
188+
<mo>&sdot;</mo>
189+
<mi>&dd;</mi><mi>x</mi>
190+
<mo>=</mo>
191+
<mo>[</mo>
192+
<mfrac>
193+
<mn>1</mn>
194+
<mi>3</mi>
195+
</mfrac>
196+
<msup>
197+
<mi>x</mi>
198+
<mn>3</mn>
199+
</msup>
200+
<msubsup>
201+
<mo>]</mo>
202+
<mn>0</mn>
203+
<mn>5</mn>
204+
</msubsup>
205+
<mo>=</mo>
206+
<mfrac>
207+
<mn>125</mn>
208+
<mi>3</mi>
209+
</mfrac>
210+
<mo>-</mo>
211+
<mn>0</mn>
212+
<mo>=</mo>
213+
<mfrac>
214+
<mn>125</mn>
215+
<mi>3</mi>
216+
</mfrac>
217+
</math>
218+
<math>
219+
<msup>
220+
<mo>sin</mo>
221+
<mn>2</mn>
222+
</msup>
223+
<mo>&theta;</mo>
224+
<mo>+</mo>
225+
<msup>
226+
<mo>cos</mo>
227+
<mn>2</mn>
228+
</msup>
229+
<mo>&theta;</mo>
230+
<mo>=</mo>
231+
<mn>1</mn>
232+
</math>
233+
<h3>Tex Support with the custom tex tag:</h3>
234+
<tex>i\hbar\frac{\partial}{\partial t}\Psi(\vec x,t) = -\frac{\hbar}{2m}\nabla^2\Psi(\vec x,t)+ V(\vec x)\Psi(\vec x,t)</tex>
141235
""";
142236

143237
class _MyHomePageState extends State<MyHomePage> {

lib/flutter_html.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Html extends StatelessWidget {
4040
this.customRender = const {},
4141
this.customImageRenders = const {},
4242
this.onImageError,
43+
this.onMathError,
4344
this.shrinkWrap = false,
4445
this.onImageTap,
4546
this.blacklistedElements = const [],
@@ -56,6 +57,7 @@ class Html extends StatelessWidget {
5657
this.customRender = const {},
5758
this.customImageRenders = const {},
5859
this.onImageError,
60+
this.onMathError,
5961
this.shrinkWrap = false,
6062
this.onImageTap,
6163
this.blacklistedElements = const [],
@@ -81,6 +83,11 @@ class Html extends StatelessWidget {
8183
/// A function that defines what to do when an image errors
8284
final ImageErrorListener? onImageError;
8385

86+
/// A function that defines what to do when either <math> or <tex> fails to render
87+
/// You can return a widget here to override the default error widget.
88+
final OnMathError? onMathError;
89+
90+
8491
/// A parameter that should be set when the HTML widget is expected to be
8592
/// flexible
8693
final bool shrinkWrap;
@@ -115,6 +122,7 @@ class Html extends StatelessWidget {
115122
onLinkTap: onLinkTap,
116123
onImageTap: onImageTap,
117124
onImageError: onImageError,
125+
onMathError: onMathError,
118126
shrinkWrap: shrinkWrap,
119127
style: style,
120128
customRender: customRender,

lib/html_parser.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ typedef OnTap = void Function(
2222
Map<String, String> attributes,
2323
dom.Element? element,
2424
);
25+
typedef OnMathError = Widget Function(
26+
String parsedTex,
27+
String exception,
28+
String exceptionWithType,
29+
);
2530
typedef CustomRender = dynamic Function(
2631
RenderContext context,
2732
Widget parsedChild,
@@ -34,6 +39,7 @@ class HtmlParser extends StatelessWidget {
3439
final OnTap? onLinkTap;
3540
final OnTap? onImageTap;
3641
final ImageErrorListener? onImageError;
42+
final OnMathError? onMathError;
3743
final bool shrinkWrap;
3844

3945
final Map<String, Style> style;
@@ -47,6 +53,7 @@ class HtmlParser extends StatelessWidget {
4753
required this.onLinkTap,
4854
required this.onImageTap,
4955
required this.onImageError,
56+
required this.onMathError,
5057
required this.shrinkWrap,
5158
required this.style,
5259
required this.customRender,

lib/src/html_elements.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ const REPLACED_ELEMENTS = [
8686
"rp",
8787
"rt",
8888
"ruby",
89+
"math",
8990
];
9091

9192
const LAYOUT_ELEMENTS = [

0 commit comments

Comments
 (0)