diff --git a/src/WpfMath/CharAtom.cs b/src/WpfMath/CharAtom.cs index c59563bd..b02a4265 100644 --- a/src/WpfMath/CharAtom.cs +++ b/src/WpfMath/CharAtom.cs @@ -15,6 +15,8 @@ public CharAtom(SourceSpan source, char character, string textStyle = null) // Null means default text style. public string TextStyle { get; } + private bool IsDefaultTextStyle => this.TextStyle == null; + protected override Box CreateBoxCore(TexEnvironment environment) { var font = GetStyledFont(environment); @@ -25,13 +27,15 @@ protected override Box CreateBoxCore(TexEnvironment environment) public override ITeXFont GetStyledFont(TexEnvironment environment) => TextStyle == TexUtilities.TextStyleName ? environment.TextFont : base.GetStyledFont(environment); - private CharInfo GetCharInfo(ITeXFont texFont, TexStyle style) - { - if (this.TextStyle == null) - return texFont.GetDefaultCharInfo(this.Character, style); - else - return texFont.GetCharInfo(this.Character, this.TextStyle, style); - } + public override bool IsSupportedByFont(ITeXFont font) => + this.IsDefaultTextStyle + ? font.SupportsDefaultCharacter(this.Character, TexStyle.Display) + : font.SupportsCharacter(this.Character, this.TextStyle, TexStyle.Display); + + private CharInfo GetCharInfo(ITeXFont texFont, TexStyle style) => + this.IsDefaultTextStyle + ? texFont.GetDefaultCharInfo(this.Character, style) + : texFont.GetCharInfo(this.Character, this.TextStyle, style); public override CharFont GetCharFont(ITeXFont texFont) { diff --git a/src/WpfMath/CharSymbol.cs b/src/WpfMath/CharSymbol.cs index bc14d6ef..a5ef76ea 100644 --- a/src/WpfMath/CharSymbol.cs +++ b/src/WpfMath/CharSymbol.cs @@ -14,6 +14,13 @@ protected CharSymbol(SourceSpan source, TexAtomType type = TexAtomType.Ordinary) /// Returns the preferred font to render this character. public virtual ITeXFont GetStyledFont(TexEnvironment environment) => environment.MathFont; + /// Checks if the symbol can be rendered by font. + public abstract bool IsSupportedByFont(ITeXFont font); + + /// + /// Returns the symbol rendered by font. Throws an exception if the symbol is not supported by font. Always + /// succeed if . + /// public abstract CharFont GetCharFont(ITeXFont texFont); } } diff --git a/src/WpfMath/DefaultTexFont.cs b/src/WpfMath/DefaultTexFont.cs index 720c5bff..5c8dffae 100644 --- a/src/WpfMath/DefaultTexFont.cs +++ b/src/WpfMath/DefaultTexFont.cs @@ -52,6 +52,14 @@ public DefaultTexFont(double size) public bool SupportsMetrics => true; + public bool SupportsSymbol(string name) => symbolMappings.ContainsKey(name); + + public bool SupportsDefaultCharacter(char character, TexStyle style) => + this.GetDefaultCharInfo(character, style, false) != null; + + public bool SupportsCharacter(char character, string textStyle, TexStyle style) => + this.GetCharInfo(character, textStyle, style, false) != null; + public double Size { get; @@ -76,8 +84,12 @@ public ExtensionChar GetExtension(CharInfo charInfo, TexStyle style) if (extension[i] == (int)TexCharKind.None) parts[i] = null; else - parts[i] = new CharInfo((char)extension[i], charInfo.Font, sizeFactor, charInfo.FontId, - GetMetrics(new CharFont((char)extension[i], charInfo.FontId), sizeFactor)); + parts[i] = new CharInfo( + (char)extension[i], + charInfo.Font, + sizeFactor, + charInfo.FontId, + GetMetrics(new CharFont((char)extension[i], charInfo.FontId), sizeFactor, true)); } return new ExtensionChar(parts[TexFontUtilities.ExtensionTop], parts[TexFontUtilities.ExtensionMiddle], @@ -98,21 +110,33 @@ public CharInfo GetNextLargerCharInfo(CharInfo charInfo, TexStyle style) var fontInfo = fontInfoList[charInfo.FontId]; var charFont = fontInfo.GetNextLarger(charInfo.Character); var newFontInfo = fontInfoList[charFont.FontId]; - return new CharInfo(charFont.Character, newFontInfo.Font, GetSizeFactor(style), charFont.FontId, - GetMetrics(charFont, GetSizeFactor(style))); + return new CharInfo( + charFont.Character, + newFontInfo.Font, + GetSizeFactor(style), + charFont.FontId, + GetMetrics(charFont, GetSizeFactor(style), true)); } - public CharInfo GetDefaultCharInfo(char character, TexStyle style) + private static string GetDefaultTextStyleMapping(char character) { - if (character >= '0' && character <= '9') - return GetCharInfo(character, defaultTextStyleMappings[(int)TexCharKind.Numbers], style); - else if (character >= 'a' && character <= 'z') - return GetCharInfo(character, defaultTextStyleMappings[(int)TexCharKind.Small], style); - else - return GetCharInfo(character, defaultTextStyleMappings[(int)TexCharKind.Capitals], style); + TexCharKind GetCharKind() + { + if (character >= '0' && character <= '9') + return TexCharKind.Numbers; + else if (character >= 'a' && character <= 'z') + return TexCharKind.Small; + else + return TexCharKind.Capitals; + } + + return defaultTextStyleMappings[(int)GetCharKind()]; } - private CharInfo GetCharInfo(char character, CharFont[] charFont, TexStyle style) + public CharInfo GetDefaultCharInfo(char character, TexStyle style, bool assert = true) => + this.GetCharInfo(character, GetDefaultTextStyleMapping(character), style, assert); + + private CharInfo GetCharInfo(char character, CharFont[] charFont, TexStyle style, bool assert) { TexCharKind charKind; int charIndexOffset; @@ -132,28 +156,47 @@ private CharInfo GetCharInfo(char character, CharFont[] charFont, TexStyle style charIndexOffset = character - 'A'; } - if (charFont[(int)charKind] == null) - return GetDefaultCharInfo(character, style); - else - return GetCharInfo(new CharFont((char)(charFont[(int)charKind].Character + charIndexOffset), - charFont[(int)charKind].FontId), style); + return charFont[(int)charKind] == null + ? this.GetDefaultCharInfo(character, style, assert) + : this.GetCharInfo( + new CharFont( + (char)(charFont[(int)charKind].Character + charIndexOffset), + charFont[(int)charKind].FontId), + style, + assert); } - public CharInfo GetCharInfo(char character, string textStyle, TexStyle style) => - textStyleMappings.TryGetValue(textStyle, out var mapping) - ? this.GetCharInfo(character, mapping, style) - : throw new TextStyleMappingNotFoundException(textStyle); + public CharInfo GetCharInfo(char character, string textStyle, TexStyle style, bool assert = true) + { + if (textStyleMappings.TryGetValue(textStyle, out var mapping)) + { + return this.GetCharInfo(character, mapping, style, assert); + } + + return assert ? throw new TextStyleMappingNotFoundException(textStyle) : (CharInfo)null; + } public CharInfo GetCharInfo(string symbolName, TexStyle style) => symbolMappings.TryGetValue(symbolName, out var mapping) ? this.GetCharInfo(mapping, style) : throw new SymbolMappingNotFoundException(symbolName); - public CharInfo GetCharInfo(CharFont charFont, TexStyle style) + public CharInfo GetCharInfo(CharFont charFont, TexStyle style, bool assert = true) { var size = GetSizeFactor(style); var fontInfo = fontInfoList[charFont.FontId]; - return new CharInfo(charFont.Character, fontInfo.Font, size, charFont.FontId, GetMetrics(charFont, size)); + var metrics = GetMetrics(charFont, size, assert); + if (metrics == null) + { + return null; + } + + return new CharInfo( + charFont.Character, + fontInfo.Font, + size, + charFont.FontId, + metrics); } public double GetKern(CharFont leftCharFont, CharFont rightCharFont, TexStyle style) @@ -312,12 +355,23 @@ public double GetDefaultLineThickness(TexStyle style) return GetParameter("defaultrulethickness") * GetSizeFactor(style) * TexFontUtilities.PixelsPerPoint; } - private TexFontMetrics GetMetrics(CharFont charFont, double size) + private static TexFontMetrics GetMetrics(CharFont charFont, double size, bool assert) { var fontInfo = fontInfoList[charFont.FontId]; var metrics = fontInfo.GetMetrics(charFont.Character); - return new TexFontMetrics(metrics[TexFontUtilities.MetricsWidth], metrics[TexFontUtilities.MetricsHeight], - metrics[TexFontUtilities.MetricsDepth], metrics[TexFontUtilities.MetricsItalic], + if (metrics == null) + { + if (assert) + throw new TexCharacterMappingNotFoundException( + $"Cannot determine metrics for '{charFont.Character}' character in font {charFont.FontId}"); + return null; + } + + return new TexFontMetrics( + metrics[TexFontUtilities.MetricsWidth], + metrics[TexFontUtilities.MetricsHeight], + metrics[TexFontUtilities.MetricsDepth], + metrics[TexFontUtilities.MetricsItalic], size * TexFontUtilities.PixelsPerPoint); } } diff --git a/src/WpfMath/DummyAtom.cs b/src/WpfMath/DummyAtom.cs index 65935b2d..c16f9a1b 100644 --- a/src/WpfMath/DummyAtom.cs +++ b/src/WpfMath/DummyAtom.cs @@ -38,11 +38,6 @@ public DummyAtom WithType(TexAtomType type) => public DummyAtom AsTextSymbol() => this.IsTextSymbol ? this : new DummyAtom(this.Type, this.Atom, true); - public bool IsCharSymbol - { - get { return this.Atom is CharSymbol; } - } - public bool IsKern { get { return this.Atom is SpaceAtom; } diff --git a/src/WpfMath/FixedCharAtom.cs b/src/WpfMath/FixedCharAtom.cs index 1eb8c7b0..3f4f1ed2 100644 --- a/src/WpfMath/FixedCharAtom.cs +++ b/src/WpfMath/FixedCharAtom.cs @@ -11,6 +11,10 @@ public FixedCharAtom(SourceSpan source, CharFont charFont) public CharFont CharFont { get; } + public override bool IsSupportedByFont(ITeXFont font) => + // Always "supported" because it ignores the font. + true; + public override CharFont GetCharFont(ITeXFont texFont) { return this.CharFont; diff --git a/src/WpfMath/ITeXFont.cs b/src/WpfMath/ITeXFont.cs index 6a06175a..f51d2403 100644 --- a/src/WpfMath/ITeXFont.cs +++ b/src/WpfMath/ITeXFont.cs @@ -6,6 +6,15 @@ internal interface ITeXFont /// Whether the font supports . bool SupportsMetrics { get; } + /// Whether the font supports the named symbol. + bool SupportsSymbol(string name); + + /// Whether the font supports the character with default size. + bool SupportsDefaultCharacter(char character, TexStyle style); + + /// Whether the font supports the character with the passed style. + bool SupportsCharacter(char character, string textStyle, TexStyle style); + double Size { get; } ITeXFont DeriveFont(double newSize); @@ -16,11 +25,11 @@ internal interface ITeXFont CharInfo GetNextLargerCharInfo(CharInfo charInfo, TexStyle style); - CharInfo GetDefaultCharInfo(char character, TexStyle style); + CharInfo GetDefaultCharInfo(char character, TexStyle style, bool assert = true); - CharInfo GetCharInfo(char character, string textStyle, TexStyle style); + CharInfo GetCharInfo(char character, string textStyle, TexStyle style, bool assert = true); - CharInfo GetCharInfo(CharFont charFont, TexStyle style); + CharInfo GetCharInfo(CharFont charFont, TexStyle style, bool assert = true); CharInfo GetCharInfo(string name, TexStyle style); diff --git a/src/WpfMath/RowAtom.cs b/src/WpfMath/RowAtom.cs index f5141bb2..3525ed77 100644 --- a/src/WpfMath/RowAtom.cs +++ b/src/WpfMath/RowAtom.cs @@ -122,17 +122,16 @@ protected override Box CreateBoxCore(TexEnvironment environment) // Check if atom is part of ligature or should be kerned. var kern = 0d; - if ((hasNextAtom && curAtom.GetRightType() == TexAtomType.Ordinary && curAtom.IsCharSymbol) && - !(this.Elements[i].GetType() == typeof(CharAtom) && ((CharAtom)this.Elements[i]).TextStyle == "text")) + if (hasNextAtom && curAtom.GetRightType() == TexAtomType.Ordinary && curAtom.Atom is CharSymbol cs) { - if (nextAtom is CharSymbol cs && ligatureKernChangeSet[(int)nextAtom.GetLeftType()]) + if (nextAtom is CharSymbol ns && ligatureKernChangeSet[(int)nextAtom.GetLeftType()]) { - var font = cs.GetStyledFont(environment); + var font = ns.GetStyledFont(environment); curAtom = curAtom.AsTextSymbol(); - if (font.SupportsMetrics) + if (font.SupportsMetrics && cs.IsSupportedByFont(font)) { var leftAtomCharFont = curAtom.GetCharFont(font); - var rightAtomCharFont = cs.GetCharFont(font); + var rightAtomCharFont = ns.GetCharFont(font); var ligatureCharFont = font.GetLigature(leftAtomCharFont, rightAtomCharFont); if (ligatureCharFont == null) { diff --git a/src/WpfMath/SymbolAtom.cs b/src/WpfMath/SymbolAtom.cs index d548b766..7ff04e39 100644 --- a/src/WpfMath/SymbolAtom.cs +++ b/src/WpfMath/SymbolAtom.cs @@ -82,6 +82,8 @@ public SymbolAtom(SourceSpan source, string name, TexAtomType type, bool isDelim protected override Box CreateBoxCore(TexEnvironment environment) => new CharBox(environment, environment.MathFont.GetCharInfo(this.Name, environment.Style)); + public override bool IsSupportedByFont(ITeXFont font) => font.SupportsSymbol(this.Name); + public override CharFont GetCharFont(ITeXFont texFont) { // Style is irrelevant here. diff --git a/src/WpfMath/SystemFont.cs b/src/WpfMath/SystemFont.cs index f14228fc..26c2729c 100644 --- a/src/WpfMath/SystemFont.cs +++ b/src/WpfMath/SystemFont.cs @@ -7,16 +7,24 @@ namespace WpfMath { internal class SystemFont : ITeXFont { - private readonly FontFamily _fontFamily; + private readonly FontFamily fontFamily; public SystemFont(double size, FontFamily fontFamily) { - _fontFamily = fontFamily; + this.fontFamily = fontFamily; Size = size; } public bool SupportsMetrics => false; + public bool SupportsSymbol(string name) => false; + + public bool SupportsDefaultCharacter(char character, TexStyle style) => + // System font implicitly supports any characters (so it seems). + true; + + public bool SupportsCharacter(char character, string textStyle, TexStyle style) => false; + public double Size { get; } public ITeXFont DeriveFont(double newSize) => throw MethodNotSupported(nameof(DeriveFont)); @@ -28,23 +36,26 @@ public ExtensionChar GetExtension(CharInfo charInfo, TexStyle style) => public CharInfo GetNextLargerCharInfo(CharInfo charInfo, TexStyle style) => throw MethodNotSupported(nameof(GetNextLargerCharInfo)); - public CharInfo GetDefaultCharInfo(char character, TexStyle style) => - throw MethodNotSupported(nameof(GetDefaultCharInfo)); + public CharInfo GetDefaultCharInfo(char character, TexStyle style, bool assert = true) => + assert ? throw MethodNotSupported(nameof(this.GetDefaultCharInfo)) : (CharInfo)null; - public CharInfo GetCharInfo(char character, string textStyle, TexStyle style) + public CharInfo GetCharInfo(char character, string textStyle, TexStyle style, bool assert = true) { - var typeface = GetTypeface(); + var typeface = this.GetTypeface(); if (!typeface.TryGetGlyphTypeface(out var glyphTypeface)) { - throw new TypeFaceNotFoundException($"Glyph typeface for font {_fontFamily.BaseUri} was not found"); + if (assert) + throw new TypeFaceNotFoundException( + $"Glyph typeface for font {this.fontFamily.BaseUri} was not found"); + return null; } var metrics = GetFontMetrics(character, typeface); return new CharInfo(character, glyphTypeface, 1.0, TexFontUtilities.NoFontId, metrics); } - public CharInfo GetCharInfo(CharFont charFont, TexStyle style) => - throw MethodNotSupported(nameof(GetCharInfo)); + public CharInfo GetCharInfo(CharFont charFont, TexStyle style, bool assert = true) => + assert ? throw MethodNotSupported(nameof(this.GetCharInfo)) : (CharInfo)null; public CharInfo GetCharInfo(string name, TexStyle style) => throw MethodNotSupported(nameof(GetCharInfo)); @@ -121,6 +132,6 @@ private TexFontMetrics GetFontMetrics(char c, Typeface typeface) return new TexFontMetrics(formattedText.Width, formattedText.Height, 0.0, formattedText.Width, 1.0); } - private Typeface GetTypeface() => new Typeface(_fontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal); // TODO[F]: Put into lazy field + private Typeface GetTypeface() => new Typeface(this.fontFamily, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal); // TODO[F]: Put into lazy field } } diff --git a/src/WpfMath/TexFontInfo.cs b/src/WpfMath/TexFontInfo.cs index ffe6a5bc..e62a9e17 100644 --- a/src/WpfMath/TexFontInfo.cs +++ b/src/WpfMath/TexFontInfo.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Windows.Media; -using WpfMath.Exceptions; namespace WpfMath { @@ -139,12 +138,12 @@ public double GetXHeight(double factor) return this.XHeight * factor; } + /// Return the character metrics or null if the metrics weren't found. public double[] GetMetrics(char character) { if (metrics.Length <= character || metrics[character] == null) { - throw new TexCharacterMappingNotFoundException( - $"Cannot determine metrics for '{character}' character in font {FontId}"); + return null; } return this.metrics[character];