diff --git a/lib/model/content.dart b/lib/model/content.dart
index 72c8240133..fbd56b006e 100644
--- a/lib/model/content.dart
+++ b/lib/model/content.dart
@@ -341,8 +341,8 @@ class CodeBlockSpanNode extends ContentNode {
}
}
-class MathBlockNode extends BlockContentNode {
- const MathBlockNode({
+abstract class MathNode extends ContentNode {
+ const MathNode({
super.debugHtmlNode,
required this.texSource,
required this.nodes,
@@ -369,8 +369,12 @@ class MathBlockNode extends BlockContentNode {
}
}
-class KatexNode extends ContentNode {
- const KatexNode({
+sealed class KatexNode extends ContentNode {
+ const KatexNode({super.debugHtmlNode});
+}
+
+class KatexSpanNode extends KatexNode {
+ const KatexSpanNode({
required this.styles,
required this.text,
required this.nodes,
@@ -402,6 +406,71 @@ class KatexNode extends ContentNode {
}
}
+class KatexVlistNode extends KatexNode {
+ const KatexVlistNode({
+ required this.rows,
+ super.debugHtmlNode,
+ });
+
+ final List rows;
+
+ @override
+ List debugDescribeChildren() {
+ return rows.map((row) => row.toDiagnosticsNode()).toList();
+ }
+}
+
+class KatexVlistRowNode extends ContentNode {
+ const KatexVlistRowNode({
+ required this.verticalOffsetEm,
+ this.nodes = const [],
+ });
+
+ final double verticalOffsetEm;
+ final List nodes;
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(StringProperty('verticalOffsetEm', '$verticalOffsetEm'));
+ }
+
+ @override
+ List debugDescribeChildren() {
+ return nodes.map((node) => node.toDiagnosticsNode()).toList();
+ }
+}
+
+class KatexNegativeMarginNode extends KatexNode {
+ const KatexNegativeMarginNode({
+ required this.marginRightEm,
+ required this.nodes,
+ super.debugHtmlNode,
+ });
+
+ final double marginRightEm;
+ final List nodes;
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(StringProperty('marginRightEm', '$marginRightEm'));
+ }
+
+ @override
+ List debugDescribeChildren() {
+ return nodes.map((node) => node.toDiagnosticsNode()).toList();
+ }
+}
+
+class MathBlockNode extends MathNode implements BlockContentNode {
+ const MathBlockNode({
+ super.debugHtmlNode,
+ required super.texSource,
+ required super.nodes,
+ });
+}
+
class ImageNodeList extends BlockContentNode {
const ImageNodeList(this.images, {super.debugHtmlNode});
@@ -863,26 +932,12 @@ class ImageEmojiNode extends EmojiNode {
}
}
-class MathInlineNode extends InlineContentNode {
+class MathInlineNode extends MathNode implements InlineContentNode {
const MathInlineNode({
super.debugHtmlNode,
- required this.texSource,
- required this.nodes,
+ required super.texSource,
+ required super.nodes,
});
-
- final String texSource;
- final List? nodes;
-
- @override
- void debugFillProperties(DiagnosticPropertiesBuilder properties) {
- super.debugFillProperties(properties);
- properties.add(StringProperty('texSource', texSource));
- }
-
- @override
- List debugDescribeChildren() {
- return nodes?.map((node) => node.toDiagnosticsNode()).toList() ?? const [];
- }
}
class GlobalTimeNode extends InlineContentNode {
diff --git a/lib/model/katex.dart b/lib/model/katex.dart
index fc71025b59..4c71ba16b5 100644
--- a/lib/model/katex.dart
+++ b/lib/model/katex.dart
@@ -1,3 +1,5 @@
+import 'package:csslib/parser.dart' as css_parser;
+import 'package:csslib/visitor.dart' as css_visitor;
import 'package:flutter/foundation.dart';
import 'package:html/dom.dart' as dom;
@@ -121,13 +123,29 @@ class _KatexParser {
}
List _parseChildSpans(dom.Element element) {
- return List.unmodifiable(element.nodes.map((node) {
- if (node case dom.Element(localName: 'span')) {
- return _parseSpan(node);
- } else {
+ var resultSpans = [];
+ for (final node in element.nodes.reversed) {
+ if (node is! dom.Element || node.localName != 'span') {
throw KatexHtmlParseError();
}
- }));
+
+ final span = _parseSpan(node);
+ resultSpans.add(span);
+
+ if (span is KatexSpanNode) {
+ final marginRightEm = span.styles.marginRightEm;
+ if (marginRightEm != null && marginRightEm.isNegative) {
+ final previousSpansReversed =
+ resultSpans.reversed.toList(growable: false);
+ resultSpans = [];
+ resultSpans.add(KatexNegativeMarginNode(
+ marginRightEm: marginRightEm,
+ nodes: previousSpansReversed));
+ }
+ }
+ }
+
+ return resultSpans.reversed.toList(growable: false);
}
static final _resetSizeClassRegExp = RegExp(r'^reset-size(\d\d?)$');
@@ -138,150 +156,230 @@ class _KatexParser {
final spanClasses = List.unmodifiable(element.className.split(' '));
+ if (element case dom.Element(localName: 'span', :final className)
+ when className.startsWith('vlist')) {
+ switch (element) {
+ case dom.Element(
+ localName: 'span',
+ className: 'vlist-t',
+ attributes: final attributesVlistT,
+ nodes: [
+ dom.Element(
+ localName: 'span',
+ className: 'vlist-r',
+ attributes: final attributesVlistR,
+ nodes: [
+ dom.Element(
+ localName: 'span',
+ className: 'vlist',
+ nodes: [
+ dom.Element(
+ localName: 'span',
+ className: '',
+ nodes: [
+ dom.Element(localName: 'span', className: 'pstrut')
+ && final pstrutSpan,
+ ...,
+ ]) && final innerSpan,
+ ]),
+ ]),
+ ])
+ when !attributesVlistT.containsKey('style') &&
+ !attributesVlistR.containsKey('style'):
+ // TODO vlist element should only have `height` style, which we ignore.
+
+ var styles = _parseSpanInlineStyles(innerSpan)!;
+ final topEm = styles.topEm ?? 0;
+
+ final pstrutStyles = _parseSpanInlineStyles(pstrutSpan)!;
+ final pstrutHeight = pstrutStyles.heightEm ?? 0;
+
+ // TODO handle negative right-margin inline style on row nodes.
+ return KatexVlistNode(rows: [
+ KatexVlistRowNode(
+ verticalOffsetEm: topEm + pstrutHeight,
+ nodes: _parseChildSpans(innerSpan)),
+ ]);
+
+ case dom.Element(
+ localName: 'span',
+ className: 'vlist-t vlist-t2',
+ attributes: final attributesVlistT,
+ nodes: [
+ dom.Element(
+ localName: 'span',
+ className: 'vlist-r',
+ attributes: final attributesVlistR,
+ nodes: [
+ dom.Element(
+ localName: 'span',
+ className: 'vlist',
+ nodes: [...]) && final vlist1,
+ dom.Element(localName: 'span', className: 'vlist-s'),
+ ]),
+ dom.Element(localName: 'span', className: 'vlist-r', nodes: [
+ dom.Element(localName: 'span', className: 'vlist', nodes: [
+ dom.Element(localName: 'span', className: '', nodes: []),
+ ])
+ ]),
+ ])
+ when !attributesVlistT.containsKey('style') &&
+ !attributesVlistR.containsKey('style'):
+ // TODO Ensure both should only have a `height` style.
+
+ final rows = [];
+
+ for (final innerSpan in vlist1.nodes) {
+ if (innerSpan case dom.Element(
+ localName: 'span',
+ className: '',
+ nodes: [
+ dom.Element(localName: 'span', className: 'pstrut') &&
+ final pstrutSpan,
+ ...,
+ ])) {
+ final styles = _parseSpanInlineStyles(innerSpan)!;
+ final topEm = styles.topEm ?? 0;
+
+ final pstrutStyles = _parseSpanInlineStyles(pstrutSpan)!;
+ final pstrutHeight = pstrutStyles.heightEm ?? 0;
+
+ // TODO handle negative right-margin inline style on row nodes.
+ rows.add(KatexVlistRowNode(
+ verticalOffsetEm: topEm + pstrutHeight,
+ nodes: _parseChildSpans(innerSpan)));
+ }
+ }
+
+ return KatexVlistNode(rows: rows);
+
+ default:
+ throw KatexHtmlParseError();
+ }
+ }
+
// Aggregate the CSS styles that apply, in the same order as the CSS
// classes specified for this span, mimicking the behaviour on web.
//
- // Each case in the switch blocks below is a separate CSS class definition
+ // Each case in the switch block below is a separate CSS class definition
// in the same order as in katex.scss :
// https://github.com/KaTeX/KaTeX/blob/2fe1941b/src/styles/katex.scss
// A copy of class definition (where possible) is accompanied in a comment
// with each case statement to keep track of updates.
- var styles = KatexSpanStyles();
+ String? fontFamily;
+ double? fontSizeEm;
+ KatexSpanFontWeight? fontWeight;
+ KatexSpanFontStyle? fontStyle;
+ KatexSpanTextAlign? textAlign;
var index = 0;
while (index < spanClasses.length) {
- var classFound = false;
-
- final spanClass = spanClasses[index];
+ final spanClass = spanClasses[index++];
switch (spanClass) {
case 'base':
// .base { ... }
// Do nothing, it has properties that don't need special handling.
- classFound = true;
+ break;
case 'strut':
// .strut { ... }
// Do nothing, it has properties that don't need special handling.
- classFound = true;
+ break;
case 'textbf':
// .textbf { font-weight: bold; }
- styles.fontWeight = KatexSpanFontWeight.bold;
- classFound = true;
+ fontWeight = KatexSpanFontWeight.bold;
case 'textit':
// .textit { font-style: italic; }
- styles.fontStyle = KatexSpanFontStyle.italic;
- classFound = true;
+ fontStyle = KatexSpanFontStyle.italic;
case 'textrm':
// .textrm { font-family: KaTeX_Main; }
- styles.fontFamily = 'KaTeX_Main';
- classFound = true;
+ fontFamily = 'KaTeX_Main';
- case 'textsf':
- // .textsf { font-family: KaTeX_SansSerif; }
- styles.fontFamily = 'KaTeX_SansSerif';
- classFound = true;
+ // case 'textsf':
+ // // .textsf { font-family: KaTeX_SansSerif; }
+ // This CSS rule has no effect, because the other `.textsf` rule below
+ // has the exact same list of declarations. Handle it there instead.
case 'texttt':
// .texttt { font-family: KaTeX_Typewriter; }
- styles.fontFamily = 'KaTeX_Typewriter';
- classFound = true;
+ fontFamily = 'KaTeX_Typewriter';
case 'mathnormal':
// .mathnormal { font-family: KaTeX_Math; font-style: italic; }
- styles.fontFamily = 'KaTeX_Math';
- styles.fontStyle = KatexSpanFontStyle.italic;
- classFound = true;
+ fontFamily = 'KaTeX_Math';
+ fontStyle = KatexSpanFontStyle.italic;
case 'mathit':
// .mathit { font-family: KaTeX_Main; font-style: italic; }
- styles.fontFamily = 'KaTeX_Main';
- styles.fontStyle = KatexSpanFontStyle.italic;
- classFound = true;
+ fontFamily = 'KaTeX_Main';
+ fontStyle = KatexSpanFontStyle.italic;
case 'mathrm':
// .mathrm { font-style: normal; }
- styles.fontStyle = KatexSpanFontStyle.normal;
- classFound = true;
+ fontStyle = KatexSpanFontStyle.normal;
case 'mathbf':
// .mathbf { font-family: KaTeX_Main; font-weight: bold; }
- styles.fontFamily = 'KaTeX_Main';
- styles.fontWeight = KatexSpanFontWeight.bold;
- classFound = true;
+ fontFamily = 'KaTeX_Main';
+ fontWeight = KatexSpanFontWeight.bold;
case 'boldsymbol':
// .boldsymbol { font-family: KaTeX_Math; font-weight: bold; font-style: italic; }
- styles.fontFamily = 'KaTeX_Math';
- styles.fontWeight = KatexSpanFontWeight.bold;
- styles.fontStyle = KatexSpanFontStyle.italic;
- classFound = true;
+ fontFamily = 'KaTeX_Math';
+ fontWeight = KatexSpanFontWeight.bold;
+ fontStyle = KatexSpanFontStyle.italic;
case 'amsrm':
// .amsrm { font-family: KaTeX_AMS; }
- styles.fontFamily = 'KaTeX_AMS';
- classFound = true;
+ fontFamily = 'KaTeX_AMS';
case 'mathbb':
case 'textbb':
// .mathbb,
// .textbb { font-family: KaTeX_AMS; }
- styles.fontFamily = 'KaTeX_AMS';
- classFound = true;
+ fontFamily = 'KaTeX_AMS';
case 'mathcal':
// .mathcal { font-family: KaTeX_Caligraphic; }
- styles.fontFamily = 'KaTeX_Caligraphic';
- classFound = true;
+ fontFamily = 'KaTeX_Caligraphic';
case 'mathfrak':
case 'textfrak':
// .mathfrak,
// .textfrak { font-family: KaTeX_Fraktur; }
- styles.fontFamily = 'KaTeX_Fraktur';
- classFound = true;
+ fontFamily = 'KaTeX_Fraktur';
case 'mathboldfrak':
case 'textboldfrak':
// .mathboldfrak,
// .textboldfrak { font-family: KaTeX_Fraktur; font-weight: bold; }
- styles.fontFamily = 'KaTeX_Fraktur';
- styles.fontWeight = KatexSpanFontWeight.bold;
- classFound = true;
+ fontFamily = 'KaTeX_Fraktur';
+ fontWeight = KatexSpanFontWeight.bold;
case 'mathtt':
// .mathtt { font-family: KaTeX_Typewriter; }
- styles.fontFamily = 'KaTeX_Typewriter';
- classFound = true;
+ fontFamily = 'KaTeX_Typewriter';
case 'mathscr':
case 'textscr':
// .mathscr,
// .textscr { font-family: KaTeX_Script; }
- styles.fontFamily = 'KaTeX_Script';
- classFound = true;
- }
+ fontFamily = 'KaTeX_Script';
- // We can't add the case for the next class (.mathsf, .textsf) in the
- // above switch block, because there is already a case for .textsf above.
- // So start a new block, to keep the order of the cases here same as the
- // CSS class definitions in katex.scss .
- switch (spanClass) {
case 'mathsf':
case 'textsf':
// .mathsf,
// .textsf { font-family: KaTeX_SansSerif; }
- styles.fontFamily = 'KaTeX_SansSerif';
- classFound = true;
+ fontFamily = 'KaTeX_SansSerif';
case 'mathboldsf':
case 'textboldsf':
// .mathboldsf,
// .textboldsf { font-family: KaTeX_SansSerif; font-weight: bold; }
- styles.fontFamily = 'KaTeX_SansSerif';
- styles.fontWeight = KatexSpanFontWeight.bold;
- classFound = true;
+ fontFamily = 'KaTeX_SansSerif';
+ fontWeight = KatexSpanFontWeight.bold;
case 'mathsfit':
case 'mathitsf':
@@ -289,15 +387,13 @@ class _KatexParser {
// .mathsfit,
// .mathitsf,
// .textitsf { font-family: KaTeX_SansSerif; font-style: italic; }
- styles.fontFamily = 'KaTeX_SansSerif';
- styles.fontStyle = KatexSpanFontStyle.italic;
- classFound = true;
+ fontFamily = 'KaTeX_SansSerif';
+ fontStyle = KatexSpanFontStyle.italic;
case 'mainrm':
// .mainrm { font-family: KaTeX_Main; font-style: normal; }
- styles.fontFamily = 'KaTeX_Main';
- styles.fontStyle = KatexSpanFontStyle.normal;
- classFound = true;
+ fontFamily = 'KaTeX_Main';
+ fontStyle = KatexSpanFontStyle.normal;
// TODO handle skipped class declarations between .mainrm and
// .sizing .
@@ -306,92 +402,69 @@ class _KatexParser {
case 'fontsize-ensurer':
// .sizing,
// .fontsize-ensurer { ... }
- if (index + 2 < spanClasses.length) {
- final resetSizeClass = spanClasses[index + 1];
- final sizeClass = spanClasses[index + 2];
+ if (index + 2 > spanClasses.length) throw KatexHtmlParseError();
+ final resetSizeClass = spanClasses[index++];
+ final sizeClass = spanClasses[index++];
- final resetSizeClassSuffix = _resetSizeClassRegExp.firstMatch(resetSizeClass)?.group(1);
- final sizeClassSuffix = _sizeClassRegExp.firstMatch(sizeClass)?.group(1);
+ final resetSizeClassSuffix = _resetSizeClassRegExp.firstMatch(resetSizeClass)?.group(1);
+ if (resetSizeClassSuffix == null) throw KatexHtmlParseError();
+ final sizeClassSuffix = _sizeClassRegExp.firstMatch(sizeClass)?.group(1);
+ if (sizeClassSuffix == null) throw KatexHtmlParseError();
- if (resetSizeClassSuffix != null && sizeClassSuffix != null) {
- const sizes = [0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.44, 1.728, 2.074, 2.488];
+ const sizes = [0.5, 0.6, 0.7, 0.8, 0.9, 1, 1.2, 1.44, 1.728, 2.074, 2.488];
- final resetSizeIdx = int.parse(resetSizeClassSuffix, radix: 10);
- final sizeIdx = int.parse(sizeClassSuffix, radix: 10);
-
- // These indexes start at 1.
- if (resetSizeIdx <= sizes.length && sizeIdx <= sizes.length) {
- styles.fontSizeEm = sizes[sizeIdx - 1] / sizes[resetSizeIdx - 1];
- index += 3;
- continue;
- }
- }
- }
+ final resetSizeIdx = int.parse(resetSizeClassSuffix, radix: 10);
+ final sizeIdx = int.parse(sizeClassSuffix, radix: 10);
- throw KatexHtmlParseError();
+ // These indexes start at 1.
+ if (resetSizeIdx > sizes.length) throw KatexHtmlParseError();
+ if (sizeIdx > sizes.length) throw KatexHtmlParseError();
+ fontSizeEm = sizes[sizeIdx - 1] / sizes[resetSizeIdx - 1];
case 'delimsizing':
// .delimsizing { ... }
- if (index + 1 < spanClasses.length) {
- final nextClass = spanClasses[index + 1];
- switch (nextClass) {
- case 'size1':
- styles.fontFamily = 'KaTeX_Size1';
- case 'size2':
- styles.fontFamily = 'KaTeX_Size2';
- case 'size3':
- styles.fontFamily = 'KaTeX_Size3';
- case 'size4':
- styles.fontFamily = 'KaTeX_Size4';
-
- case 'mult':
- // TODO handle nested spans with `.delim-size{1,4}` class.
- break;
- }
-
- if (styles.fontFamily == null) throw KatexHtmlParseError();
-
- index += 2;
- continue;
- }
-
- throw KatexHtmlParseError();
+ if (index + 1 > spanClasses.length) throw KatexHtmlParseError();
+ fontFamily = switch (spanClasses[index++]) {
+ 'size1' => 'KaTeX_Size1',
+ 'size2' => 'KaTeX_Size2',
+ 'size3' => 'KaTeX_Size3',
+ 'size4' => 'KaTeX_Size4',
+ 'mult' =>
+ // TODO handle nested spans with `.delim-size{1,4}` class.
+ throw KatexHtmlParseError(),
+ _ => throw KatexHtmlParseError(),
+ };
// TODO handle .nulldelimiter and .delimcenter .
case 'op-symbol':
// .op-symbol { ... }
- if (index + 1 < spanClasses.length) {
- final nextClass = spanClasses[index + 1];
- switch (nextClass) {
- case 'small-op':
- styles.fontFamily = 'KaTeX_Size1';
- case 'large-op':
- styles.fontFamily = 'KaTeX_Size2';
- }
- if (styles.fontFamily == null) throw KatexHtmlParseError();
-
- index += 2;
- continue;
- }
-
- throw KatexHtmlParseError();
+ if (index + 1 > spanClasses.length) throw KatexHtmlParseError();
+ fontFamily = switch (spanClasses[index++]) {
+ 'small-op' => 'KaTeX_Size1',
+ 'large-op' => 'KaTeX_Size2',
+ _ => throw KatexHtmlParseError(),
+ };
// TODO handle more classes from katex.scss
- }
- // Ignore these classes because they don't have a CSS definition
- // in katex.scss, but we encounter them in the generated HTML.
- switch (spanClass) {
case 'mord':
case 'mopen':
- classFound = true;
- }
-
- if (!classFound) _logError('KaTeX: Unsupported CSS class: $spanClass');
+ // Ignore these classes because they don't have a CSS definition
+ // in katex.scss, but we encounter them in the generated HTML.
+ break;
- index++;
+ default:
+ _logError('KaTeX: Unsupported CSS class: $spanClass');
+ }
}
+ final styles = KatexSpanStyles(
+ fontFamily: fontFamily,
+ fontSizeEm: fontSizeEm,
+ fontWeight: fontWeight,
+ fontStyle: fontStyle,
+ textAlign: textAlign,
+ );
String? text;
List? spans;
@@ -402,11 +475,79 @@ class _KatexParser {
}
if (text == null && spans == null) throw KatexHtmlParseError();
- return KatexNode(
- styles: styles,
+ final inlineStyles = _parseSpanInlineStyles(element);
+
+ return KatexSpanNode(
+ styles: inlineStyles != null
+ ? styles.merge(inlineStyles)
+ : styles,
text: text,
nodes: spans);
}
+
+ KatexSpanStyles? _parseSpanInlineStyles(dom.Element element) {
+ if (element.attributes case {'style': final styleStr}) {
+ // `package:csslib` doesn't seem to have a way to parse inline styles:
+ // https://github.com/dart-lang/tools/issues/1173
+ // So, workaround that by wrapping it in a universal declaration.
+ final stylesheet = css_parser.parse('*{$styleStr}');
+ if (stylesheet.topLevels case [css_visitor.RuleSet() && final rule]) {
+ double? heightEm;
+ double? marginRightEm;
+ double? topEm;
+ double? verticalAlignEm;
+
+ for (final declaration in rule.declarationGroup.declarations) {
+ if (declaration case css_visitor.Declaration(
+ :final property,
+ expression: css_visitor.Expressions(
+ expressions: [css_visitor.Expression() && final expression]),
+ )) {
+ switch (property) {
+ case 'height':
+ heightEm = _getEm(expression);
+ if (heightEm != null) continue;
+
+ case 'margin-right':
+ marginRightEm = _getEm(expression);
+ if (marginRightEm != null) continue;
+
+ case 'top':
+ topEm = _getEm(expression);
+ if (topEm != null) continue;
+
+ case 'vertical-align':
+ verticalAlignEm = _getEm(expression);
+ if (verticalAlignEm != null) continue;
+ }
+
+ // TODO handle more CSS properties
+ _logError('KaTeX: Unsupported CSS property: $property of '
+ 'type ${expression.runtimeType}');
+ } else {
+ throw KatexHtmlParseError();
+ }
+ }
+
+ return KatexSpanStyles(
+ heightEm: heightEm,
+ marginRightEm: marginRightEm,
+ topEm: topEm,
+ verticalAlignEm: verticalAlignEm,
+ );
+ } else {
+ throw KatexHtmlParseError();
+ }
+ }
+ return null;
+ }
+
+ double? _getEm(css_visitor.Expression expression) {
+ if (expression is css_visitor.EmTerm && expression.value is num) {
+ return (expression.value as num).toDouble();
+ }
+ return null;
+ }
}
enum KatexSpanFontWeight {
@@ -424,14 +565,24 @@ enum KatexSpanTextAlign {
right,
}
+@immutable
class KatexSpanStyles {
- String? fontFamily;
- double? fontSizeEm;
- KatexSpanFontWeight? fontWeight;
- KatexSpanFontStyle? fontStyle;
- KatexSpanTextAlign? textAlign;
-
- KatexSpanStyles({
+ final double? heightEm;
+ final double? marginRightEm;
+ final double? topEm;
+ final double? verticalAlignEm;
+
+ final String? fontFamily;
+ final double? fontSizeEm;
+ final KatexSpanFontWeight? fontWeight;
+ final KatexSpanFontStyle? fontStyle;
+ final KatexSpanTextAlign? textAlign;
+
+ const KatexSpanStyles({
+ this.heightEm,
+ this.marginRightEm,
+ this.topEm,
+ this.verticalAlignEm,
this.fontFamily,
this.fontSizeEm,
this.fontWeight,
@@ -442,6 +593,10 @@ class KatexSpanStyles {
@override
int get hashCode => Object.hash(
'KatexSpanStyles',
+ heightEm,
+ marginRightEm,
+ topEm,
+ verticalAlignEm,
fontFamily,
fontSizeEm,
fontWeight,
@@ -452,6 +607,10 @@ class KatexSpanStyles {
@override
bool operator ==(Object other) {
return other is KatexSpanStyles &&
+ other.heightEm == heightEm &&
+ other.marginRightEm == marginRightEm &&
+ other.topEm == topEm &&
+ other.verticalAlignEm == verticalAlignEm &&
other.fontFamily == fontFamily &&
other.fontSizeEm == fontSizeEm &&
other.fontWeight == fontWeight &&
@@ -462,6 +621,10 @@ class KatexSpanStyles {
@override
String toString() {
final args = [];
+ if (heightEm != null) args.add('heightEm: $heightEm');
+ if (marginRightEm != null) args.add('marginRightEm: $marginRightEm');
+ if (topEm != null) args.add('topEm: $topEm');
+ if (verticalAlignEm != null) args.add('verticalAlignEm: $verticalAlignEm');
if (fontFamily != null) args.add('fontFamily: $fontFamily');
if (fontSizeEm != null) args.add('fontSizeEm: $fontSizeEm');
if (fontWeight != null) args.add('fontWeight: $fontWeight');
@@ -469,6 +632,20 @@ class KatexSpanStyles {
if (textAlign != null) args.add('textAlign: $textAlign');
return '${objectRuntimeType(this, 'KatexSpanStyles')}(${args.join(', ')})';
}
+
+ KatexSpanStyles merge(KatexSpanStyles other) {
+ return KatexSpanStyles(
+ heightEm: other.heightEm ?? heightEm,
+ marginRightEm: other.marginRightEm ?? marginRightEm,
+ topEm: other.topEm ?? topEm,
+ verticalAlignEm: other.verticalAlignEm ?? verticalAlignEm,
+ fontFamily: other.fontFamily ?? fontFamily,
+ fontSizeEm: other.fontSizeEm ?? fontSizeEm,
+ fontStyle: other.fontStyle ?? fontStyle,
+ fontWeight: other.fontWeight ?? fontWeight,
+ textAlign: other.textAlign ?? textAlign,
+ );
+ }
}
class KatexHtmlParseError extends Error {
diff --git a/lib/widgets/content.dart b/lib/widgets/content.dart
index 4bf6f8adae..b7035dac7f 100644
--- a/lib/widgets/content.dart
+++ b/lib/widgets/content.dart
@@ -816,35 +816,39 @@ class MathBlock extends StatelessWidget {
children: [TextSpan(text: node.texSource)])));
}
- return _Katex(inline: false, nodes: nodes);
+ return _Katex(
+ inline: false,
+ textStyle: ContentTheme.of(context).textStylePlainParagraph,
+ nodes: nodes);
}
}
// Base text style from .katex class in katex.scss :
// https://github.com/KaTeX/KaTeX/blob/613c3da8/src/styles/katex.scss#L13-L15
-const kBaseKatexTextStyle = TextStyle(
- fontSize: kBaseFontSize * 1.21,
- fontFamily: 'KaTeX_Main',
- height: 1.2);
+TextStyle mkBaseKatexTextStyle(TextStyle style) {
+ assert(style.fontSize != null);
+ return style.copyWith(
+ fontSize: style.fontSize! * 1.21,
+ fontFamily: 'KaTeX_Main',
+ height: 1.2,
+ fontWeight: FontWeight.normal,
+ fontStyle: FontStyle.normal);
+}
class _Katex extends StatelessWidget {
const _Katex({
required this.inline,
+ required this.textStyle,
required this.nodes,
});
final bool inline;
+ final TextStyle textStyle;
final List nodes;
@override
Widget build(BuildContext context) {
- Widget widget = Text.rich(TextSpan(
- children: List.unmodifiable(nodes.map((e) {
- return WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: _KatexSpan(e));
- }))));
+ Widget widget = _KatexNodeList(nodes: nodes);
if (!inline) {
widget = Center(
@@ -856,35 +860,49 @@ class _Katex extends StatelessWidget {
return Directionality(
textDirection: TextDirection.ltr,
child: DefaultTextStyle(
- style: kBaseKatexTextStyle.copyWith(
- color: ContentTheme.of(context).textStylePlainParagraph.color),
+ style: mkBaseKatexTextStyle(textStyle),
child: widget));
}
}
+class _KatexNodeList extends StatelessWidget {
+ const _KatexNodeList({required this.nodes});
+
+ final List nodes;
+
+ @override
+ Widget build(BuildContext context) {
+ return Text.rich(TextSpan(
+ children: List.unmodifiable(nodes.map((e) {
+ return WidgetSpan(
+ alignment: PlaceholderAlignment.baseline,
+ baseline: TextBaseline.alphabetic,
+ child: switch (e) {
+ KatexSpanNode() => _KatexSpan(e),
+ KatexVlistNode() => _KatexVlist(e),
+ KatexNegativeMarginNode() => _KatexNegativeMargin(e),
+ });
+ }))));
+ }
+}
+
class _KatexSpan extends StatelessWidget {
- const _KatexSpan(this.span);
+ const _KatexSpan(this.node);
- final KatexNode span;
+ final KatexSpanNode node;
@override
Widget build(BuildContext context) {
final em = DefaultTextStyle.of(context).style.fontSize!;
Widget widget = const SizedBox.shrink();
- if (span.text != null) {
- widget = Text(span.text!);
- } else if (span.nodes != null && span.nodes!.isNotEmpty) {
- widget = Text.rich(TextSpan(
- children: List.unmodifiable(span.nodes!.map((e) {
- return WidgetSpan(
- alignment: PlaceholderAlignment.baseline,
- baseline: TextBaseline.alphabetic,
- child: _KatexSpan(e));
- }))));
+ if (node.text != null) {
+ widget = Text(node.text!);
+ } else if (node.nodes != null && node.nodes!.isNotEmpty) {
+ widget = _KatexNodeList(nodes: node.nodes!);
}
- final styles = span.styles;
+ final styles = node.styles;
final fontFamily = styles.fontFamily;
final fontSize = switch (styles.fontSizeEm) {
@@ -934,7 +952,75 @@ class _KatexSpan extends StatelessWidget {
textAlign: textAlign,
child: widget);
}
- return widget;
+
+ if (styles.verticalAlignEm != null) {
+ widget = Baseline(
+ baseline: styles.verticalAlignEm! * em,
+ baselineType: TextBaseline.alphabetic,
+ child: widget);
+ }
+
+ return Container(
+ margin: styles.marginRightEm != null && !styles.marginRightEm!.isNegative
+ ? EdgeInsets.only(right: styles.marginRightEm! * em)
+ : null,
+ height: styles.heightEm != null
+ ? styles.heightEm! * em
+ : null,
+ child: widget,
+ );
+ }
+}
+
+class _KatexVlist extends StatelessWidget {
+ const _KatexVlist(this.node);
+
+ final KatexVlistNode node;
+
+ @override
+ Widget build(BuildContext context) {
+ final em = DefaultTextStyle.of(context).style.fontSize!;
+
+ return Stack(children: List.unmodifiable(node.rows.map((row) {
+ return Transform.translate(
+ offset: Offset(0, row.verticalOffsetEm * em),
+ child: RichText(text: TextSpan(
+ children: List.unmodifiable(row.nodes.map((e) {
+ return WidgetSpan(
+ alignment: PlaceholderAlignment.baseline,
+ baseline: TextBaseline.alphabetic,
+ child: switch (e) {
+ KatexSpanNode() => _KatexSpan(e),
+ KatexVlistNode() => _KatexVlist(e),
+ KatexNegativeMarginNode() => _KatexNegativeMargin(e),
+ });
+ })))));
+ })));
+ }
+}
+
+class _KatexNegativeMargin extends StatelessWidget {
+ const _KatexNegativeMargin(this.node);
+
+ final KatexNegativeMarginNode node;
+
+ @override
+ Widget build(BuildContext context) {
+ final em = DefaultTextStyle.of(context).style.fontSize!;
+
+ return Transform.translate(
+ offset: Offset(node.marginRightEm * em, 0),
+ child: Text.rich(TextSpan(
+ children: List.unmodifiable(node.nodes.map((e) {
+ return WidgetSpan(
+ alignment: PlaceholderAlignment.baseline,
+ baseline: TextBaseline.alphabetic,
+ child: switch (e) {
+ KatexSpanNode() => _KatexSpan(e),
+ KatexVlistNode() => _KatexVlist(e),
+ KatexNegativeMarginNode() => _KatexNegativeMargin(e),
+ });
+ })))));
}
}
@@ -1256,7 +1342,7 @@ class _InlineContentBuilder {
: WidgetSpan(
alignment: PlaceholderAlignment.baseline,
baseline: TextBaseline.alphabetic,
- child: _Katex(inline: true, nodes: nodes));
+ child: _Katex(inline: true, textStyle: widget.style, nodes: nodes));
case GlobalTimeNode():
return WidgetSpan(alignment: PlaceholderAlignment.middle,
diff --git a/test/model/content_test.dart b/test/model/content_test.dart
index 5ab60c8e7e..315baa5e4c 100644
--- a/test/model/content_test.dart
+++ b/test/model/content_test.dart
@@ -518,9 +518,9 @@ class ContentExample {
' \\lambda '
'λ
',
MathInlineNode(texSource: r'\lambda', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.6944), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -529,7 +529,7 @@ class ContentExample {
]),
]));
- static final mathBlock = ContentExample(
+ static const mathBlock = ContentExample(
'math block',
"```math\n\\lambda\n```",
expectedText: r'\lambda',
@@ -538,9 +538,9 @@ class ContentExample {
'\\lambda'
'λ',
[MathBlockNode(texSource: r'\lambda', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.6944), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -549,7 +549,7 @@ class ContentExample {
]),
])]);
- static final mathBlocksMultipleInParagraph = ContentExample(
+ static const mathBlocksMultipleInParagraph = ContentExample(
'math blocks, multiple in paragraph',
'```math\na\n\nb\n```',
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/.E2.9C.94.20Rajesh/near/2001490
@@ -563,9 +563,9 @@ class ContentExample {
'b'
'b', [
MathBlockNode(texSource: 'a', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.4306), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -574,9 +574,9 @@ class ContentExample {
]),
]),
MathBlockNode(texSource: 'b', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.6944), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -586,7 +586,7 @@ class ContentExample {
]),
]);
- static final mathBlockInQuote = ContentExample(
+ static const mathBlockInQuote = ContentExample(
'math block in quote',
// There's sometimes a quirky extra `
\n` at the end of the `` that
// encloses the math block. In particular this happens when the math block
@@ -602,9 +602,9 @@ class ContentExample {
'
\n
\n',
[QuotationNode([
MathBlockNode(texSource: r'\lambda', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.6944), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -614,7 +614,7 @@ class ContentExample {
]),
])]);
- static final mathBlocksMultipleInQuote = ContentExample(
+ static const mathBlocksMultipleInQuote = ContentExample(
'math blocks, multiple in quote',
"````quote\n```math\na\n\nb\n```\n````",
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/.E2.9C.94.20Rajesh/near/2029236
@@ -631,9 +631,9 @@ class ContentExample {
'
\n\n',
[QuotationNode([
MathBlockNode(texSource: 'a', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.4306), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -642,9 +642,9 @@ class ContentExample {
]),
]),
MathBlockNode(texSource: 'b', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.6944), text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -654,7 +654,7 @@ class ContentExample {
]),
])]);
- static final mathBlockBetweenImages = ContentExample(
+ static const mathBlockBetweenImages = ContentExample(
'math block between images',
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/Greg/near/2035891
'https://upload.wikimedia.org/wikipedia/commons/7/78/Verregende_bloem_van_een_Helenium_%27El_Dorado%27._22-07-2023._%28d.j.b%29.jpg\n```math\na\n```\nhttps://upload.wikimedia.org/wikipedia/commons/thumb/7/71/Zaadpluizen_van_een_Clematis_texensis_%27Princess_Diana%27._18-07-2023_%28actm.%29_02.jpg/1280px-Zaadpluizen_van_een_Clematis_texensis_%27Princess_Diana%27._18-07-2023_%28actm.%29_02.jpg',
@@ -680,9 +680,9 @@ class ContentExample {
originalHeight: null),
]),
MathBlockNode(texSource: 'a', nodes: [
- KatexNode(styles: KatexSpanStyles(), text: null, nodes: [
- KatexNode(styles: KatexSpanStyles(),text: null, nodes: []),
- KatexNode(
+ KatexSpanNode(styles: KatexSpanStyles(), text: null, nodes: [
+ KatexSpanNode(styles: KatexSpanStyles(heightEm: 0.4306),text: null, nodes: []),
+ KatexSpanNode(
styles: KatexSpanStyles(
fontFamily: 'KaTeX_Math',
fontStyle: KatexSpanFontStyle.italic),
@@ -702,7 +702,7 @@ class ContentExample {
// The font sizes can be compared using the katex.css generated
// from katex.scss :
// https://unpkg.com/katex@0.16.21/dist/katex.css
- static final mathBlockKatexSizing = ContentExample(
+ static const mathBlockKatexSizing = ContentExample(
'math block; KaTeX different sizing',
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/Rajesh/near/2155476
'```math\n\\Huge 1\n\\huge 2\n\\LARGE 3\n\\Large 4\n\\large 5\n\\normalsize 6\n\\small 7\n\\footnotesize 8\n\\scriptsize 9\n\\tiny 0\n```',
@@ -727,51 +727,51 @@ class ContentExample {
MathBlockNode(
texSource: "\\Huge 1\n\\huge 2\n\\LARGE 3\n\\Large 4\n\\large 5\n\\normalsize 6\n\\small 7\n\\footnotesize 8\n\\scriptsize 9\n\\tiny 0",
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
- styles: KatexSpanStyles(),
+ KatexSpanNode(
+ styles: KatexSpanStyles(heightEm: 1.6034),
text: null,
nodes: []),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 2.488), // .reset-size6.size11
text: '1',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 2.074), // .reset-size6.size10
text: '2',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 1.728), // .reset-size6.size9
text: '3',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 1.44), // .reset-size6.size8
text: '4',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 1.2), // .reset-size6.size7
text: '5',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 1.0), // .reset-size6.size6
text: '6',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 0.9), // .reset-size6.size5
text: '7',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 0.8), // .reset-size6.size4
text: '8',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 0.7), // .reset-size6.size3
text: '9',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 0.5), // .reset-size6.size1
text: '0',
nodes: null),
@@ -779,7 +779,7 @@ class ContentExample {
]),
]);
- static final mathBlockKatexNestedSizing = ContentExample(
+ static const mathBlockKatexNestedSizing = ContentExample(
'math block; KaTeX nested sizing',
'```math\n\\tiny {1 \\Huge 2}\n```',
''
@@ -796,23 +796,23 @@ class ContentExample {
MathBlockNode(
texSource: '\\tiny {1 \\Huge 2}',
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
- styles: KatexSpanStyles(),
+ KatexSpanNode(
+ styles: KatexSpanStyles(heightEm: 1.6034),
text: null,
nodes: []),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 0.5), // reset-size6 size1
text: null,
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: '1',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontSizeEm: 4.976), // reset-size1 size11
text: '2',
nodes: null),
@@ -821,7 +821,7 @@ class ContentExample {
]),
]);
- static final mathBlockKatexDelimSizing = ContentExample(
+ static const mathBlockKatexDelimSizing = ContentExample(
'math block; KaTeX delimiter sizing',
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/Rajesh/near/2147135
'```math\n⟨ \\big( \\Big[ \\bigg⌈ \\Bigg⌊\n```',
@@ -841,50 +841,52 @@ class ContentExample {
MathBlockNode(
texSource: '⟨ \\big( \\Big[ \\bigg⌈ \\Bigg⌊',
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
- styles: KatexSpanStyles(),
+ KatexSpanNode(
+ styles: KatexSpanStyles(
+ heightEm: 3.0,
+ verticalAlignEm: -1.25),
text: null,
nodes: []),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: '⟨',
nodes: null),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontFamily: 'KaTeX_Size1'),
text: '(',
nodes: null),
]),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontFamily: 'KaTeX_Size2'),
text: '[',
nodes: null),
]),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontFamily: 'KaTeX_Size3'),
text: '⌈',
nodes: null),
]),
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(),
text: null,
nodes: [
- KatexNode(
+ KatexSpanNode(
styles: KatexSpanStyles(fontFamily: 'KaTeX_Size4'),
text: '⌊',
nodes: null),
diff --git a/test/widgets/content_test.dart b/test/widgets/content_test.dart
index a788225aac..d5445fb931 100644
--- a/test/widgets/content_test.dart
+++ b/test/widgets/content_test.dart
@@ -595,15 +595,20 @@ void main() {
final content = ContentExample.mathBlockKatexSizing;
await prepareContent(tester, plainContent(content.html));
+ final context = tester.element(find.byType(MathBlock));
+ final baseTextStyle =
+ mkBaseKatexTextStyle(ContentTheme.of(context).textStylePlainParagraph);
+
final mathBlockNode = content.expectedNodes.single as MathBlockNode;
- final baseNode = mathBlockNode.nodes!.single;
+ final baseNode = mathBlockNode.nodes!.single as KatexSpanNode;
final nodes = baseNode.nodes!.skip(1); // Skip .strut node.
- for (final katexNode in nodes) {
- final fontSize = katexNode.styles.fontSizeEm! * kBaseKatexTextStyle.fontSize!;
+ for (var katexNode in nodes) {
+ katexNode = katexNode as KatexSpanNode;
+ final fontSize = katexNode.styles.fontSizeEm! * baseTextStyle.fontSize!;
checkKatexText(tester, katexNode.text!,
fontFamily: 'KaTeX_Main',
fontSize: fontSize,
- fontHeight: kBaseKatexTextStyle.height!);
+ fontHeight: baseTextStyle.height!);
}
});
@@ -616,17 +621,21 @@ void main() {
final content = ContentExample.mathBlockKatexNestedSizing;
await prepareContent(tester, plainContent(content.html));
- var fontSize = 0.5 * kBaseKatexTextStyle.fontSize!;
+ final context = tester.element(find.byType(MathBlock));
+ final baseTextStyle =
+ mkBaseKatexTextStyle(ContentTheme.of(context).textStylePlainParagraph);
+
+ var fontSize = 0.5 * baseTextStyle.fontSize!;
checkKatexText(tester, '1',
fontFamily: 'KaTeX_Main',
fontSize: fontSize,
- fontHeight: kBaseKatexTextStyle.height!);
+ fontHeight: baseTextStyle.height!);
fontSize = 4.976 * fontSize;
checkKatexText(tester, '2',
fontFamily: 'KaTeX_Main',
fontSize: fontSize,
- fontHeight: kBaseKatexTextStyle.height!);
+ fontHeight: baseTextStyle.height!);
});
testWidgets('displays KaTeX content with different delimiter sizing', (tester) async {
@@ -639,25 +648,28 @@ void main() {
await prepareContent(tester, plainContent(content.html));
final mathBlockNode = content.expectedNodes.single as MathBlockNode;
- final baseNode = mathBlockNode.nodes!.single;
+ final baseNode = mathBlockNode.nodes!.single as KatexSpanNode;
var nodes = baseNode.nodes!.skip(1); // Skip .strut node.
- final fontSize = kBaseKatexTextStyle.fontSize!;
+ final context = tester.element(find.byType(MathBlock));
+ final baseTextStyle =
+ mkBaseKatexTextStyle(ContentTheme.of(context).textStylePlainParagraph);
- final firstNode = nodes.first;
+ final firstNode = nodes.first as KatexSpanNode;
checkKatexText(tester, firstNode.text!,
fontFamily: 'KaTeX_Main',
- fontSize: fontSize,
- fontHeight: kBaseKatexTextStyle.height!);
+ fontSize: baseTextStyle.fontSize!,
+ fontHeight: baseTextStyle.height!);
nodes = nodes.skip(1);
for (var katexNode in nodes) {
- katexNode = katexNode.nodes!.single; // Skip empty .mord parent.
+ katexNode = katexNode as KatexSpanNode;
+ katexNode = katexNode.nodes!.single as KatexSpanNode; // Skip empty .mord parent.
final fontFamily = katexNode.styles.fontFamily!;
checkKatexText(tester, katexNode.text!,
fontFamily: fontFamily,
- fontSize: fontSize,
- fontHeight: kBaseKatexTextStyle.height!);
+ fontSize: baseTextStyle.fontSize!,
+ fontHeight: baseTextStyle.height!);
}
});
});