Skip to content
Open
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ SelectableMath(
## [Line Breaking](doc/line_breaking.md)

## Credits
This project is possible thanks to the inspirations and resources from [the KaTeX Project](https://katex.org/), [MathJax](www.mathjax.org), [Zefyr](https://github.com/memspace/zefyr), and [CaTeX](https://github.com/simpleclub/CaTeX).
This project is possible thanks to the inspirations and resources from [the KaTeX Project](https://katex.org/), [MathJax](www.mathjax.org), [Zefyr](https://github.com/memspace/zefyr), and [CaTeX](https://github.com/simpleclub/CaTeX), and [flutter_html_math](https://github.com/Sub6Resources/flutter_html).

## Goals
- [x] : TeX math parsing (See [design doc](doc/design.md))
Expand All @@ -117,4 +117,4 @@ This project is possible thanks to the inspirations and resources from [the KaTe
- [ ] : UnicodeMath parsing and encoding
- [ ] : [UnicodeMath](https://www.unicode.org/notes/tn28/UTN28-PlainTextMath-v3.1.pdf)-style editing
- [ ] : Breakable equations
- [ ] : MathML parsing and encoding
- [X] : MathML parsing and encoding
6 changes: 1 addition & 5 deletions example/lib/equations.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_math_fork/flutter_math.dart';
import 'package:google_fonts/google_fonts.dart';

const equations = [
['Solution of quadratic equation', r'x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}'],
Expand Down Expand Up @@ -37,10 +36,7 @@ class EquationsPage extends StatelessWidget {
children: [
ListTile(
title: Text(entry[0]),
subtitle: SelectableText(
entry[1],
style: GoogleFonts.robotoMono(),
),
subtitle: SelectableText(entry[1]),
),
Container(
padding: const EdgeInsets.fromLTRB(1, 5, 1, 5),
Expand Down
36 changes: 22 additions & 14 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ packages:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
sha256: bfe67ef28df125b7dddcea62755991f807aa39a2492a23e1550161692950bbe0
url: "https://pub.dev"
source: hosted
version: "2.11.0"
version: "2.10.0"
boolean_selector:
dependency: transitive
description:
Expand Down Expand Up @@ -65,6 +65,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.3"
csslib:
dependency: transitive
description:
name: csslib
sha256: b36c7f7e24c0bdf1bf9a3da461c837d1de64b9f8beb190c9011d8c72a3dfd745
url: "https://pub.dev"
source: hosted
version: "0.17.2"
fake_async:
dependency: transitive
description:
Expand Down Expand Up @@ -122,14 +130,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.0.3+4"
google_fonts:
dependency: "direct main"
html:
dependency: transitive
description:
name: google_fonts
sha256: "6b6f10f0ce3c42f6552d1c70d2c28d764cf22bb487f50f66cca31dcd5194f4d6"
name: html
sha256: "79d498e6d6761925a34ee5ea8fa6dfef38607781d2fa91e37523474282af55cb"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
version: "0.15.2"
http:
dependency: transitive
description:
Expand Down Expand Up @@ -166,10 +174,10 @@ packages:
dependency: transitive
description:
name: matcher
sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb"
sha256: c94db23593b89766cda57aab9ac311e3616cf87c6fa4e9749df032f66f30dcb8
url: "https://pub.dev"
source: hosted
version: "0.12.15"
version: "0.12.14"
material_color_utilities:
dependency: transitive
description:
Expand All @@ -182,10 +190,10 @@ packages:
dependency: transitive
description:
name: meta
sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3"
sha256: "12307e7f0605ce3da64cf0db90e5fcab0869f3ca03f76be6bb2991ce0a55e82b"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
version: "1.9.0"
mime:
dependency: transitive
description:
Expand Down Expand Up @@ -218,7 +226,7 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
path_provider:
path_provider:
dependency: transitive
description:
name: path_provider
Expand Down Expand Up @@ -355,10 +363,10 @@ packages:
dependency: transitive
description:
name: test_api
sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb
sha256: "6182294da5abf431177fccc1ee02401f6df30f766bc6130a0852c6b6d7ee6b2d"
url: "https://pub.dev"
source: hosted
version: "0.5.1"
version: "0.4.18"
tuple:
dependency: transitive
description:
Expand Down
1 change: 0 additions & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ dependencies:
path: ../

provider: any
flutter_tex: ^4.0.3+4
google_fonts: ^4.0.4

dev_dependencies:
Expand Down
4 changes: 3 additions & 1 deletion lib/src/render/svg/draw_svg_root.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter_svg/flutter_svg.dart';

void drawSvgRoot(PictureInfo svgRoot, PaintingContext context, Offset offset) {
if (svgRoot.picture == null) return;

final canvas = context.canvas;
canvas.save();
canvas.translate(offset.dx, offset.dy);
Expand All @@ -15,6 +17,6 @@ void drawSvgRoot(PictureInfo svgRoot, PaintingContext context, Offset offset) {
svgRoot.size.width,
svgRoot.size.height,
));
canvas.drawPicture(svgRoot.picture);
canvas.drawPicture(svgRoot.picture!);
canvas.restore();
}
99 changes: 99 additions & 0 deletions lib/src/utils/mathml_to_tex.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import 'package:html/dom.dart' as dom;

String parseMathMLRecursive(dom.Node node, String parsed) {
if (node is dom.Element) {
List<dom.Element> nodeList = node.nodes.whereType<dom.Element>().toList();
if (node.localName == "math" ||
node.localName == "mrow" ||
node.localName == "mtr") {
for (var element in nodeList) {
parsed = parseMathMLRecursive(element, parsed);
}
}
// note: munder, mover, and munderover do not support placing braces and other
// markings above/below elements, instead they are treated as super/subscripts for now.
if ((node.localName == "msup" ||
node.localName == "msub" ||
node.localName == "munder" ||
node.localName == "mover") &&
nodeList.length == 2) {
parsed = parseMathMLRecursive(nodeList[0], parsed);
parsed =
"${parseMathMLRecursive(nodeList[1], "$parsed${node.localName == "msup" || node.localName == "mover" ? "^" : "_"}{")}}";
}
if ((node.localName == "msubsup" || node.localName == "munderover") &&
nodeList.length == 3) {
parsed = parseMathMLRecursive(nodeList[0], parsed);
parsed = "${parseMathMLRecursive(nodeList[1], "${parsed}_{")}}";
parsed = "${parseMathMLRecursive(nodeList[2], "$parsed^{")}}";
}
if (node.localName == "mfrac" && nodeList.length == 2) {
parsed = "${parseMathMLRecursive(nodeList[0], parsed + r"\frac{")}}";
parsed = "${parseMathMLRecursive(nodeList[1], "$parsed{")}}";
}
// note: doesn't support answer & intermediate steps
if (node.localName == "mlongdiv" && nodeList.length == 4) {
parsed = parseMathMLRecursive(nodeList[0], parsed);
parsed = "${parseMathMLRecursive(nodeList[2], parsed + r"\overline{)")}}";
}
if (node.localName == "msqrt") {
parsed = parsed + r"\sqrt{";
for (var element in nodeList) {
parsed = parseMathMLRecursive(element, parsed);
}
parsed = "$parsed}";
}
if (node.localName == "mroot" && nodeList.length == 2) {
parsed = "${parseMathMLRecursive(nodeList[1], parsed + r"\sqrt[")}]";
parsed = "${parseMathMLRecursive(nodeList[0], "$parsed{")}}";
}
if (node.localName == "mi" ||
node.localName == "mn" ||
node.localName == "mo") {
if (_mathML2Tex.keys.contains(node.text.trim())) {
parsed = parsed +
_mathML2Tex[
_mathML2Tex.keys.firstWhere((e) => e == node.text.trim())]!;
} else if (node.text.startsWith("&") && node.text.endsWith(";")) {
parsed = parsed +
node.text
.trim()
.replaceFirst("&", r"\")
.substring(0, node.text.trim().length - 1);
} else {
parsed = parsed + node.text.trim();
}
}
if (node.localName == 'mtable') {
String inner =
nodeList.map((e) => parseMathMLRecursive(e, '')).join(' \\\\');
parsed = '$parsed\\begin{matrix}$inner\\end{matrix}';
}
if (node.localName == "mtd") {
for (var element in nodeList) {
parsed = parseMathMLRecursive(element, parsed);
}
parsed = '$parsed & ';
}
}
return parsed;
}

Map<String, String> _mathML2Tex = {
"sin": r"\sin",
"sinh": r"\sinh",
"csc": r"\csc",
"csch": r"csch",
"cos": r"\cos",
"cosh": r"\cosh",
"sec": r"\sec",
"sech": r"\sech",
"tan": r"\tan",
"tanh": r"\tanh",
"cot": r"\cot",
"coth": r"\coth",
"log": r"\log",
"ln": r"\ln",
"{": r"\{",
"}": r"\}",
};
58 changes: 58 additions & 0 deletions lib/src/widgets/math.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import 'package:flutter/material.dart';
import 'package:html/parser.dart';
import 'package:provider/provider.dart';
import 'package:html/dom.dart' as dom;

import '../ast/options.dart';
import '../ast/style.dart';
Expand All @@ -8,6 +10,7 @@ import '../ast/tex_break.dart';
import '../parser/tex/parse_error.dart';
import '../parser/tex/parser.dart';
import '../parser/tex/settings.dart';
import '../utils/mathml_to_tex.dart';
import 'exception.dart';
import 'mode.dart';
import 'selectable.dart';
Expand Down Expand Up @@ -163,6 +166,61 @@ class Math extends StatelessWidget {
);
}

/// Math builder using a MathML string
///
/// {@template flutter_math_fork.widgets.math.tex_builder}
/// [expression] will first be parsed under [settings]. Then the acquired
/// [SyntaxTree] will be built under a specific options. If [ParseException]
/// is thrown or a build error occurs, [onErrorFallback] will be displayed.
///
/// You can control the options via [mathStyle] and [textStyle].
/// {@endtemplate}
///
/// See alse:
///
/// * [Math.mathStyle]
/// * [Math.textStyle]
factory Math.mathML(
String expression, {
Key? key,
MathStyle mathStyle = MathStyle.display,
TextStyle? textStyle,
OnErrorFallback onErrorFallback = defaultOnErrorFallback,
TexParserSettings settings = const TexParserSettings(),
double? textScaleFactor,
MathOptions? options,
}) {
return Math.mathMLFromDom(parse(expression));
}

/// Math builder using a Math Element
///
/// {@template flutter_math_fork.widgets.math.tex_builder}
/// [expression] will first be parsed under [settings]. Then the acquired
/// [SyntaxTree] will be built under a specific options. If [ParseException]
/// is thrown or a build error occurs, [onErrorFallback] will be displayed.
///
/// You can control the options via [mathStyle] and [textStyle].
/// {@endtemplate}
///
/// See alse:
///
/// * [Math.mathStyle]
/// * [Math.textStyle]
factory Math.mathMLFromDom(
dom.Node node, {
Key? key,
MathStyle mathStyle = MathStyle.display,
TextStyle? textStyle,
OnErrorFallback onErrorFallback = defaultOnErrorFallback,
TexParserSettings settings = const TexParserSettings(),
double? textScaleFactor,
MathOptions? options,
}) {
final expression = parseMathMLRecursive(node, r'');
return Math.tex(expression);
}

@override
Widget build(BuildContext context) {
if (parseError != null) {
Expand Down
9 changes: 6 additions & 3 deletions lib/src/widgets/selection/gesture_detector_builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,11 @@ class MathSelectionGestureDetectorBuilder {
}
}

late TapDragStartDetails startDetails;

@protected
void onDragSelectionStart(TapDragStartDetails details) {
startDetails = details;
delegate.selectPositionAt(
from: details.globalPosition,
cause: SelectionChangedCause.drag,
Expand All @@ -167,10 +170,10 @@ class MathSelectionGestureDetectorBuilder {
}

@protected
void onDragSelectionUpdate(TapDragUpdateDetails details) {
void onDragSelectionUpdate(TapDragUpdateDetails updateDetails) {
delegate.selectPositionAt(
from: details.globalPosition - details.offsetFromOrigin,
to: details.globalPosition,
from: startDetails.globalPosition,
to: updateDetails.globalPosition,
cause: SelectionChangedCause.drag,
);
}
Expand Down
4 changes: 3 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ environment:
dependencies:
flutter:
sdk: flutter

flutter_svg: ">=2.0.0+1 <3.0.0"
provider: ^6.0.5
meta: ^1.8.0
collection: ^1.17.0
tuple: ^2.0.1
# Plugin for parsing html
html: ^0.15.0

dev_dependencies:
flutter_test:
Expand Down