Skip to content

Add preliminary support for MathML #577

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 9 commits into from
Mar 18, 2021
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
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