@@ -411,6 +411,7 @@ class _KatexParser {
411411 KatexSpanFontWeight ? fontWeight;
412412 KatexSpanFontStyle ? fontStyle;
413413 KatexSpanTextAlign ? textAlign;
414+ KatexBorderStyle ? borderStyle;
414415 var index = 0 ;
415416 while (index < spanClasses.length) {
416417 final spanClass = spanClasses[index++ ];
@@ -626,6 +627,32 @@ class _KatexParser {
626627 case 'nobreak' :
627628 case 'allowbreak' :
628629 case 'mathdefault' :
630+ case 'tag' :
631+ case 'eqn-num' :
632+ case 'mtable' :
633+ case 'col-align-l' :
634+ case 'col-align-c' :
635+ case 'col-align-r' :
636+ case 'delimcenter' :
637+ case 'accent' :
638+ case 'accent-body' :
639+ case 'vlist' :
640+ case 'vlist-r' :
641+ case 'vlist-s' :
642+ case 'svg-align' :
643+ case 'hide-tail' :
644+ case 'halfarrow-left' :
645+ case 'halfarrow-right' :
646+ case 'brace-left' :
647+ case 'brace-center' :
648+ case 'brace-right' :
649+ case 'root' :
650+ case 'sqrt' :
651+ case 'pstrut' :
652+ case 'arraycolsep' :
653+ case 'vertical-separator' :
654+ case 'frac-line' :
655+ case 'mfrac' :
629656 // Ignore these classes because they don't have a CSS definition
630657 // in katex.scss, but we encounter them in the generated HTML.
631658 // (Why are they there if they're not used? The story seems to be:
@@ -636,6 +663,24 @@ class _KatexParser {
636663 // )
637664 break ;
638665
666+ case 'overline' :
667+ case 'underline' :
668+ break ;
669+
670+ case 'overline-line' :
671+ borderStyle = KatexBorderStyle (
672+ position: KatexBorderPosition .bottom,
673+ widthEm: 0.04 ,
674+ );
675+ break ;
676+
677+ case 'underline-line' :
678+ borderStyle = KatexBorderStyle (
679+ position: KatexBorderPosition .bottom,
680+ widthEm: 0.04 ,
681+ );
682+ break ;
683+
639684 default :
640685 assert (debugLog ('KaTeX: Unsupported CSS class: $spanClass ' ));
641686 unsupportedCssClasses.add (spanClass);
@@ -644,6 +689,18 @@ class _KatexParser {
644689 }
645690
646691 final inlineStyles = _parseInlineStyles (element);
692+ // Extract border width if borderStyle was set
693+ if (borderStyle != null ) {
694+ if (inlineStyles != null ) {
695+ final borderWidthEm = _takeStyleEm (inlineStyles, 'border-bottom-width' );
696+ if (borderWidthEm != null ) {
697+ borderStyle = KatexBorderStyle (
698+ position: borderStyle.position,
699+ widthEm: borderWidthEm,
700+ color: borderStyle.color,
701+ );
702+ }}
703+ }
647704 final styles = KatexSpanStyles (
648705 widthEm: widthEm,
649706 fontFamily: fontFamily,
@@ -657,19 +714,37 @@ class _KatexParser {
657714 marginRightEm: _takeStyleEm (inlineStyles, 'margin-right' ),
658715 color: _takeStyleColor (inlineStyles, 'color' ),
659716 position: _takeStylePosition (inlineStyles, 'position' ),
717+ borderStyle: borderStyle,
660718 // TODO handle more CSS properties
661719 );
662720 if (inlineStyles != null && inlineStyles.isNotEmpty) {
663721 for (final property in inlineStyles.keys) {
722+ // Ignore known properties that don't need special handling
723+ if (property == 'width' || property == 'min-width' || property == 'border-bottom-width' ) {continue ;}
664724 assert (debugLog ('KaTeX: Unexpected inline CSS property: $property ' ));
665725 unsupportedInlineCssProperties.add (property);
666726 _hasError = true ;
667727 }
668728 }
669729 if (styles.topEm != null && styles.position != KatexSpanPosition .relative) {
670730 // The meaning of `top` would be different without `position: relative`.
731+ // Allow 'top' without 'position: relative' inside vlist structures
732+ var parent = element.parent;
733+ var isInVlist = false ;
734+
735+ while (parent != null ) {
736+ if (parent.localName == 'span' ) {
737+ final className = parent.className;
738+ if (className.contains ('vlist' ) || className.contains ('overline' ) || className.contains ('underline' )) {
739+ isInVlist = true ;
740+ break ;
741+ }}
742+ parent = parent.parent;
743+ }
744+ if (! isInVlist) {
671745 throw _KatexHtmlParseError (
672746 'unsupported inline CSS property "top" given "position: ${styles .position }"' );
747+ }
673748 }
674749
675750 String ? text;
@@ -840,6 +915,39 @@ enum KatexSpanPosition {
840915 relative,
841916}
842917
918+ enum KatexBorderPosition {
919+ top,
920+ bottom,
921+ }
922+
923+ class KatexBorderStyle {
924+ const KatexBorderStyle ({
925+ required this .position,
926+ required this .widthEm,
927+ this .color,
928+ });
929+
930+ final KatexBorderPosition position;
931+ final double widthEm;
932+ final KatexSpanColor ? color;
933+
934+ @override
935+ bool operator == (Object other) {
936+ return other is KatexBorderStyle &&
937+ other.position == position &&
938+ other.widthEm == widthEm &&
939+ other.color == color;
940+ }
941+
942+ @override
943+ int get hashCode => Object .hash ('KatexBorderStyle' , position, widthEm, color);
944+
945+ @override
946+ String toString () {
947+ return '${objectRuntimeType (this , 'KatexBorderStyle' )}($position , $widthEm , $color )' ;
948+ }
949+ }
950+
843951class KatexSpanColor {
844952 const KatexSpanColor (this .r, this .g, this .b, this .a);
845953
@@ -893,6 +1001,7 @@ class KatexSpanStyles {
8931001
8941002 final KatexSpanColor ? color;
8951003 final KatexSpanPosition ? position;
1004+ final KatexBorderStyle ? borderStyle;
8961005
8971006 const KatexSpanStyles ({
8981007 this .widthEm,
@@ -907,6 +1016,7 @@ class KatexSpanStyles {
9071016 this .textAlign,
9081017 this .color,
9091018 this .position,
1019+ this .borderStyle,
9101020 });
9111021
9121022 @override
@@ -924,6 +1034,7 @@ class KatexSpanStyles {
9241034 textAlign,
9251035 color,
9261036 position,
1037+ borderStyle,
9271038 );
9281039
9291040 @override
@@ -940,7 +1051,8 @@ class KatexSpanStyles {
9401051 other.fontStyle == fontStyle &&
9411052 other.textAlign == textAlign &&
9421053 other.color == color &&
943- other.position == position;
1054+ other.position == position &&
1055+ other.borderStyle == borderStyle;
9441056 }
9451057
9461058 @override
@@ -958,6 +1070,7 @@ class KatexSpanStyles {
9581070 if (textAlign != null ) args.add ('textAlign: $textAlign ' );
9591071 if (color != null ) args.add ('color: $color ' );
9601072 if (position != null ) args.add ('position: $position ' );
1073+ if (borderStyle != null ) args.add ('borderStyle: $borderStyle ' );
9611074 return '${objectRuntimeType (this , 'KatexSpanStyles' )}(${args .join (', ' )})' ;
9621075 }
9631076
@@ -975,6 +1088,7 @@ class KatexSpanStyles {
9751088 bool textAlign = true ,
9761089 bool color = true ,
9771090 bool position = true ,
1091+ bool borderStyle = true ,
9781092 }) {
9791093 return KatexSpanStyles (
9801094 widthEm: widthEm ? this .widthEm : null ,
@@ -989,6 +1103,7 @@ class KatexSpanStyles {
9891103 textAlign: textAlign ? this .textAlign : null ,
9901104 color: color ? this .color : null ,
9911105 position: position ? this .position : null ,
1106+ borderStyle: borderStyle ? this .borderStyle : null ,
9921107 );
9931108 }
9941109}
0 commit comments