private static FontData GetFontData(Typeface typeface, double fontSize) { if (!typeface.Equals(cachedTypeface) || fontSize != cachedFontSize) { if (!typeface.TryGetGlyphTypeface(out GlyphTypeface glyphTypeface)) { defaultTypface.TryGetGlyphTypeface(out glyphTypeface); } bool charactersFound = GetTextBounds(glyphTypeface, fontSize, out double topDistance, out double bottomDistance); fontCache = new FontData(glyphTypeface, topDistance, bottomDistance, charactersFound); cachedTypeface = typeface; cachedFontSize = fontSize; } return(fontCache); }
public static double FontHeight(Typeface typeface, double fontSize, double dpiScale) { FontData fontData = GetFontData(typeface, fontSize); if (!fontData.HeightsCalculated) { return(MeasureText("A", typeface, fontSize, dpiScale).Height); } double topDistance = fontData.TopDistance; if (fontData.GlyphTypeface.Baseline * fontSize > fontData.TopDistance) { topDistance = fontData.GlyphTypeface.Baseline * fontSize * fontData.GlyphTypeface.Height; } return((Math.Ceiling(topDistance / dpiScale) * dpiScale) + (Math.Ceiling(fontData.BottomDistance / dpiScale) * dpiScale)); }
internal static GlyphRun CreateGlyphRun(string text, Typeface typeface, double fontSize, double dpiScale, double glyphRunStartPosition, out double runWidth) { if (text.Length == 0) { runWidth = 0; return(null); } FontData fontData = GetFontData(typeface, fontSize); ushort[] glyphIndexes = new ushort[text.Length]; double[] advanceWidths = new double[text.Length]; double totalWidth = 0; int codePoint; for (int n = 0; n < text.Length; n++) { // C# uses UTF16 encoded strings which for some characters requires 2 surrogate pair chars to encode // one character, if so we add a zero width space glyph after the real glyph to keep the number of // glyphs in the glyph run the same as the number of chars in the string to easier map mouse clicks // to whole characters in case a surrogate pair is used. if (char.IsHighSurrogate(text[n])) { codePoint = char.ConvertToUtf32(text, n); } else if (char.IsLowSurrogate(text[n])) { codePoint = '\u200B'; } else { codePoint = text[n]; } ushort glyphIndex = ReplaceGlyph(codePoint, fontData.GlyphTypeface, fontSize, dpiScale, glyphRunStartPosition + totalWidth, out double width); glyphIndexes[n] = glyphIndex; advanceWidths[n] = width; totalWidth += width; } double maxTopDistance = fontData.TopDistance; if (fontData.GlyphTypeface.Baseline * fontSize > maxTopDistance || !fontData.HeightsCalculated) { maxTopDistance = fontData.GlyphTypeface.Baseline * fontSize * fontData.GlyphTypeface.Height; } maxTopDistance = Math.Ceiling(maxTopDistance / dpiScale) * dpiScale; GlyphRun run = new GlyphRun( glyphTypeface: fontData.GlyphTypeface, bidiLevel: 0, isSideways: false, renderingEmSize: Math.Ceiling(fontSize / dpiScale) * dpiScale, pixelsPerDip: (float)dpiScale, glyphIndices: glyphIndexes, baselineOrigin: new Point(0, maxTopDistance), advanceWidths: advanceWidths, glyphOffsets: null, characters: null, deviceFontName: null, clusterMap: null, caretStops: null, language: null); runWidth = totalWidth; return(run); }