Mostly working parser, some issues with glyph positioning. Should create a LatexGlyph : LatexSegment, and change LatexSegment.Glyphs to be of LatexGlyphs. Then, ON ITERATION, compute the altered positions of the LatexGlyphs and create the glyphruns from that.
This commit is contained in:
@@ -1,30 +1,36 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace LatexEditor.Fonts
|
namespace LatexEditor.Fonts
|
||||||
{
|
{
|
||||||
public class GlyphInfo
|
public class GlyphInfo
|
||||||
{
|
{
|
||||||
public CmFont Font;
|
public CmFont Font;
|
||||||
public Point Offset;
|
public double RelativeSize;
|
||||||
public double Size;
|
public int Char;
|
||||||
private int Char;
|
public Point RelativeOffset;
|
||||||
|
public Point BaselineOrigin;
|
||||||
|
|
||||||
public GlyphTypeface Gtf => Font.GlyphTypeface();
|
public GlyphTypeface Gtf => Font.GlyphTypeface();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Index of this glyph within the GlyphTypeface.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="KeyNotFoundException">Thrown if <see cref="Font"/> does not have a glyph for char <see cref="Char"/></exception>
|
|
||||||
public ushort Index => Gtf.CharacterToGlyphMap[Char];
|
public ushort Index => Gtf.CharacterToGlyphMap[Char];
|
||||||
|
|
||||||
public GlyphInfo(CmFont font, int character, double size, Point offset)
|
public double RelAdvWidth => Gtf.AdvanceWidths[Index];
|
||||||
|
public double RelAdvHeight => Gtf.AdvanceHeights[Index];
|
||||||
|
|
||||||
|
public Point Offset => new Point(
|
||||||
|
BaselineOrigin.X + RelativeOffset.X * RelativeSize,
|
||||||
|
BaselineOrigin.Y + RelativeOffset.Y * RelativeSize);
|
||||||
|
|
||||||
|
public GlyphInfo(CmFont font, int c, Point relativeOffset, Point baselineOrigin, double relativeSize = 1)
|
||||||
{
|
{
|
||||||
Font = font;
|
Font = font;
|
||||||
Char = character;
|
RelativeSize = relativeSize;
|
||||||
Size = size;
|
RelativeOffset = relativeOffset;
|
||||||
Offset = offset;
|
BaselineOrigin = baselineOrigin;
|
||||||
|
Char = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -76,7 +76,13 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="Parser\LatexNull.cs" />
|
||||||
<Compile Include="Parser\LatexParser.cs" />
|
<Compile Include="Parser\LatexParser.cs" />
|
||||||
|
<Compile Include="Parser\LatexReturn.cs" />
|
||||||
|
<Compile Include="Parser\LatexRun.cs" />
|
||||||
|
<Compile Include="Parser\LatexSegment.cs" />
|
||||||
|
<Compile Include="Parser\LatexSpace.cs" />
|
||||||
|
<Compile Include="Parser\LatexText.cs" />
|
||||||
<Compile Include="Parser\Lexer.cs" />
|
<Compile Include="Parser\Lexer.cs" />
|
||||||
<Compile Include="Parser\Token.cs" />
|
<Compile Include="Parser\Token.cs" />
|
||||||
<Compile Include="Parser\TokenDescriptor.cs" />
|
<Compile Include="Parser\TokenDescriptor.cs" />
|
||||||
|
|||||||
@@ -41,83 +41,6 @@ namespace LatexEditor
|
|||||||
set => SetValue(FontSizeProperty, value);
|
set => SetValue(FontSizeProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected List<GlyphInfo> Parse(string latex)
|
|
||||||
{
|
|
||||||
var x = 0d;
|
|
||||||
var y = 0d;
|
|
||||||
var font = CmFont.SerifItalic;
|
|
||||||
var size = FontSize;
|
|
||||||
|
|
||||||
var glyphs = new List<GlyphInfo>(latex.Length);
|
|
||||||
|
|
||||||
var state = new Stack<string>();
|
|
||||||
state.Push("-");
|
|
||||||
|
|
||||||
while (latex.Length > 0)
|
|
||||||
{
|
|
||||||
var s = state.Peek();
|
|
||||||
|
|
||||||
if (s == "cmd")
|
|
||||||
{
|
|
||||||
state.Pop();
|
|
||||||
var m = Regex.Match(latex, @"([a-z]+)(?=[^a-z]|$)", RegexOptions.IgnoreCase);
|
|
||||||
if (m.Index == 0)
|
|
||||||
{
|
|
||||||
var cmd = latex.Substring(0, m.Groups[1].Length);
|
|
||||||
switch (cmd)
|
|
||||||
{
|
|
||||||
case "it":
|
|
||||||
font = CmFont.SerifItalic;
|
|
||||||
break;
|
|
||||||
case "sym":
|
|
||||||
font = CmFont.Symbols;
|
|
||||||
break;
|
|
||||||
case "rm":
|
|
||||||
font = CmFont.Serif;
|
|
||||||
break;
|
|
||||||
case "sup":
|
|
||||||
size = FontSize * 0.4;
|
|
||||||
y = FontSize * (1 - 0.4);
|
|
||||||
break;
|
|
||||||
case "sub":
|
|
||||||
size = FontSize * 0.5;
|
|
||||||
y = -FontSize * 0.5 * 0.5;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
latex = latex.Substring(m.Length).TrimStart(' ');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
latex = latex.Substring(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (latex.StartsWith("\\"))
|
|
||||||
{
|
|
||||||
latex = latex.Substring(1);
|
|
||||||
|
|
||||||
state.Push("cmd");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var chr = latex[0];
|
|
||||||
latex = latex.Substring(1);
|
|
||||||
|
|
||||||
var gtf = font.GlyphTypeface();
|
|
||||||
var point = new Point(x, y);
|
|
||||||
var gi = new GlyphInfo(font, chr, size, point);
|
|
||||||
x += gtf.AdvanceWidths[gi.Index] * size;
|
|
||||||
glyphs.Add(gi);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return glyphs;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnRender(DrawingContext dc)
|
protected override void OnRender(DrawingContext dc)
|
||||||
{
|
{
|
||||||
Debug.WriteLine(dc, nameof(LatexViewer));
|
Debug.WriteLine(dc, nameof(LatexViewer));
|
||||||
@@ -125,22 +48,23 @@ namespace LatexEditor
|
|||||||
|
|
||||||
if (string.IsNullOrEmpty(Content)) return;
|
if (string.IsNullOrEmpty(Content)) return;
|
||||||
|
|
||||||
var glyphInfoList = LatexParser.Parse(Content);
|
var glyphInfoList = LatexParser.ToGlyphInfos(Content);
|
||||||
|
|
||||||
var gtfGroups = glyphInfoList.GroupBy(gi => gi.Gtf);
|
var gtfGroups = glyphInfoList.GroupBy(gi => gi.Gtf);
|
||||||
foreach (var gtfGroup in gtfGroups)
|
foreach (var gtfGroup in gtfGroups)
|
||||||
{
|
{
|
||||||
var gtf = gtfGroup.Key;
|
var gtf = gtfGroup.Key;
|
||||||
|
|
||||||
var sizeGroups = gtfGroup.GroupBy(gi => gi.Size);
|
var sizeGroups = gtfGroup.GroupBy(gi => gi.RelativeSize);
|
||||||
foreach (var sizeGroup in sizeGroups)
|
foreach (var sizeGroup in sizeGroups)
|
||||||
{
|
{
|
||||||
var size = sizeGroup.Key;
|
var size = sizeGroup.Key;
|
||||||
|
|
||||||
var glyphs = sizeGroup.Select(gi => gi.Index).ToList();
|
var glyphs = sizeGroup.Select(gi => gi.Index).ToList();
|
||||||
var advanceWidths = new double[glyphs.Count];
|
var advanceWidths = new double[glyphs.Count]; // all zero - position based only on offset
|
||||||
var offsets = sizeGroup.Select(gi => gi.Offset).ToList();
|
var offsets = sizeGroup.Select(gi => new Point(gi.Offset.X * FontSize, gi.Offset.Y * FontSize))
|
||||||
var gr = new GlyphRun(gtf, 0, false, size,
|
.ToList();
|
||||||
|
var gr = new GlyphRun(gtf, 0, false, size * FontSize,
|
||||||
glyphs, new Point(0, FontSize), advanceWidths,
|
glyphs, new Point(0, FontSize), advanceWidths,
|
||||||
offsets, null, null, null, null, null);
|
offsets, null, null, null, null, null);
|
||||||
|
|
||||||
|
|||||||
@@ -8,20 +8,20 @@
|
|||||||
Title="MainWindow" Height="350" Width="525">
|
Title="MainWindow" Height="350" Width="525">
|
||||||
<Grid>
|
<Grid>
|
||||||
<local:LatexViewer>
|
<local:LatexViewer>
|
||||||
<!-- e^{\pi i}=-1\{\textrm{in the complex plane}-->
|
<!-- e^{\pi i}=-1\{\textrm{in the complex plane}-->
|
||||||
A \ \alpha \quad N \ \nu \\
|
{2}{123}
|
||||||
B \ \beta \quad \Xi \ \xi \\
|
<!-- A \ \alpha \quad N \ \nu \\-->
|
||||||
\Gamma \ \gamma \quad O \ \omicron \\
|
<!-- B \ \beta \quad \Xi \ \xi \\-->
|
||||||
\Delta \ \delta \quad \Pi \ \pi \\
|
<!-- \Gamma \ \gamma \quad O \ \omicron \\-->
|
||||||
E \ \epsilon \quad P \ \rho \\
|
<!-- \Delta \ \delta \quad \Pi \ \pi \\-->
|
||||||
Z \ \zeta \quad \Sigma \ \sigma \\
|
<!-- E \ \epsilon \quad P \ \rho \\-->
|
||||||
H \ \eta \quad T \ \tau \\
|
<!-- Z \ \zeta \quad \Sigma \ \sigma \\-->
|
||||||
\Theta \ \theta \quad Y \ \upsilon \\
|
<!-- H \ \eta \quad T \ \tau \\-->
|
||||||
I \ \iota \quad \Phi \ \phi \\
|
<!-- \Theta \ \theta \quad Y \ \upsilon \\-->
|
||||||
K \ \kappa \quad X \ \chi \\
|
<!-- I \ \iota \quad \Phi \ \phi \\-->
|
||||||
\Lambda \ \lambda \quad \Psi \ \psi \\
|
<!-- K \ \kappa \quad X \ \chi \\-->
|
||||||
M \ \mu \quad \Omega \ \omega
|
<!-- \Lambda \ \lambda \quad \Psi \ \psi \\-->
|
||||||
|
<!-- M \ \mu \quad \Omega \ \omega-->
|
||||||
|
|
||||||
</local:LatexViewer>
|
</local:LatexViewer>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
13
WpfTex/LatexEditor/Parser/LatexNull.cs
Normal file
13
WpfTex/LatexEditor/Parser/LatexNull.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
|
namespace LatexEditor.Parser
|
||||||
|
{
|
||||||
|
public class LatexNull : LatexSegment
|
||||||
|
{
|
||||||
|
public override IEnumerable<GlyphInfo> Glyphs => Enumerable.Empty<GlyphInfo>();
|
||||||
|
public override double RelAdvWidth => 0;
|
||||||
|
public override double RelAdvHeight => 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Windows;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Windows.Navigation;
|
||||||
using LatexEditor.Fonts;
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
namespace LatexEditor.Parser
|
namespace LatexEditor.Parser
|
||||||
@@ -18,7 +19,7 @@ namespace LatexEditor.Parser
|
|||||||
new TokenDescriptor("letter", @"[\S]"), // nearly catch-all for uncaptured symbols
|
new TokenDescriptor("letter", @"[\S]"), // nearly catch-all for uncaptured symbols
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<string, int> greekLetters = new Dictionary<string, int>
|
internal static readonly Dictionary<string, int> GreekLetters = new Dictionary<string, int>
|
||||||
{
|
{
|
||||||
["alpha"] = 0x03b1,
|
["alpha"] = 0x03b1,
|
||||||
["beta"] = 0x03b2,
|
["beta"] = 0x03b2,
|
||||||
@@ -45,19 +46,22 @@ namespace LatexEditor.Parser
|
|||||||
["psi"] = 0x03c8,
|
["psi"] = 0x03c8,
|
||||||
["omega"] = 0x03c9,
|
["omega"] = 0x03c9,
|
||||||
|
|
||||||
["Gamma"] = 0x0393,
|
// Alpha Beta Epsilon Zeta Eta Iota Kappa Mu Nu
|
||||||
["Delta"] = 0x0394,
|
// Omicron Rho Tau and Upsilon all denoted by
|
||||||
["Theta"] = 0x0398,
|
// Latin symbols A B E Z H I K M N O P T and Y
|
||||||
["Lambda"] = 0x039b,
|
["Gamma"] = 0x0393,
|
||||||
["Xi"] = 0x039e,
|
["Delta"] = 0x0394,
|
||||||
["Pi"] = 0x03a0,
|
["Theta"] = 0x0398,
|
||||||
["Sigma"] = 0x03a3,
|
["Lambda"] = 0x039b,
|
||||||
["Phi"] = 0x03a6,
|
["Xi"] = 0x039e,
|
||||||
["Psi"] = 0x03a8,
|
["Pi"] = 0x03a0,
|
||||||
["Omega"] = 0x03a9,
|
["Sigma"] = 0x03a3,
|
||||||
|
["Phi"] = 0x03a6,
|
||||||
|
["Psi"] = 0x03a8,
|
||||||
|
["Omega"] = 0x03a9,
|
||||||
};
|
};
|
||||||
|
|
||||||
private static readonly Dictionary<string, double> spaces = new Dictionary<string, double>
|
internal static readonly Dictionary<string, double> Spaces = new Dictionary<string, double>
|
||||||
{
|
{
|
||||||
[","] = 0.16666,
|
[","] = 0.16666,
|
||||||
["!"] = -0.16666,
|
["!"] = -0.16666,
|
||||||
@@ -69,57 +73,12 @@ namespace LatexEditor.Parser
|
|||||||
["qquad"] = 4,
|
["qquad"] = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
public static IEnumerable<GlyphInfo> Parse(string latex) => Parse(LatexLexer.Tokenize(latex));
|
public static IEnumerable<GlyphInfo> ToGlyphInfos(string latex) => ToGlyphInfos(LatexLexer.Tokenize(latex));
|
||||||
|
|
||||||
private static IEnumerable<GlyphInfo> Parse(IEnumerable<Token> tokens)
|
private static IEnumerable<GlyphInfo> ToGlyphInfos(IEnumerable<Token> tokens)
|
||||||
{
|
{
|
||||||
var tokenQ = new Queue<Token>(tokens);
|
var lss = LatexSegment.ToLatexSegment(tokens);
|
||||||
var glyphs = new List<GlyphInfo>();
|
return lss.Glyphs;
|
||||||
|
|
||||||
const double size = 20;
|
|
||||||
var x = 0d;
|
|
||||||
var y = 0d;
|
|
||||||
|
|
||||||
void AddGlyph(CmFont font, int c)
|
|
||||||
{
|
|
||||||
glyphs.Add(new GlyphInfo(font, c, size, new Point(x * size, y * size)));
|
|
||||||
var gtf = font.GlyphTypeface();
|
|
||||||
var idx = gtf.CharacterToGlyphMap[c];
|
|
||||||
x += gtf.AdvanceWidths[idx];
|
|
||||||
}
|
|
||||||
|
|
||||||
while (tokenQ.Count > 0)
|
|
||||||
{
|
|
||||||
var token = tokenQ.Dequeue();
|
|
||||||
var val = token.Value;
|
|
||||||
switch (token.TokenName)
|
|
||||||
{
|
|
||||||
case "letter":
|
|
||||||
AddGlyph(CmFont.SerifItalic, val[0]);
|
|
||||||
break;
|
|
||||||
case "number":
|
|
||||||
AddGlyph(CmFont.Serif, val[0]);
|
|
||||||
break;
|
|
||||||
case "command":
|
|
||||||
if (greekLetters.ContainsKey(val))
|
|
||||||
AddGlyph(CmFont.SerifItalic, greekLetters[val]);
|
|
||||||
else if (spaces.ContainsKey(val))
|
|
||||||
x += spaces[val];
|
|
||||||
else
|
|
||||||
foreach (var c in token.CapturedString)
|
|
||||||
AddGlyph(CmFont.SerifItalic, c);
|
|
||||||
break;
|
|
||||||
case "escape":
|
|
||||||
if (val == "\\")
|
|
||||||
{
|
|
||||||
x = 0;
|
|
||||||
y -= 1;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return glyphs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
18
WpfTex/LatexEditor/Parser/LatexReturn.cs
Normal file
18
WpfTex/LatexEditor/Parser/LatexReturn.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
|
namespace LatexEditor.Parser
|
||||||
|
{
|
||||||
|
public class LatexReturn : LatexSegment
|
||||||
|
{
|
||||||
|
public override IEnumerable<GlyphInfo> Glyphs => Enumerable.Empty<GlyphInfo>();
|
||||||
|
public override double RelAdvWidth => 0;
|
||||||
|
public override double RelAdvHeight { get; }
|
||||||
|
|
||||||
|
public LatexReturn(double relAdvHeight)
|
||||||
|
{
|
||||||
|
RelAdvHeight = relAdvHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
41
WpfTex/LatexEditor/Parser/LatexRun.cs
Normal file
41
WpfTex/LatexEditor/Parser/LatexRun.cs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
|
namespace LatexEditor.Parser
|
||||||
|
{
|
||||||
|
public class LatexRun : LatexSegment
|
||||||
|
{
|
||||||
|
public override IEnumerable<GlyphInfo> Glyphs => Segments.SelectMany(ls => ls.Glyphs);
|
||||||
|
|
||||||
|
public override double RelAdvWidth { get; }
|
||||||
|
public override double RelAdvHeight { get; }
|
||||||
|
|
||||||
|
public IEnumerable<LatexSegment> Segments { get; set; }
|
||||||
|
|
||||||
|
public LatexRun(IEnumerable<LatexSegment> segments)
|
||||||
|
{
|
||||||
|
Segments = segments;
|
||||||
|
|
||||||
|
// to shift characters into position
|
||||||
|
var x = 0d;
|
||||||
|
var y = 0d;
|
||||||
|
foreach (var segment in Segments)
|
||||||
|
{
|
||||||
|
if (segment is LatexReturn)
|
||||||
|
{
|
||||||
|
x = 0d;
|
||||||
|
y -= segment.RelAdvHeight;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach (var gi in segment.Glyphs)
|
||||||
|
{
|
||||||
|
gi.BaselineOrigin.X = x;
|
||||||
|
gi.BaselineOrigin.Y = y;
|
||||||
|
}
|
||||||
|
x += segment.RelAdvWidth;
|
||||||
|
}
|
||||||
|
RelAdvWidth = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
101
WpfTex/LatexEditor/Parser/LatexSegment.cs
Normal file
101
WpfTex/LatexEditor/Parser/LatexSegment.cs
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
|
namespace LatexEditor.Parser
|
||||||
|
{
|
||||||
|
public abstract class LatexSegment
|
||||||
|
{
|
||||||
|
[NotNull]
|
||||||
|
public abstract IEnumerable<GlyphInfo> Glyphs { get; }
|
||||||
|
|
||||||
|
public static LatexSegment ToLatexSegment(IEnumerable<Token> tokens)
|
||||||
|
{
|
||||||
|
var tokenQ = new Queue<Token>(tokens);
|
||||||
|
var segments = new List<LatexSegment>();
|
||||||
|
while (tokenQ.Count > 0)
|
||||||
|
if (PopLatexSegment(tokenQ, out var ls))
|
||||||
|
segments.Add(ls);
|
||||||
|
|
||||||
|
if (segments.Count == 0)
|
||||||
|
return new LatexNull();
|
||||||
|
if (segments.Count == 1)
|
||||||
|
return segments[0];
|
||||||
|
return new LatexRun(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract double RelAdvWidth { get; }
|
||||||
|
public abstract double RelAdvHeight { get; }
|
||||||
|
|
||||||
|
private static bool PopLatexSegment(Queue<Token> tokens, out LatexSegment val)
|
||||||
|
{
|
||||||
|
val = null;
|
||||||
|
|
||||||
|
if (tokens.Count == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// todo: tabulate this
|
||||||
|
|
||||||
|
var head = tokens.Dequeue();
|
||||||
|
if (head.TokenName == "letter")
|
||||||
|
val = new LatexText(CmFont.SerifItalic, head.Value);
|
||||||
|
if (head.TokenName == "command")
|
||||||
|
{
|
||||||
|
if (LatexParser.GreekLetters.ContainsKey(head.Value))
|
||||||
|
val = new LatexText(CmFont.SerifItalic, LatexParser.GreekLetters[head.Value]);
|
||||||
|
if (LatexParser.Spaces.ContainsKey(head.Value))
|
||||||
|
val = new LatexSpace(LatexParser.Spaces[head.Value]);
|
||||||
|
if (head.Value == "^")
|
||||||
|
if (PopLatexSegment(tokens, out var content))
|
||||||
|
{
|
||||||
|
val = new LatexSuper(content);
|
||||||
|
}
|
||||||
|
else // to prevent possible nullref, but still incorrect.
|
||||||
|
{
|
||||||
|
val = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (val == null) // if no command matches
|
||||||
|
val = new LatexText(CmFont.SerifItalic, head.CapturedString);
|
||||||
|
}
|
||||||
|
if (head.TokenName == "number")
|
||||||
|
val = new LatexText(CmFont.Serif, head.Value);
|
||||||
|
if (head.TokenName == "escape")
|
||||||
|
if (head.Value == "\\")
|
||||||
|
val = new LatexReturn(1);
|
||||||
|
if (head.TokenName == "open")
|
||||||
|
{
|
||||||
|
var segments = new List<LatexSegment>();
|
||||||
|
while (tokens.Peek().TokenName != "close")
|
||||||
|
if (PopLatexSegment(tokens, out var content))
|
||||||
|
segments.Add(content);
|
||||||
|
tokens.Dequeue(); // toss the close
|
||||||
|
val = new LatexRun(segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
return val != null || PopLatexSegment(tokens, out val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LatexSuper : LatexSegment
|
||||||
|
{
|
||||||
|
public override IEnumerable<GlyphInfo> Glyphs => Content.Glyphs;
|
||||||
|
|
||||||
|
public override double RelAdvWidth => Content.RelAdvWidth;
|
||||||
|
public override double RelAdvHeight => Content.RelAdvHeight;
|
||||||
|
|
||||||
|
public LatexSegment Content { get; set; }
|
||||||
|
|
||||||
|
public LatexSuper(LatexSegment content)
|
||||||
|
{
|
||||||
|
Content = content;
|
||||||
|
|
||||||
|
foreach (var gi in Content.Glyphs)
|
||||||
|
{
|
||||||
|
gi.RelativeOffset.Y += 1;
|
||||||
|
gi.RelativeSize *= 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
WpfTex/LatexEditor/Parser/LatexSpace.cs
Normal file
19
WpfTex/LatexEditor/Parser/LatexSpace.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
|
namespace LatexEditor.Parser
|
||||||
|
{
|
||||||
|
public class LatexSpace : LatexSegment
|
||||||
|
{
|
||||||
|
public override IEnumerable<GlyphInfo> Glyphs => Enumerable.Empty<GlyphInfo>();
|
||||||
|
public override double RelAdvWidth { get; }
|
||||||
|
public override double RelAdvHeight { get; }
|
||||||
|
|
||||||
|
public LatexSpace(double relAdvWidth)
|
||||||
|
{
|
||||||
|
RelAdvWidth = relAdvWidth;
|
||||||
|
RelAdvHeight = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
WpfTex/LatexEditor/Parser/LatexText.cs
Normal file
35
WpfTex/LatexEditor/Parser/LatexText.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Windows;
|
||||||
|
using LatexEditor.Fonts;
|
||||||
|
|
||||||
|
namespace LatexEditor.Parser
|
||||||
|
{
|
||||||
|
public class LatexText : LatexSegment
|
||||||
|
{
|
||||||
|
private readonly List<GlyphInfo> _glyphs = new List<GlyphInfo>();
|
||||||
|
|
||||||
|
public override IEnumerable<GlyphInfo> Glyphs => _glyphs;
|
||||||
|
public override double RelAdvWidth { get; }
|
||||||
|
public override double RelAdvHeight { get; }
|
||||||
|
|
||||||
|
public LatexText(CmFont cmFont, int c)
|
||||||
|
{
|
||||||
|
var gi = new GlyphInfo(cmFont, c, new Point(0, 0), new Point(0, 0));
|
||||||
|
_glyphs.Add(gi);
|
||||||
|
RelAdvWidth = gi.RelAdvWidth;
|
||||||
|
RelAdvHeight = gi.RelAdvHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LatexText(CmFont cmFont, string text)
|
||||||
|
{
|
||||||
|
var x = 0d;
|
||||||
|
foreach (var c in text)
|
||||||
|
{
|
||||||
|
var gi = new GlyphInfo(cmFont, c, new Point(x, 0), new Point(0, 0));
|
||||||
|
_glyphs.Add(gi);
|
||||||
|
x += gi.RelAdvWidth;
|
||||||
|
}
|
||||||
|
RelAdvWidth = x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user