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
34 changes: 33 additions & 1 deletion lib/model/katex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ class _KatexParser {
KatexSpanFontWeight? fontWeight;
KatexSpanFontStyle? fontStyle;
KatexSpanTextAlign? textAlign;
KatexSpanBorderBottomStyle? borderBottomStyle;
var index = 0;
while (index < spanClasses.length) {
final spanClass = spanClasses[index++];
Expand Down Expand Up @@ -626,6 +627,8 @@ class _KatexParser {
case 'nobreak':
case 'allowbreak':
case 'mathdefault':
case 'overline':
case 'underline':
// Ignore these classes because they don't have a CSS definition
// in katex.scss, but we encounter them in the generated HTML.
// (Why are they there if they're not used? The story seems to be:
Expand All @@ -636,6 +639,15 @@ class _KatexParser {
// )
break;

case 'overline-line':
case 'underline-line':
// .overline-line,
// .underline-line { width: 100%; border-bottom-style: solid; }
// Border applied via inline style: border-bottom-width: 0.04em;
widthEm = double.infinity;
borderBottomStyle = KatexSpanBorderBottomStyle.solid;
break;

default:
assert(debugLog('KaTeX: Unsupported CSS class: $spanClass'));
unsupportedCssClasses.add(spanClass);
Expand All @@ -657,6 +669,8 @@ class _KatexParser {
marginRightEm: _takeStyleEm(inlineStyles, 'margin-right'),
color: _takeStyleColor(inlineStyles, 'color'),
position: _takeStylePosition(inlineStyles, 'position'),
borderBottomStyle: borderBottomStyle,
borderBottomWidthEm: _takeStyleEm(inlineStyles, 'border-bottom-width')
// TODO handle more CSS properties
);
if (inlineStyles != null && inlineStyles.isNotEmpty) {
Expand Down Expand Up @@ -840,6 +854,10 @@ enum KatexSpanPosition {
relative,
}

enum KatexSpanBorderBottomStyle {
solid,
}

class KatexSpanColor {
const KatexSpanColor(this.r, this.g, this.b, this.a);

Expand Down Expand Up @@ -893,6 +911,8 @@ class KatexSpanStyles {

final KatexSpanColor? color;
final KatexSpanPosition? position;
final KatexSpanBorderBottomStyle? borderBottomStyle;
final double? borderBottomWidthEm;

const KatexSpanStyles({
this.widthEm,
Expand All @@ -907,6 +927,8 @@ class KatexSpanStyles {
this.textAlign,
this.color,
this.position,
this.borderBottomStyle,
this.borderBottomWidthEm,
});

@override
Expand All @@ -924,6 +946,8 @@ class KatexSpanStyles {
textAlign,
color,
position,
borderBottomStyle,
borderBottomWidthEm
);

@override
Expand All @@ -940,7 +964,9 @@ class KatexSpanStyles {
other.fontStyle == fontStyle &&
other.textAlign == textAlign &&
other.color == color &&
other.position == position;
other.position == position &&
other.borderBottomStyle == borderBottomStyle &&
other.borderBottomWidthEm == borderBottomWidthEm;
}

@override
Expand All @@ -958,6 +984,8 @@ class KatexSpanStyles {
if (textAlign != null) args.add('textAlign: $textAlign');
if (color != null) args.add('color: $color');
if (position != null) args.add('position: $position');
if (borderBottomStyle != null) args.add('borderBottomStyle: $borderBottomStyle');
if (borderBottomWidthEm != null) args.add('borderBottomWidthEm: $borderBottomWidthEm');
return '${objectRuntimeType(this, 'KatexSpanStyles')}(${args.join(', ')})';
}

Expand All @@ -975,6 +1003,8 @@ class KatexSpanStyles {
bool textAlign = true,
bool color = true,
bool position = true,
bool borderBottomStyle = true,
bool borderBottomWidthEm = true,
}) {
return KatexSpanStyles(
widthEm: widthEm ? this.widthEm : null,
Expand All @@ -989,6 +1019,8 @@ class KatexSpanStyles {
textAlign: textAlign ? this.textAlign : null,
color: color ? this.color : null,
position: position ? this.position : null,
borderBottomStyle: borderBottomStyle ? this.borderBottomStyle : null,
borderBottomWidthEm: borderBottomWidthEm ? this.borderBottomWidthEm : null,
);
}
}
Expand Down
25 changes: 20 additions & 5 deletions lib/widgets/katex.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,19 @@ class _KatexSpan extends StatelessWidget {
null => null,
};

if (styles.borderBottomStyle == KatexSpanBorderBottomStyle.solid &&
styles.borderBottomWidthEm != null) {
final borderColor = color ?? DefaultTextStyle.of(context).style.color!;
final borderWidth = styles.borderBottomWidthEm! * em;

widget = DecoratedBox(
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(color: borderColor, width: borderWidth, style: BorderStyle.solid))),
child: widget,
);
}

TextStyle? textStyle;
if (fontFamily != null ||
fontSize != null ||
Expand Down Expand Up @@ -232,11 +245,13 @@ class _KatexVlist extends StatelessWidget {
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: _KatexSpan(row.node));
})));
return IntrinsicWidth(
child: Stack(children: List.unmodifiable(node.rows.map((row) {
return Transform.translate(
offset: Offset(0, row.verticalOffsetEm * em),
child: _KatexSpan(row.node));
}))),
);
}
}

Expand Down
105 changes: 105 additions & 0 deletions test/model/katex_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -731,6 +731,109 @@ class KatexExample extends ContentExample {
]),
]),
]);

static final overline = KatexExample.block(
r'overline: \overline{AB}',
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/Saif.20KaTeX/near/2285099
r'\overline{AB}',
'<p>'
'<span class="katex-display"><span class="katex">'
'<span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><mover accent="true"><mrow><mi>A</mi><mi>B</mi></mrow><mo stretchy="true">‾</mo></mover></mrow><annotation encoding="application/x-tex">\\overline{AB}</annotation></semantics></math></span>'
'<span class="katex-html" aria-hidden="true">'
'<span class="base">'
'<span class="strut" style="height:0.8833em;"></span>'
'<span class="mord overline">'
'<span class="vlist-t">'
'<span class="vlist-r">'
'<span class="vlist" style="height:0.8833em;">'
'<span style="top:-3em;">'
'<span class="pstrut" style="height:3em;"></span>'
'<span class="mord">'
'<span class="mord mathnormal">A</span>'
'<span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span>'
'<span style="top:-3.8033em;">'
'<span class="pstrut" style="height:3em;"></span>'
'<span class="overline-line" style="border-bottom-width:0.04em;"></span></span></span></span></span></span></span></span></span></p>',[
KatexSpanNode(nodes: [
KatexStrutNode(heightEm: 0.8833, verticalAlignEm: null),
KatexSpanNode(nodes: [
KatexVlistNode(rows: [
KatexVlistRowNode(
verticalOffsetEm: -3 + 3,
node: KatexSpanNode(nodes: [
KatexSpanNode(nodes: [
KatexSpanNode(
styles: KatexSpanStyles(fontFamily: 'KaTeX_Math', fontStyle: KatexSpanFontStyle.italic),
text: 'A'),
KatexSpanNode(
styles: KatexSpanStyles(marginRightEm: 0.05017, fontFamily: 'KaTeX_Math', fontStyle: KatexSpanFontStyle.italic),
text: 'B'),
]),
])),
KatexVlistRowNode(
verticalOffsetEm: -3.8033 + 3,
node: KatexSpanNode(nodes: [
KatexSpanNode(
styles: KatexSpanStyles(widthEm: double.infinity, borderBottomStyle: KatexSpanBorderBottomStyle.solid, borderBottomWidthEm: 0.04),
nodes: []),
])),
]),
]),
]),
]);

static final underline = KatexExample.block(
r'underline: \underline{AB}',
// https://chat.zulip.org/#narrow/channel/7-test-here/topic/Saif.20KaTeX/near/2285099
r'\underline{AB}',
'<p>'
'<span class="katex-display"><span class="katex">'
'<span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML" display="block"><semantics><mrow><munder accentunder="true"><mrow><mi>A</mi><mi>B</mi></mrow><mo stretchy="true">‾</mo></munder></mrow><annotation encoding="application/x-tex">\\underline{AB}</annotation></semantics></math></span>'
'<span class="katex-html" aria-hidden="true">'
'<span class="base">'
'<span class="strut" style="height:0.8833em;vertical-align:-0.2em;"></span>'
'<span class="mord underline">'
'<span class="vlist-t vlist-t2">'
'<span class="vlist-r">'
'<span class="vlist" style="height:0.6833em;">'
'<span style="top:-2.84em;">'
'<span class="pstrut" style="height:3em;"></span>'
'<span class="underline-line" style="border-bottom-width:0.04em;"></span></span>'
'<span style="top:-3em;">'
'<span class="pstrut" style="height:3em;"></span>'
'<span class="mord">'
'<span class="mord mathnormal">A</span>'
'<span class="mord mathnormal" style="margin-right:0.05017em;">B</span></span></span></span>'
'<span class="vlist-s">​</span></span>'
'<span class="vlist-r">'
'<span class="vlist" style="height:0.2em;"><span></span></span></span></span></span></span></span></span></span></p>',[
KatexSpanNode(nodes: [
KatexStrutNode(heightEm: 0.8833, verticalAlignEm: -0.2),
KatexSpanNode(nodes: [
KatexVlistNode(rows: [
KatexVlistRowNode(
verticalOffsetEm: -2.84 + 3,
node: KatexSpanNode(nodes: [
KatexSpanNode(
styles: KatexSpanStyles(widthEm: double.infinity, borderBottomStyle: KatexSpanBorderBottomStyle.solid, borderBottomWidthEm: 0.04),
nodes: []),
])),
KatexVlistRowNode(
verticalOffsetEm: -3 + 3,
node: KatexSpanNode(nodes: [
KatexSpanNode(nodes: [
KatexSpanNode(
styles: KatexSpanStyles(fontFamily: 'KaTeX_Math', fontStyle: KatexSpanFontStyle.italic),
text: 'A'),
KatexSpanNode(
styles: KatexSpanStyles(marginRightEm: 0.05017, fontFamily: 'KaTeX_Math', fontStyle: KatexSpanFontStyle.italic),
text: 'B'),
]),
])),
]),
]),
]),
]);
}

void main() async {
Expand All @@ -754,6 +857,8 @@ void main() async {
testParseExample(KatexExample.bigOperators);
testParseExample(KatexExample.colonEquals);
testParseExample(KatexExample.nulldelimiter);
testParseExample(KatexExample.overline);
testParseExample(KatexExample.underline);

group('parseCssHexColor', () {
const testCases = [
Expand Down
8 changes: 8 additions & 0 deletions test/widgets/katex_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ void main() {
('a', Offset(2.47, 3.36), Size(10.88, 25.00)),
('b', Offset(15.81, 3.36), Size(8.82, 25.00)),
]),
(KatexExample.overline, skip: false, [
('A', Offset(0.0, 5.61), Size(15.43, 25.0)),
('B', Offset(15.43, 5.61), Size(15.61, 25.0)),
]),
(KatexExample.underline, skip: false, [
('A', Offset(0.0, 5.61), Size(15.43, 25.0)),
('B', Offset(15.43, 5.61), Size(15.61, 25.0)),
]),
];

for (final testCase in testCases) {
Expand Down
Loading