Skip to content

Commit 8f77f13

Browse files
content: Support parsing and handling inline styles for KaTeX content
1 parent 4b27c91 commit 8f77f13

File tree

2 files changed

+120
-2
lines changed

2 files changed

+120
-2
lines changed

lib/model/katex.dart

+99-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'package:csslib/parser.dart' as css_parser;
2+
import 'package:csslib/visitor.dart' as css_visitor;
13
import 'package:flutter/foundation.dart';
24
import 'package:html/dom.dart' as dom;
35

@@ -292,11 +294,72 @@ class KatexParser {
292294
}
293295
if (text == null && spans == null) throw KatexHtmlParseError();
294296

297+
final inlineStyles = _parseSpanInlineStyles(element);
298+
295299
return KatexNode(
296300
text: text,
297-
styles: styles,
301+
styles: inlineStyles != null
302+
? styles.merge(inlineStyles)
303+
: styles,
298304
nodes: spans);
299305
}
306+
307+
KatexSpanStyles? _parseSpanInlineStyles(dom.Element element) {
308+
if (element.attributes case {'style': final styleStr}) {
309+
final stylesheet = css_parser.parse('*{$styleStr}');
310+
final topLevels = stylesheet.topLevels;
311+
if (topLevels.length != 1) throw KatexHtmlParseError();
312+
final topLevel = topLevels.single;
313+
if (topLevel is! css_visitor.RuleSet) throw KatexHtmlParseError();
314+
final rule = topLevel;
315+
316+
double? marginLeftEm;
317+
double? marginRightEm;
318+
double? paddingLeftEm;
319+
320+
for (final declaration in rule.declarationGroup.declarations) {
321+
if (declaration is! css_visitor.Declaration) throw KatexHtmlParseError();
322+
final property = declaration.property;
323+
324+
final expressions = declaration.expression;
325+
if (expressions is! css_visitor.Expressions) throw KatexHtmlParseError();
326+
if (expressions.expressions.length != 1) throw KatexHtmlParseError();
327+
final expression = expressions.expressions.single;
328+
329+
switch (property) {
330+
case 'margin-left':
331+
marginLeftEm = _getEm(expression);
332+
if (marginLeftEm != null) continue;
333+
334+
case 'margin-right':
335+
marginRightEm = _getEm(expression);
336+
if (marginRightEm != null) continue;
337+
338+
case 'padding-left':
339+
paddingLeftEm = _getEm(expression);
340+
if (paddingLeftEm != null) continue;
341+
342+
default:
343+
// TODO handle more CSS properties
344+
assert(debugLog('Unsupported CSS property: $property of type ${expression.runtimeType}'));
345+
}
346+
}
347+
348+
return KatexSpanStyles(
349+
marginLeftEm: marginLeftEm,
350+
marginRightEm: marginRightEm,
351+
paddingLeftEm: paddingLeftEm,
352+
);
353+
}
354+
return null;
355+
}
356+
357+
double? _getEm(css_visitor.Expression expression) {
358+
if (expression is css_visitor.EmTerm && expression.value is num) {
359+
return (expression.value as num).toDouble();
360+
}
361+
return null;
362+
}
300363
}
301364

302365
enum KatexSpanFontWeight {
@@ -315,13 +378,22 @@ enum KatexSpanTextAlign {
315378
}
316379

317380
class KatexSpanStyles {
381+
double? heightEm;
382+
double? marginLeftEm;
383+
double? marginRightEm;
384+
double? paddingLeftEm;
385+
318386
String? fontFamily;
319387
double? fontSizeEm;
320388
KatexSpanFontStyle? fontStyle;
321389
KatexSpanFontWeight? fontWeight;
322390
KatexSpanTextAlign? textAlign;
323391

324392
KatexSpanStyles({
393+
this.heightEm,
394+
this.marginLeftEm,
395+
this.marginRightEm,
396+
this.paddingLeftEm,
325397
this.fontFamily,
326398
this.fontSizeEm,
327399
this.fontStyle,
@@ -332,6 +404,10 @@ class KatexSpanStyles {
332404
@override
333405
int get hashCode => Object.hash(
334406
'KatexSpanStyles',
407+
heightEm,
408+
marginLeftEm,
409+
marginRightEm,
410+
paddingLeftEm,
335411
fontFamily,
336412
fontSizeEm,
337413
fontStyle,
@@ -342,6 +418,10 @@ class KatexSpanStyles {
342418
@override
343419
bool operator ==(Object other) {
344420
return other is KatexSpanStyles &&
421+
other.heightEm == heightEm &&
422+
other.marginLeftEm == marginLeftEm &&
423+
other.marginRightEm == marginRightEm &&
424+
other.paddingLeftEm == paddingLeftEm &&
345425
other.fontFamily == fontFamily &&
346426
other.fontSizeEm == fontSizeEm &&
347427
other.fontStyle == fontStyle &&
@@ -356,13 +436,31 @@ class KatexSpanStyles {
356436
if (this == _zero) return '${objectRuntimeType(this, 'KatexSpanStyles')}()';
357437

358438
final args = <String>[];
439+
if (heightEm != null) args.add('heightEm: $heightEm');
440+
if (marginLeftEm != null) args.add('marginLeftEm: $marginLeftEm');
441+
if (marginRightEm != null) args.add('marginRightEm: $marginRightEm');
442+
if (paddingLeftEm != null) args.add('paddingLeftEm: $paddingLeftEm');
359443
if (fontFamily != null) args.add('fontFamily: $fontFamily');
360444
if (fontSizeEm != null) args.add('fontSizeEm: $fontSizeEm');
361445
if (fontStyle != null) args.add('fontStyle: $fontStyle');
362446
if (fontWeight != null) args.add('fontWeight: $fontWeight');
363447
if (textAlign != null) args.add('textAlign: $textAlign');
364448
return '${objectRuntimeType(this, 'KatexSpanStyles')}(${args.join(', ')})';
365449
}
450+
451+
KatexSpanStyles merge(KatexSpanStyles other) {
452+
return KatexSpanStyles(
453+
heightEm: other.heightEm ?? heightEm,
454+
marginLeftEm: other.marginLeftEm ?? marginLeftEm,
455+
marginRightEm: other.marginRightEm ?? marginRightEm,
456+
paddingLeftEm: other.paddingLeftEm ?? paddingLeftEm,
457+
fontFamily: other.fontFamily ?? fontFamily,
458+
fontSizeEm: other.fontSizeEm ?? fontSizeEm,
459+
fontStyle: other.fontStyle ?? fontStyle,
460+
fontWeight: other.fontWeight ?? fontWeight,
461+
textAlign: other.textAlign ?? textAlign,
462+
);
463+
}
366464
}
367465

368466
class KatexSpanStylesProperty extends DiagnosticsProperty<KatexSpanStyles> {

lib/widgets/content.dart

+21-1
Original file line numberDiff line numberDiff line change
@@ -925,13 +925,33 @@ class _KatexSpan extends StatelessWidget {
925925
};
926926
}
927927

928+
var margin = EdgeInsets.zero;
929+
final marginRightEm = styles.marginRightEm;
930+
final marginLeftEm = styles.marginLeftEm;
931+
if (marginRightEm != null && !marginRightEm.isNegative) {
932+
margin += EdgeInsets.only(right: marginRightEm * em);
933+
}
934+
if (marginLeftEm != null && !marginLeftEm.isNegative) {
935+
margin += EdgeInsets.only(left: marginLeftEm * em);
936+
}
937+
938+
var padding = EdgeInsets.zero;
939+
final paddingLeftEm = styles.paddingLeftEm;
940+
if (paddingLeftEm != null && !paddingLeftEm.isNegative) {
941+
padding += EdgeInsets.only(right: paddingLeftEm * em);
942+
}
943+
928944
if (textStyle != null || textAlign != null) {
929945
widget = DefaultTextStyle.merge(
930946
style: textStyle,
931947
textAlign: textAlign,
932948
child: widget);
933949
}
934-
return widget;
950+
return Container(
951+
margin: margin != EdgeInsets.zero ? margin : null,
952+
padding: padding != EdgeInsets.zero ? padding : null,
953+
child: widget,
954+
);
935955
}
936956
}
937957

0 commit comments

Comments
 (0)