Skip to content

Commit a3e8f0e

Browse files
committed
Merge remote-tracking branch 'pr/1559'
2 parents 1f3e881 + e8cd58a commit a3e8f0e

File tree

6 files changed

+441
-23
lines changed

6 files changed

+441
-23
lines changed

lib/model/content.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,28 @@ class KatexVlistRowNode extends ContentNode {
460460
}
461461
}
462462

463+
class KatexNegativeMarginNode extends KatexNode {
464+
const KatexNegativeMarginNode({
465+
required this.leftOffsetEm,
466+
required this.nodes,
467+
super.debugHtmlNode,
468+
}) : assert(leftOffsetEm < 0);
469+
470+
final double leftOffsetEm;
471+
final List<KatexNode> nodes;
472+
473+
@override
474+
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
475+
super.debugFillProperties(properties);
476+
properties.add(DoubleProperty('leftOffsetEm', leftOffsetEm));
477+
}
478+
479+
@override
480+
List<DiagnosticsNode> debugDescribeChildren() {
481+
return nodes.map((node) => node.toDiagnosticsNode()).toList();
482+
}
483+
}
484+
463485
class MathBlockNode extends MathNode implements BlockContentNode {
464486
const MathBlockNode({
465487
super.debugHtmlNode,

lib/model/katex.dart

Lines changed: 56 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'package:collection/collection.dart';
12
import 'package:csslib/parser.dart' as css_parser;
23
import 'package:csslib/visitor.dart' as css_visitor;
34
import 'package:flutter/foundation.dart';
@@ -167,16 +168,42 @@ class _KatexParser {
167168
}
168169

169170
List<KatexNode> _parseChildSpans(List<dom.Node> nodes) {
170-
return List.unmodifiable(nodes.map((node) {
171-
if (node case dom.Element(localName: 'span')) {
172-
return _parseSpan(node);
173-
} else {
171+
var resultSpans = QueueList<KatexNode>();
172+
for (final node in nodes.reversed) {
173+
if (node is! dom.Element || node.localName != 'span') {
174174
throw _KatexHtmlParseError(
175175
node is dom.Element
176176
? 'unsupported html node: ${node.localName}'
177177
: 'unsupported html node');
178178
}
179-
}));
179+
180+
final span = _parseSpan(node);
181+
182+
if (span is KatexSpanNode) {
183+
final marginRightEm = span.styles.marginRightEm;
184+
if (marginRightEm != null && marginRightEm.isNegative) {
185+
final previousSpans = resultSpans;
186+
resultSpans = QueueList<KatexNode>();
187+
resultSpans.addFirst(KatexNegativeMarginNode(
188+
leftOffsetEm: marginRightEm,
189+
nodes: previousSpans));
190+
}
191+
}
192+
193+
resultSpans.addFirst(span);
194+
195+
if (span is KatexSpanNode) {
196+
final marginLeftEm = span.styles.marginLeftEm;
197+
if (marginLeftEm != null && marginLeftEm.isNegative) {
198+
final previousSpans = resultSpans;
199+
resultSpans = QueueList<KatexNode>();
200+
resultSpans.addFirst(KatexNegativeMarginNode(
201+
leftOffsetEm: marginLeftEm,
202+
nodes: previousSpans));
203+
}
204+
}
205+
}
206+
return resultSpans;
180207
}
181208

182209
static final _resetSizeClassRegExp = RegExp(r'^reset-size(\d\d?)$');
@@ -266,13 +293,31 @@ class _KatexParser {
266293
final pstrutStyles = _parseSpanInlineStyles(pstrutSpan)!;
267294
final pstrutHeight = pstrutStyles.heightEm ?? 0;
268295

296+
KatexSpanNode innerSpanNode = KatexSpanNode(
297+
styles: styles,
298+
text: null,
299+
nodes: _parseChildSpans(otherSpans));
300+
301+
final marginRightEm = styles.marginRightEm;
302+
final marginLeftEm = styles.marginLeftEm;
303+
if (marginRightEm != null && marginRightEm.isNegative) {
304+
throw _KatexHtmlParseError();
305+
}
306+
if (marginLeftEm != null && marginLeftEm.isNegative) {
307+
innerSpanNode = KatexSpanNode(
308+
styles: KatexSpanStyles(),
309+
text: null,
310+
nodes: [
311+
KatexNegativeMarginNode(
312+
leftOffsetEm: marginLeftEm,
313+
nodes: [innerSpanNode]),
314+
]);
315+
}
316+
269317
rows.add(KatexVlistRowNode(
270318
verticalOffsetEm: topEm + pstrutHeight,
271319
debugHtmlNode: kDebugMode ? innerSpan : null,
272-
node: KatexSpanNode(
273-
styles: styles,
274-
text: null,
275-
nodes: _parseChildSpans(otherSpans))));
320+
node: innerSpanNode));
276321
} else {
277322
throw _KatexHtmlParseError();
278323
}
@@ -605,17 +650,11 @@ class _KatexParser {
605650

606651
case 'margin-right':
607652
marginRightEm = _getEm(expression);
608-
if (marginRightEm != null) {
609-
if (marginRightEm < 0) throw _KatexHtmlParseError();
610-
continue;
611-
}
653+
if (marginRightEm != null) continue;
612654

613655
case 'margin-left':
614656
marginLeftEm = _getEm(expression);
615-
if (marginLeftEm != null) {
616-
if (marginLeftEm < 0) throw _KatexHtmlParseError();
617-
continue;
618-
}
657+
if (marginLeftEm != null) continue;
619658
}
620659

621660
// TODO handle more CSS properties

lib/widgets/content.dart

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import 'code_block.dart';
2222
import 'dialog.dart';
2323
import 'icons.dart';
2424
import 'inset_shadow.dart';
25+
import 'katex.dart';
2526
import 'lightbox.dart';
2627
import 'message_list.dart';
2728
import 'poll.dart';
@@ -884,6 +885,7 @@ class _KatexNodeList extends StatelessWidget {
884885
KatexSpanNode() => _KatexSpan(e),
885886
KatexStrutNode() => _KatexStrut(e),
886887
KatexVlistNode() => _KatexVlist(e),
888+
KatexNegativeMarginNode() => _KatexNegativeMargin(e),
887889
}));
888890
}))));
889891
}
@@ -957,23 +959,21 @@ class _KatexSpan extends StatelessWidget {
957959
}
958960

959961
final marginRight = switch (styles.marginRightEm) {
960-
double marginRightEm => marginRightEm * em,
961-
null => null,
962+
double marginRightEm when marginRightEm >= 0 => marginRightEm * em,
963+
_ => null,
962964
};
963965
final marginLeft = switch (styles.marginLeftEm) {
964-
double marginLeftEm => marginLeftEm * em,
965-
null => null,
966+
double marginLeftEm when marginLeftEm >= 0 => marginLeftEm * em,
967+
_ => null,
966968
};
967969

968970
EdgeInsets? margin;
969971
if (marginRight != null || marginLeft != null) {
970972
margin = EdgeInsets.zero;
971973
if (marginRight != null) {
972-
assert(marginRight >= 0);
973974
margin += EdgeInsets.only(right: marginRight);
974975
}
975976
if (marginLeft != null) {
976-
assert(marginLeft >= 0);
977977
margin += EdgeInsets.only(left: marginLeft);
978978
}
979979
}
@@ -1031,6 +1031,21 @@ class _KatexVlist extends StatelessWidget {
10311031
}
10321032
}
10331033

1034+
class _KatexNegativeMargin extends StatelessWidget {
1035+
const _KatexNegativeMargin(this.node);
1036+
1037+
final KatexNegativeMarginNode node;
1038+
1039+
@override
1040+
Widget build(BuildContext context) {
1041+
final em = DefaultTextStyle.of(context).style.fontSize!;
1042+
1043+
return NegativeLeftOffset(
1044+
leftOffset: node.leftOffsetEm * em,
1045+
child: _KatexNodeList(nodes: node.nodes));
1046+
}
1047+
}
1048+
10341049
class WebsitePreview extends StatelessWidget {
10351050
const WebsitePreview({super.key, required this.node});
10361051

0 commit comments

Comments
 (0)