-
Notifications
You must be signed in to change notification settings - Fork 347
KaTeX overline underline border #1919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 1 commit
c6d8b3b
444a32f
676ad48
0766f7a
ea5ff37
c2eda0c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -411,6 +411,7 @@ class _KatexParser { | |
| KatexSpanFontWeight? fontWeight; | ||
| KatexSpanFontStyle? fontStyle; | ||
| KatexSpanTextAlign? textAlign; | ||
| KatexBorderStyle? borderStyle; | ||
| var index = 0; | ||
| while (index < spanClasses.length) { | ||
| final spanClass = spanClasses[index++]; | ||
|
|
@@ -626,6 +627,32 @@ class _KatexParser { | |
| case 'nobreak': | ||
| case 'allowbreak': | ||
| case 'mathdefault': | ||
| case 'tag': | ||
| case 'eqn-num': | ||
| case 'mtable': | ||
| case 'col-align-l': | ||
| case 'col-align-c': | ||
| case 'col-align-r': | ||
| case 'delimcenter': | ||
| case 'accent': | ||
| case 'accent-body': | ||
| case 'vlist': | ||
| case 'vlist-r': | ||
| case 'vlist-s': | ||
| case 'svg-align': | ||
| case 'hide-tail': | ||
| case 'halfarrow-left': | ||
| case 'halfarrow-right': | ||
| case 'brace-left': | ||
| case 'brace-center': | ||
| case 'brace-right': | ||
| case 'root': | ||
| case 'sqrt': | ||
| case 'pstrut': | ||
| case 'arraycolsep': | ||
| case 'vertical-separator': | ||
| case 'frac-line': | ||
| case 'mfrac': | ||
| // 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: | ||
|
|
@@ -636,6 +663,24 @@ class _KatexParser { | |
| // ) | ||
| break; | ||
|
|
||
| case 'overline': | ||
| case 'underline': | ||
| break; | ||
|
Comment on lines
640
to
643
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason these classes are ignored? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. comment #1919 (comment) |
||
|
|
||
| case 'overline-line': | ||
| borderStyle = KatexBorderStyle( | ||
| position: KatexBorderPosition.bottom, | ||
| widthEm: 0.04, | ||
| ); | ||
| break; | ||
|
|
||
| case 'underline-line': | ||
| borderStyle = KatexBorderStyle( | ||
| position: KatexBorderPosition.bottom, | ||
| widthEm: 0.04, | ||
| ); | ||
|
Comment on lines
645
to
660
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please add the CSS source inline from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In katex.scss, I found this -- .overline .overline-line,
.underline .underline-line,
.hline {
display: inline-block;
width: 100%;
border-bottom-style: solid;
}so the I should add this, right? // .overline-line { display: inline-block; width: 100%; border-bottom-style: solid; }
// Border applied via inline style: border-top-width: 0.04em; |
||
| break; | ||
|
|
||
| default: | ||
| assert(debugLog('KaTeX: Unsupported CSS class: $spanClass')); | ||
| unsupportedCssClasses.add(spanClass); | ||
|
|
@@ -644,6 +689,18 @@ class _KatexParser { | |
| } | ||
|
|
||
| final inlineStyles = _parseInlineStyles(element); | ||
| // Extract border width if borderStyle was set | ||
| if (borderStyle != null) { | ||
| if (inlineStyles != null) { | ||
| final borderWidthEm = _takeStyleEm(inlineStyles, 'border-bottom-width'); | ||
| if (borderWidthEm != null) { | ||
| borderStyle = KatexBorderStyle( | ||
| position: borderStyle.position, | ||
| widthEm: borderWidthEm, | ||
| color: borderStyle.color, | ||
| ); | ||
| }} | ||
| } | ||
| final styles = KatexSpanStyles( | ||
| widthEm: widthEm, | ||
| fontFamily: fontFamily, | ||
|
|
@@ -657,10 +714,13 @@ class _KatexParser { | |
| marginRightEm: _takeStyleEm(inlineStyles, 'margin-right'), | ||
| color: _takeStyleColor(inlineStyles, 'color'), | ||
| position: _takeStylePosition(inlineStyles, 'position'), | ||
| borderStyle: borderStyle, | ||
| // TODO handle more CSS properties | ||
| ); | ||
| if (inlineStyles != null && inlineStyles.isNotEmpty) { | ||
| for (final property in inlineStyles.keys) { | ||
| // Ignore known properties that don't need special handling | ||
| if (property == 'width' || property == 'min-width' || property == 'border-bottom-width') {continue;} | ||
| assert(debugLog('KaTeX: Unexpected inline CSS property: $property')); | ||
| unsupportedInlineCssProperties.add(property); | ||
| _hasError = true; | ||
|
|
@@ -840,6 +900,39 @@ enum KatexSpanPosition { | |
| relative, | ||
| } | ||
|
|
||
| enum KatexBorderPosition { | ||
| top, | ||
| bottom, | ||
| } | ||
|
|
||
| class KatexBorderStyle { | ||
| const KatexBorderStyle({ | ||
| required this.position, | ||
| required this.widthEm, | ||
| this.color, | ||
| }); | ||
|
|
||
| final KatexBorderPosition position; | ||
| final double widthEm; | ||
| final KatexSpanColor? color; | ||
|
|
||
| @override | ||
| bool operator ==(Object other) { | ||
| return other is KatexBorderStyle && | ||
| other.position == position && | ||
| other.widthEm == widthEm && | ||
| other.color == color; | ||
| } | ||
|
|
||
| @override | ||
| int get hashCode => Object.hash('KatexBorderStyle', position, widthEm, color); | ||
|
|
||
| @override | ||
| String toString() { | ||
| return '${objectRuntimeType(this, 'KatexBorderStyle')}($position, $widthEm, $color)'; | ||
| } | ||
| } | ||
|
|
||
| class KatexSpanColor { | ||
| const KatexSpanColor(this.r, this.g, this.b, this.a); | ||
|
|
||
|
|
@@ -893,6 +986,7 @@ class KatexSpanStyles { | |
|
|
||
| final KatexSpanColor? color; | ||
| final KatexSpanPosition? position; | ||
| final KatexBorderStyle? borderStyle; | ||
|
|
||
| const KatexSpanStyles({ | ||
| this.widthEm, | ||
|
|
@@ -907,6 +1001,7 @@ class KatexSpanStyles { | |
| this.textAlign, | ||
| this.color, | ||
| this.position, | ||
| this.borderStyle, | ||
| }); | ||
|
|
||
| @override | ||
|
|
@@ -924,6 +1019,7 @@ class KatexSpanStyles { | |
| textAlign, | ||
| color, | ||
| position, | ||
| borderStyle, | ||
| ); | ||
|
|
||
| @override | ||
|
|
@@ -940,7 +1036,8 @@ class KatexSpanStyles { | |
| other.fontStyle == fontStyle && | ||
| other.textAlign == textAlign && | ||
| other.color == color && | ||
| other.position == position; | ||
| other.position == position && | ||
| other.borderStyle == borderStyle; | ||
| } | ||
|
|
||
| @override | ||
|
|
@@ -958,6 +1055,7 @@ class KatexSpanStyles { | |
| if (textAlign != null) args.add('textAlign: $textAlign'); | ||
| if (color != null) args.add('color: $color'); | ||
| if (position != null) args.add('position: $position'); | ||
| if (borderStyle != null) args.add('borderStyle: $borderStyle'); | ||
| return '${objectRuntimeType(this, 'KatexSpanStyles')}(${args.join(', ')})'; | ||
| } | ||
|
|
||
|
|
@@ -975,6 +1073,7 @@ class KatexSpanStyles { | |
| bool textAlign = true, | ||
| bool color = true, | ||
| bool position = true, | ||
| bool borderStyle = true, | ||
| }) { | ||
| return KatexSpanStyles( | ||
| widthEm: widthEm ? this.widthEm : null, | ||
|
|
@@ -989,6 +1088,7 @@ class KatexSpanStyles { | |
| textAlign: textAlign ? this.textAlign : null, | ||
| color: color ? this.color : null, | ||
| position: position ? this.position : null, | ||
| borderStyle: borderStyle ? this.borderStyle : null, | ||
| ); | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -42,12 +42,13 @@ class KatexWidget extends StatelessWidget { | |
| Widget build(BuildContext context) { | ||
| Widget widget = _KatexNodeList(nodes: nodes); | ||
|
|
||
| return Directionality( | ||
| return IntrinsicWidth( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain the reasoning behind this change? If it is needed, then it should have it's own separate commit. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. without
|
||
| child: Directionality( | ||
| textDirection: TextDirection.ltr, | ||
| child: DefaultTextStyle( | ||
| style: mkBaseKatexTextStyle(textStyle).copyWith( | ||
| color: ContentTheme.of(context).textStylePlainParagraph.color), | ||
| child: widget)); | ||
| child: widget))); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -122,6 +123,19 @@ class _KatexSpan extends StatelessWidget { | |
| Color.fromARGB(katexColor.a, katexColor.r, katexColor.g, katexColor.b), | ||
| null => null, | ||
| }; | ||
| if (styles.borderStyle case final borderStyle?) { | ||
| final currentColor = color ?? DefaultTextStyle.of(context).style.color!; | ||
| final Color borderColor = borderStyle.color != null | ||
| ? Color.fromARGB(borderStyle.color!.a, borderStyle.color!.r, borderStyle.color!.g, borderStyle.color!.b) | ||
| : currentColor; | ||
| final double borderWidth = borderStyle.widthEm * em; | ||
|
|
||
| return Container( | ||
| constraints: const BoxConstraints(minWidth: double.infinity), | ||
| height: borderWidth, | ||
| color: borderColor, | ||
| ); | ||
| } | ||
|
Comment on lines
127
to
140
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Continued from #1919 (comment). The return here ends up discarding the base widget, again is there a reason to not use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry about discarding base widget, this change should be fine I think widget = Container(
constraints: const BoxConstraints(minWidth: double.infinity),
decoration: BoxDecoration(
border: Border(bottom: BorderSide(color: borderColor, width: borderWidth))),
child: widget,
);about |
||
|
|
||
| TextStyle? textStyle; | ||
| if (fontFamily != null || | ||
|
|
@@ -232,11 +246,11 @@ 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 IntrinsicWidth(child: Stack(children: List.unmodifiable(node.rows.map((row) { | ||
|
||
| return Transform.translate( | ||
| offset: Offset(0, row.verticalOffsetEm * em), | ||
| child: _KatexSpan(row.node)); | ||
| }))); | ||
| })))); | ||
| } | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -731,6 +731,109 @@ class KatexExample extends ContentExample { | |
| ]), | ||
| ]), | ||
| ]); | ||
|
|
||
| static final overline = KatexExample.block( | ||
| // https://chat.zulip.org/#narrow/channel/7-test-here/topic/Saif.20KaTeX/near/2285099 | ||
| r'overline: \overline{AB}', | ||
| 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(borderStyle: KatexBorderStyle(position: KatexBorderPosition.bottom, widthEm: 0.04, color: null)), | ||
| nodes: []), | ||
| ])), | ||
| ]), | ||
| ]), | ||
| ]), | ||
| ]); | ||
|
|
||
| static final underline = KatexExample.block( | ||
| // https://chat.zulip.org/#narrow/channel/7-test-here/topic/Saif.20KaTeX/near/2285099 | ||
| r'underline: \underline{AB}', | ||
| 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(borderStyle: KatexBorderStyle(position: KatexBorderPosition.bottom, widthEm: 0.04, color: null)), | ||
| 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 { | ||
|
|
@@ -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 = [ | ||
|
|
@@ -821,4 +926,4 @@ void main() async { | |
| }, skip: Platform.isWindows, // [intended] purely analyzes source, so | ||
| // any one platform is enough; avoid dealing with Windows file paths | ||
| ); | ||
| } | ||
| } | ||
|
||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, can you explain the reasoning for ignoring all these classes? I see many here that we already handle in some way.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sorry for lack of explanation.
with
forceRenderKatexoff, the katex code kept falling back to source code, so I ignored all possible warnings, but actually by just ignoring overline and underline case the fallback problem was averted. So these cases can be removed.The reason of this:
is that \overline or \underline and \overline-line or \underline-line are parent-child constructs and by assigning borderStyle to both, the parser ends up with a redundant border on the outer wrapper, and that gives this error ---
ignoring those fixes the issue and also matches KaTeX’s expected DOM structure for \overline{AB}.