Skip to content

Commit

Permalink
feat: Add support for inline KaTeX rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
0xc3u committed Oct 30, 2024
1 parent 38f60da commit 98f458b
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ Header 1 | Header 2 | Header 3
### KaTex Support
based on _CSharpMath_
$$\int_0^\infty \mathrm{e}^{-x}\,\mathrm{d}x$$
$$A_{triangle}=\frac{1}{2}({b}\cdot{h})$$
Area of a circle is $\pi r^2$
### Lokal SVG Image
Expand Down
29 changes: 25 additions & 4 deletions src/Indiko.Maui.Controls.Markdown/LatexView.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
using SkiaSharp;
using SkiaSharp.Views.Maui;
using SkiaSharp.Views.Maui.Controls;
using Typography.OpenFont;

namespace Indiko.Maui.Controls.Markdown;

public sealed class LatexView : SKCanvasView
{

private System.Drawing.RectangleF _bounds;

public static readonly BindableProperty TextProperty = BindableProperty.Create(propertyName: nameof(Text),
returnType: typeof(string), declaringType: typeof(LatexView), propertyChanged: OnLatexChanged);

Expand Down Expand Up @@ -57,23 +61,41 @@ private static void OnLatexChanged(BindableObject bindable, object oldValue, obj
{
var view = bindable as LatexView;
view?.InvalidateSurface();
view?.UpdateSize();
}
}

public LatexView()
{
PaintSurface += OnPainting;
SizeChanged += (s, e) => UpdateSize();
}

~LatexView()
{
PaintSurface -= OnPainting;
}

protected override void OnSizeAllocated(double width, double height)
private void UpdateSize()
{
base.OnSizeAllocated(width, height);
InvalidateSurface();
if (!string.IsNullOrEmpty(Text))
{
var painter = new CSharpMath.SkiaSharp.MathPainter
{
LaTeX = Text,
FontSize = FontSize,
AntiAlias = true,
DisplayErrorInline = true,
TextColor = TextColor.ToSKColor(),
ErrorColor = ErrorColor.ToSKColor(),
HighlightColor = HighlightColor.ToSKColor(),
PaintStyle = CSharpMath.Rendering.FrontEnd.PaintStyle.Fill
};

var measuredBounds = painter.Measure();
WidthRequest = (measuredBounds.Width * 0.5);
HeightRequest = (measuredBounds.Height * 0.5);
}
}

private void OnPainting(object? sender, SKPaintSurfaceEventArgs e)
Expand All @@ -100,7 +122,6 @@ private void OnPainting(object? sender, SKPaintSurfaceEventArgs e)
HighlightColor = HighlightColor.ToSKColor(),
PaintStyle = CSharpMath.Rendering.FrontEnd.PaintStyle.Fill
};

painter.Draw(canvas);
}
}
97 changes: 90 additions & 7 deletions src/Indiko.Maui.Controls.Markdown/MarkdownView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Input;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Controls.Shapes;
using Microsoft.Maui.Graphics.Text;
using SkiaSharp;
using Image = Microsoft.Maui.Controls.Image;

Expand All @@ -12,6 +14,8 @@ public class MarkdownView : ContentView
{

private static readonly Regex KaTeXBlockRegex = new Regex(@"\$\$(.*?)\$\$", RegexOptions.Compiled | RegexOptions.Singleline);
private static readonly Regex KaTeXInlineRegex = new Regex(@"\$(.*?)\$", RegexOptions.Compiled);

private readonly Thickness _defaultListIndent = new(10, 0, 10, 0);
private Dictionary<string, ImageSource> _imageCache = [];

Expand Down Expand Up @@ -498,7 +502,6 @@ private void RenderMarkdown()
gridRow++;
isExitingList = false;
}

else if (IsKaTeXBlock(line))
{
var match = KaTeXBlockRegex.Match(line);
Expand All @@ -507,21 +510,27 @@ private void RenderMarkdown()
var latexView = new LatexView
{
Text = latexFormula,
FontSize = 48f,
FontSize = (float)(TextFontSize * 4),
TextColor = TextColor,
HighlightColor = Colors.Transparent,
ErrorColor = Colors.Red,
HorizontalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.Center,
VerticalOptions = LayoutOptions.Center,
HeightRequest = 50
};

grid.Children.Add(latexView);
Grid.SetRow(latexView, gridRow++);
Grid.SetColumnSpan(latexView, 2);
continue;
}

else if (IsKaTeXInline(line))
{
var katexGrid = CreateInlineKatexBlock(line, TextColor);
grid.Children.Add(katexGrid);
Grid.SetRow(katexGrid, gridRow++);
Grid.SetColumnSpan(katexGrid, 2);
continue;
}
else if (IsTable(lines, i, out int tableEndIndex)) // Detect table
{
var table = CreateTable(lines, i, tableEndIndex);
Expand Down Expand Up @@ -665,6 +674,12 @@ private static bool IsKaTeXBlock(string line)
return KaTeXBlockRegex.IsMatch(trimmedLine);
}

private static bool IsKaTeXInline(string line)
{
string trimmedLine = line.TrimStart();
return KaTeXInlineRegex.IsMatch(trimmedLine);
}

private static bool IsHorizontalRule(string line)
{
string compactLine = line.Replace(" ", string.Empty);
Expand Down Expand Up @@ -740,7 +755,6 @@ private static bool IsTaskList(string line, out bool isChecked)
return line.StartsWith("- [ ]") || isChecked;
}


private static bool IsTable(string[] lines, int currentIndex, out int tableEndIndex)
{
tableEndIndex = currentIndex;
Expand Down Expand Up @@ -785,7 +799,6 @@ private Image CreateImageBlock(string line)

return image;
}

private Border CreateCodeBlock(string codeText, out Label contentLabel)
{
Label content = new()
Expand Down Expand Up @@ -884,6 +897,76 @@ private FormattedString CreateFormattedString(string line, Color textColor)
return formattedString;
}

private Grid CreateInlineKatexBlock(string line, Color textColor)
{
var grid = new Grid
{
ColumnDefinitions =
{
new ColumnDefinition { Width = GridLength.Auto },
new ColumnDefinition { Width = GridLength.Auto },
new ColumnDefinition { Width = GridLength.Auto }
},
RowDefinitions =
{
new RowDefinition { Height = GridLength.Auto }
}
};

var match = KaTeXInlineRegex.Match(line);

if (match.Success)
{
string beforeText = line[..match.Index];
string katexFormula = match.Groups[1].Value;
string afterText = line[(match.Index + match.Length)..];

if (!string.IsNullOrEmpty(beforeText))
{
var beforeLabel = new Label
{
Text = beforeText,
TextColor = textColor,
FontSize = TextFontSize,
FontFamily = TextFontFace,
VerticalOptions = LayoutOptions.Center
};
grid.Children.Add(beforeLabel);
Grid.SetColumn(beforeLabel, 0);
}

var latexView = new LatexView
{
Text = katexFormula,
FontSize = (float)TextFontSize * 4,
TextColor = TextColor,
HighlightColor = Colors.Transparent,
ErrorColor = Colors.Red,
HorizontalOptions = LayoutOptions.Start,
VerticalOptions = LayoutOptions.Center,
Margin = new Thickness(-10,-10)
};
grid.Children.Add(latexView);
Grid.SetColumn(latexView, 1);

if (!string.IsNullOrEmpty(afterText))
{
var afterLabel = new Label
{
Text = afterText,
TextColor = textColor,
FontSize = TextFontSize,
FontFamily = TextFontFace,
VerticalOptions = LayoutOptions.Center
};
grid.Children.Add(afterLabel);
Grid.SetColumn(afterLabel, 2);
}
}
return grid;
}


private void AddBulletPointToGrid(Grid grid, int gridRow)
{
string bulletPointSign = "-";
Expand Down

0 comments on commit 98f458b

Please sign in to comment.