Skip to content

Commit 9d08564

Browse files
katex: Render overline and underline and add widget tests
_findLineInfo() walks the vlist subtree (it's small) and collects spans with borderBottomWidthEm, recording each span’s line thickness, height, and vertical position. CustomPaint then draws these lines over the stacked spans. The +0.9 offset is a visual alignment tweak.
1 parent 91e12f8 commit 9d08564

File tree

2 files changed

+77
-6
lines changed

2 files changed

+77
-6
lines changed

lib/widgets/katex.dart

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'dart:math' as math;
22

33
import 'package:flutter/foundation.dart';
4-
import 'package:flutter/widgets.dart';
4+
import 'package:flutter/material.dart';
55
import 'package:flutter/rendering.dart';
66

77
import '../model/content.dart';
@@ -231,12 +231,75 @@ class _KatexVlist extends StatelessWidget {
231231
@override
232232
Widget build(BuildContext context) {
233233
final em = DefaultTextStyle.of(context).style.fontSize!;
234+
final defaultColor = DefaultTextStyle.of(context).style.color ;
235+
236+
final lines = <_VlistLine>[];
237+
for (final row in node.rows) {
238+
final lineInfo = _findLineInfo(row.node);
239+
if (lineInfo != null) {
240+
final y = (row.verticalOffsetEm - (lineInfo.heightEm ?? 0) + 0.9) * em;
241+
final thickness = lineInfo.borderWidthEm * em;
242+
lines.add(_VlistLine(y, thickness, lineInfo.color ?? defaultColor));
243+
}}
244+
245+
return CustomPaint(
246+
foregroundPainter: _KatexBorderPainter(lines),
247+
child: Stack(
248+
children: List.unmodifiable(node.rows.map((row) {
249+
return Transform.translate(
250+
offset: Offset(0, row.verticalOffsetEm * em),
251+
child: _KatexSpan(row.node));
252+
}))));
253+
}
254+
({double? heightEm, double borderWidthEm, Color? color})? _findLineInfo(KatexSpanNode span) {
255+
final borderWidth = span.styles.borderBottomWidthEm;
256+
final color = span.styles.color != null
257+
? Color.fromARGB(span.styles.color!.a, span.styles.color!.r, span.styles.color!.g, span.styles.color!.b)
258+
: null;
259+
if (borderWidth != null) {return (heightEm: span.styles.heightEm, borderWidthEm: borderWidth, color: color);}
260+
261+
if (span.nodes != null) {
262+
for (final child in span.nodes!) {
263+
if (child is KatexSpanNode) {
264+
final info = _findLineInfo(child);
265+
if (info != null) return info;
266+
}}}
267+
return null;
268+
}
269+
}
270+
271+
class _VlistLine {
272+
const _VlistLine(this.y, this.thickness, this.color);
273+
final double y;
274+
final double thickness;
275+
final Color? color;
276+
}
277+
278+
class _KatexBorderPainter extends CustomPainter {
279+
const _KatexBorderPainter(this.lines);
234280

235-
return Stack(children: List.unmodifiable(node.rows.map((row) {
236-
return Transform.translate(
237-
offset: Offset(0, row.verticalOffsetEm * em),
238-
child: _KatexSpan(row.node));
239-
})));
281+
final List<_VlistLine> lines;
282+
283+
@override
284+
void paint(Canvas canvas, Size size) {
285+
if (lines.isEmpty) return;
286+
final paint = Paint()..style = PaintingStyle.fill;
287+
for (final line in lines) {
288+
paint.color = line.color ?? Colors.black;
289+
canvas.drawRect(Rect.fromLTWH(0, line.y, size.width, line.thickness), paint);
290+
}
291+
}
292+
293+
@override
294+
bool shouldRepaint(covariant _KatexBorderPainter oldDelegate) {
295+
if (oldDelegate.lines.length != lines.length) return true;
296+
for (var i = 0; i < lines.length; i++) {
297+
if (oldDelegate.lines[i].y != lines[i].y ||
298+
oldDelegate.lines[i].thickness != lines[i].thickness ||
299+
oldDelegate.lines[i].color != lines[i].color) {
300+
return true;
301+
}}
302+
return false;
240303
}
241304
}
242305

test/widgets/katex_test.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,14 @@ void main() {
8181
('a', Offset(2.47, 3.36), Size(10.88, 25.00)),
8282
('b', Offset(15.81, 3.36), Size(8.82, 25.00)),
8383
]),
84+
(KatexExample.overline, skip: false, [
85+
('A', Offset(0.0, 5.61), Size(15.43, 25.0)),
86+
('B', Offset(15.43, 5.61), Size(15.61, 25.0)),
87+
]),
88+
(KatexExample.underline, skip: false, [
89+
('A', Offset(0.0, 5.61), Size(15.43, 25.0)),
90+
('B', Offset(15.43, 5.61), Size(15.61, 25.0)),
91+
]),
8492
];
8593

8694
for (final testCase in testCases) {

0 commit comments

Comments
 (0)