Skip to content

Commit

Permalink
Merge pull request #577 from tneotia/master
Browse files Browse the repository at this point in the history
Add preliminary support for MathML
  • Loading branch information
erickok authored Mar 18, 2021
2 parents 1fa33d8 + ee0c641 commit ad237b6
Show file tree
Hide file tree
Showing 8 changed files with 286 additions and 10 deletions.
64 changes: 62 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ A Flutter widget for rendering HTML and CSS as Flutter widgets.
- [customRender](#customrender)

- [onImageError](#onimageerror)

- [onMathError](#onmatherror)

- [onImageTap](#onimagetap)

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

- [SVG](#svg)

- [MathML](#mathml)

- [Tex](#tex)

- [Table](#table)

- [Notes](#notes)
Expand All @@ -100,8 +106,8 @@ Add the following to your `pubspec.yaml` file:
| `mark` | `nav` | `noscript`|`ol` | `p` | `pre` | `q` | `rp` | `rt` | `ruby` | `s` |
| `samp` | `section` | `small` | `span`| `strike` | `strong`| `sub` | `sup` | `summary` | `svg`| `table`|
| `tbody` | `td` | `template` | `tfoot` | `th` | `thead` |`time` | `tr` | `tt` | `u` | `ul` |
| `var` | `video` | | | | | | | | | |

| `var` | `video` | `math`: | `mrow` | `msup` | `msub` | `mover` | `munder` | `msubsup` | `moverunder` | `mfrac` |
| `mlongdiv` | `msqrt` | `mroot` | `mi` | `mn` | `mo` | | | | | |


## Currently Supported CSS Attributes:
Expand Down Expand Up @@ -152,6 +158,7 @@ If you would like to modify or sanitize the HTML before rendering it, then `Html
| `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. |
| `customRender` | A powerful API that allows you to customize everything when rendering a specific HTML tag. |
| `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. |
| `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`. |
| `shrinkWrap` | A `bool` used while rendering different widgets to specify whether they should be shrink-wrapped or not, like `ContainerSpan` |
| `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. |
| `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. |
Expand Down Expand Up @@ -335,6 +342,24 @@ Widget html = Html(
);
```

### onMathError:

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`.

#### Example Usage - onMathError:

```dart
Widget html = Html(
data: """<!-- Some MathML string that fails to parse -->""",
onMathError: (String parsedTex, String error, String errorWithType) {
//your logic here. A Widget must be returned from this function:
return Text(error);
//you can also try and fix the parsing yourself:
return Math.tex(correctedParsedTex);
},
);
```

### onImageTap:

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

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.

### MathML

This package renders MathML elements using the [`flutter_math`](https://pub.dev/packages/flutter_math) plugin.

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`.

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.

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.

If you'd like to see more MathML features, feel free to create a PR or file a feature request!

### Tex

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.

Use a custom tag inside your HTML (an example could be `<tex>`), and place your **raw** Tex string inside.

Then, use the `customRender` parameter to add the widget to render Tex. It could look like this:

```dart
Widget htmlWidget = Html(
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>""",
customRender: {
"tex": (_, __, ___, element) => Math.tex(
element.text,
onErrorFallback: (FlutterMathException e) {
//return your error widget here e.g.
return Text(e.message);
},
),
}
);
```

### Table

This package renders table elements using the [`flutter_layout_grid`](https://pub.dev/packages/flutter_layout_grid) plugin.
Expand Down
110 changes: 102 additions & 8 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => new _MyHomePageState();
}

const htmlData = """
<h1>Header 1</h1>
<h2>Header 2</h2>
<h3>Header 3</h3>
<h4>Header 4</h4>
<h5>Header 5</h5>
<h6>Header 6</h6>
<h3>Ruby Support:</h3>
const htmlData = r"""
<h1>Header 1</h1>
<h2>Header 2</h2>
<h3>Header 3</h3>
<h4>Header 4</h4>
<h5>Header 5</h5>
<h6>Header 6</h6>
<h3>Ruby Support:</h3>
<p>
<ruby>
漢<rt>かん</rt>
Expand Down Expand Up @@ -138,6 +138,100 @@ const htmlData = """
<img alt='Empty source' src='' />
<h3>Broken network image</h3>
<img alt='Broken image' src='https://www.notgoogle.com/images/branding/googlelogo/2x/googlelogo_color_92x30dp.png' />
<h3>MathML Support:</h3>
<math>
<mrow>
<mi>x</mi>
<mo>=</mo>
<mfrac>
<mrow>
<mrow>
<mo>-</mo>
<mi>b</mi>
</mrow>
<mo>&PlusMinus;</mo>
<msqrt>
<mrow>
<msup>
<mi>b</mi>
<mn>2</mn>
</msup>
<mo>-</mo>
<mrow>
<mn>4</mn>
<mo>&InvisibleTimes;</mo>
<mi>a</mi>
<mo>&InvisibleTimes;</mo>
<mi>c</mi>
</mrow>
</mrow>
</msqrt>
</mrow>
<mrow>
<mn>2</mn>
<mo>&InvisibleTimes;</mo>
<mi>a</mi>
</mrow>
</mfrac>
</mrow>
</math>
<math>
<munderover >
<mo> &int; </mo>
<mn> 0 </mn>
<mi> 5 </mi>
</munderover>
<msup>
<mi>x</mi>
<mn>2</mn>
</msup>
<mo>&sdot;</mo>
<mi>&dd;</mi><mi>x</mi>
<mo>=</mo>
<mo>[</mo>
<mfrac>
<mn>1</mn>
<mi>3</mi>
</mfrac>
<msup>
<mi>x</mi>
<mn>3</mn>
</msup>
<msubsup>
<mo>]</mo>
<mn>0</mn>
<mn>5</mn>
</msubsup>
<mo>=</mo>
<mfrac>
<mn>125</mn>
<mi>3</mi>
</mfrac>
<mo>-</mo>
<mn>0</mn>
<mo>=</mo>
<mfrac>
<mn>125</mn>
<mi>3</mi>
</mfrac>
</math>
<math>
<msup>
<mo>sin</mo>
<mn>2</mn>
</msup>
<mo>&theta;</mo>
<mo>+</mo>
<msup>
<mo>cos</mo>
<mn>2</mn>
</msup>
<mo>&theta;</mo>
<mo>=</mo>
<mn>1</mn>
</math>
<h3>Tex Support with the custom tex tag:</h3>
<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>
""";

class _MyHomePageState extends State<MyHomePage> {
Expand Down
8 changes: 8 additions & 0 deletions lib/flutter_html.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class Html extends StatelessWidget {
this.customRender = const {},
this.customImageRenders = const {},
this.onImageError,
this.onMathError,
this.shrinkWrap = false,
this.onImageTap,
this.blacklistedElements = const [],
Expand All @@ -56,6 +57,7 @@ class Html extends StatelessWidget {
this.customRender = const {},
this.customImageRenders = const {},
this.onImageError,
this.onMathError,
this.shrinkWrap = false,
this.onImageTap,
this.blacklistedElements = const [],
Expand All @@ -81,6 +83,11 @@ class Html extends StatelessWidget {
/// A function that defines what to do when an image errors
final ImageErrorListener? onImageError;

/// A function that defines what to do when either <math> or <tex> fails to render
/// You can return a widget here to override the default error widget.
final OnMathError? onMathError;


/// A parameter that should be set when the HTML widget is expected to be
/// flexible
final bool shrinkWrap;
Expand Down Expand Up @@ -115,6 +122,7 @@ class Html extends StatelessWidget {
onLinkTap: onLinkTap,
onImageTap: onImageTap,
onImageError: onImageError,
onMathError: onMathError,
shrinkWrap: shrinkWrap,
style: style,
customRender: customRender,
Expand Down
7 changes: 7 additions & 0 deletions lib/html_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ typedef OnTap = void Function(
Map<String, String> attributes,
dom.Element? element,
);
typedef OnMathError = Widget Function(
String parsedTex,
String exception,
String exceptionWithType,
);
typedef CustomRender = dynamic Function(
RenderContext context,
Widget parsedChild,
Expand All @@ -34,6 +39,7 @@ class HtmlParser extends StatelessWidget {
final OnTap? onLinkTap;
final OnTap? onImageTap;
final ImageErrorListener? onImageError;
final OnMathError? onMathError;
final bool shrinkWrap;

final Map<String, Style> style;
Expand All @@ -47,6 +53,7 @@ class HtmlParser extends StatelessWidget {
required this.onLinkTap,
required this.onImageTap,
required this.onImageError,
required this.onMathError,
required this.shrinkWrap,
required this.style,
required this.customRender,
Expand Down
1 change: 1 addition & 0 deletions lib/src/html_elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ const REPLACED_ELEMENTS = [
"rp",
"rt",
"ruby",
"math",
];

const LAYOUT_ELEMENTS = [
Expand Down
Loading

0 comments on commit ad237b6

Please sign in to comment.