From be8329fce662ce1c1529f262b02fab31a51f79b8 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 08:04:53 +0000 Subject: [PATCH 1/6] Add a managed TrueType reader for text on every platform (#412) First part of step 6 of #412. Text gets its glyphs from GDI today (GraphicsPath.AddString, gdi32 for widths and kerning), which does not exist outside Windows. The new internal class TrueTypeFont reads font files itself: - .ttf files and every font of a .ttc collection; CFF-based .otf and bitmap-only fonts are recognized and report HasTrueTypeOutlines=false - glyph contours from glyf/loca with composite glyphs resolved (offsets, scales, 2x2 transforms, point matching) - cmap formats 0, 4, 6 and 12, including symbol fonts - advance widths (hmtx), metrics (head, hhea, OS/2) and names (name) - kerning pairs of the version 0 kern table, as GDI reads them - GetPathData returns the outline in the layout of a GDI+ GraphicsPath after AddString, so GlyphShapeBuilder.PathDataToPath2D can take it unchanged; the quadratic segments become cubic ones by exact degree elevation Nothing uses it yet: the font service that replaces GDI in Text and FontCache follows in the next pull request. The tests compare names, metrics, glyph indices, advance widths, all contours of 112 characters and the kerning with reference data that fontTools wrote from the same font (Liberation Sans, SIL OFL 1.1, added under tests/CADability.Tests/Files/Fonts together with the script that wrote the reference). They also check that the outline is exactly the quadratic curves, a font collection, and invalid input. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 --- CADability/TrueTypeFont.cs | 648 ++++++++++++++++++ LICENSES/OFL-1.1.txt | 43 ++ REUSE.toml | 11 + .../LiberationSans-Regular.reference.json | 1 + .../Files/Fonts/LiberationSans-Regular.ttf | Bin 0 -> 410820 bytes .../Files/Fonts/make_reference.py | 30 + tests/CADability.Tests/TrueTypeFontTests.cs | 228 ++++++ 7 files changed, 961 insertions(+) create mode 100644 CADability/TrueTypeFont.cs create mode 100644 LICENSES/OFL-1.1.txt create mode 100644 tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.reference.json create mode 100644 tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.ttf create mode 100644 tests/CADability.Tests/Files/Fonts/make_reference.py create mode 100644 tests/CADability.Tests/TrueTypeFontTests.cs diff --git a/CADability/TrueTypeFont.cs b/CADability/TrueTypeFont.cs new file mode 100644 index 000000000..9ad4c2369 --- /dev/null +++ b/CADability/TrueTypeFont.cs @@ -0,0 +1,648 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Text; + +namespace CADability +{ + /// + /// A point of a TrueType glyph contour in font units. Off-curve points are the control points of quadratic Bezier + /// segments; between two consecutive off-curve points there is an implied on-curve point in the middle. + /// + internal struct GlyphPoint + { + public double X, Y; + public bool OnCurve; + public GlyphPoint(double x, double y, bool onCurve) + { + X = x; + Y = y; + OnCurve = onCurve; + } + } + + /// + /// Reads a TrueType font (.ttf, or one font of a .ttc collection) without any help of the operating system, so that text + /// works on every platform (issue #412). It reads the glyph outlines (exact quadratic curves from the "glyf" table, + /// composite glyphs included), the advance widths, the metrics, the kerning pairs of the "kern" table and the names. + /// OpenType fonts with CFF outlines (some .otf files) are recognized, but their outlines cannot be read + /// ( is false). + /// + internal sealed class TrueTypeFont + { + private readonly byte[] data; + private readonly Dictionary tables = new Dictionary(); + private readonly int numGlyphs; + private readonly int numberOfHMetrics; + private readonly bool longLoca; + private int cmapOffset = -1, cmapFormat, symbolOffset; + private Dictionary kerningPairs; // (left glyph << 16 | right glyph) -> value in font units, created on demand + + /// + /// The family name, e.g. "Liberation Sans", as GDI knows it (name ID 1, Windows platform preferred). + /// + public string FamilyName { get; } + /// + /// The typographic family name (name ID 16) when the font has one, otherwise . + /// + public string TypographicFamilyName { get; } + /// + /// The subfamily (style) name, e.g. "Regular" or "Bold Italic" (name ID 2). + /// + public string SubfamilyName { get; } + /// + /// The full name, e.g. "Liberation Sans Bold" (name ID 4). + /// + public string FullName { get; } + public bool IsBold { get; } + public bool IsItalic { get; } + public int UnitsPerEm { get; } + public int Ascender { get; } + public int Descender { get; } + public int LineGap { get; } + public int WinAscent { get; } + public int WinDescent { get; } + /// + /// False for an OpenType font with CFF outlines and for a bitmap font, whose glyphs cannot be read. + /// + public bool HasTrueTypeOutlines { get; } + public int GlyphCount => numGlyphs; + + /// + /// The ascent of the character cell in font units, as GDI and GDI+ use it for TrueType fonts (usWinAscent). + /// + public int CellAscent => WinAscent; + /// + /// The descent of the character cell in font units, positive downwards (usWinDescent). + /// + public int CellDescent => WinDescent; + /// + /// The distance between two lines in font units, computed like GDI does: the cell height plus the external leading, + /// which is the part of the line gap of the "hhea" table that is not already part of the cell. + /// + public int LineSpacing + { + get + { + int cell = WinAscent + WinDescent; + int external = Math.Max(0, LineGap - (cell - (Ascender - Descender))); + return cell + external; + } + } + + private TrueTypeFont(byte[] data, int offset) + { + this.data = data; + uint version = U32(offset); + bool trueType = version == 0x00010000 || version == 0x74727565; // 'true' (old Apple fonts) + if (!trueType && version != 0x4F54544F) throw new InvalidDataException("not a TrueType or OpenType font"); // 'OTTO' + int numTables = U16(offset + 4); + for (int i = 0; i < numTables; ++i) + { + int rec = offset + 12 + 16 * i; + string tag = Encoding.ASCII.GetString(data, rec, 4); + int tableOffset = (int)U32(rec + 8); + int length = (int)U32(rec + 12); + if (tableOffset < 0 || length < 0 || tableOffset + (long)length > data.Length) throw new InvalidDataException("table " + tag + " exceeds the file"); + tables[tag] = (tableOffset, length); + } + // a TrueType font without outlines is a bitmap font (e.g. color emoji) + HasTrueTypeOutlines = trueType && tables.ContainsKey("glyf") && tables.ContainsKey("loca"); + int head = Table("head"); + UnitsPerEm = U16(head + 18); + if (UnitsPerEm == 0) UnitsPerEm = 1000; + int macStyle = U16(head + 44); + longLoca = S16(head + 50) != 0; + numGlyphs = U16(Table("maxp") + 4); + int hhea = Table("hhea"); + Ascender = S16(hhea + 4); + Descender = S16(hhea + 6); + LineGap = S16(hhea + 8); + numberOfHMetrics = U16(hhea + 34); + bool bold = (macStyle & 1) != 0, italic = (macStyle & 2) != 0; + WinAscent = Ascender; + WinDescent = -Descender; + if (tables.TryGetValue("OS/2", out var os2) && os2.length >= 78) + { + int fsSelection = U16(os2.offset + 62); + italic = (fsSelection & 1) != 0; + bold = (fsSelection & 0x20) != 0; + WinAscent = U16(os2.offset + 74); + WinDescent = U16(os2.offset + 76); + } + IsBold = bold; + IsItalic = italic; + ReadNames(out string family, out string subfamily, out string full, out string typographicFamily); + FamilyName = family ?? ""; + SubfamilyName = subfamily ?? ""; + FullName = full ?? FamilyName; + TypographicFamilyName = typographicFamily ?? FamilyName; + SelectCmap(); + } + + /// + /// Reads all fonts of a font file: one for a .ttf or .otf file, all fonts of a .ttc or .otc collection. + /// + /// The content of the file + /// The fonts + public static TrueTypeFont[] Load(byte[] data) + { + if (data == null || data.Length < 12) throw new InvalidDataException("not a font file"); + if (data[0] == 't' && data[1] == 't' && data[2] == 'c' && data[3] == 'f') + { + int count = (int)ReadU32(data, 8); + if (count <= 0 || 12 + 4L * count > data.Length) throw new InvalidDataException("invalid font collection"); + TrueTypeFont[] res = new TrueTypeFont[count]; + for (int i = 0; i < count; ++i) res[i] = new TrueTypeFont(data, (int)ReadU32(data, 12 + 4 * i)); + return res; + } + return new TrueTypeFont[] { new TrueTypeFont(data, 0) }; + } + /// + /// Reads all fonts of the font file , see . + /// + public static TrueTypeFont[] Load(string fileName) + { + return Load(File.ReadAllBytes(fileName)); + } + + /// + /// Returns the glyph index of the character (a Unicode code point), 0 (the "missing + /// glyph") if the font has no glyph for it. + /// + public int GetGlyphIndex(int codePoint) + { + if (cmapOffset < 0) return 0; + int glyph = Lookup(codePoint); + if (glyph == 0 && symbolOffset != 0 && codePoint < 0x100) glyph = Lookup(codePoint + symbolOffset); + return glyph < numGlyphs ? glyph : 0; + } + /// + /// Returns the advance width of a glyph in font units. + /// + public int GetAdvanceWidth(int glyph) + { + int hmtx = Table("hmtx"); + if (numberOfHMetrics == 0) return 0; + int i = Math.Min(glyph, numberOfHMetrics - 1); + return U16(hmtx + 4 * i); + } + /// + /// Returns the kerning between two glyphs in font units from the "kern" table, 0 if there is none. Kerning that only + /// exists in the "GPOS" table is not used, which is what GDI does as well. + /// + public int GetKerning(int leftGlyph, int rightGlyph) + { + if (kerningPairs == null) kerningPairs = ReadKerning(); + kerningPairs.TryGetValue((leftGlyph << 16) | rightGlyph, out int res); + return res; + } + /// + /// All kerning pairs of the "kern" table: (left glyph, right glyph) to the value in font units. + /// + public IEnumerable> GetKerningPairs() + { + if (kerningPairs == null) kerningPairs = ReadKerning(); + foreach (KeyValuePair kv in kerningPairs) yield return new KeyValuePair<(int, int), int>((kv.Key >> 16, kv.Key & 0xFFFF), kv.Value); + } + + /// + /// Returns the contours of a glyph in font units (y upwards), with the components of a composite glyph resolved. + /// + public List GetContours(int glyph) + { + if (!HasTrueTypeOutlines) throw new NotSupportedException("The outlines of the font \"" + FullName + "\" cannot be read: they are in CFF format or the font has bitmaps only"); + List res = new List(); + AddContours(glyph, res, 0); + return res; + } + + /// + /// Returns the outline of a glyph as path data in the form a GDI+ GraphicsPath has after AddString with an em size + /// of 1 and StringFormat.GenericTypographic: in units of the em, y downwards, the top of the character cell at 0 + /// (the baseline at /). The quadratic segments are converted to cubic Bezier + /// segments without any loss (degree elevation), as GDI+ does. The point types are those of GDI+ PathPointType: + /// 0 start, 1 line, 3 Bezier, 0x80 closes the figure. This is the input of GlyphShapeBuilder.PathDataToPath2D. + /// + public void GetPathData(int glyph, out PointF[] points, out byte[] types) + { + List pts = new List(); + List tps = new List(); + double scale = 1.0 / UnitsPerEm; + PointF Map(double x, double y) => new PointF((float)(x * scale), (float)((CellAscent - y) * scale)); + foreach (GlyphPoint[] contour in GetContours(glyph)) + { + if (contour.Length < 2) continue; + int n = contour.Length; + // the start point: the first on-curve point, or the middle between the last and the first point + int first = -1; + for (int i = 0; i < n; ++i) if (contour[i].OnCurve) { first = i; break; } + double sx, sy; + if (first >= 0) + { + sx = contour[first].X; + sy = contour[first].Y; + } + else + { + first = n - 1; // all points are off-curve: start in the middle of the last and the first point + sx = (contour[n - 1].X + contour[0].X) / 2; + sy = (contour[n - 1].Y + contour[0].Y) / 2; + } + int startIndex = pts.Count; + pts.Add(Map(sx, sy)); + tps.Add(0); + double cx = sx, cy = sy; // current point + for (int k = 1; k <= n; ++k) + { + GlyphPoint p = contour[(first + k) % n]; + if (p.OnCurve) + { + if (k == n) break; // the closing line is implied by the closing flag + if (p.X == cx && p.Y == cy) continue; + pts.Add(Map(p.X, p.Y)); + tps.Add(1); + cx = p.X; + cy = p.Y; + } + else + { + GlyphPoint next = contour[(first + k + 1) % n]; + double ex, ey; + if (k == n) + { // the start point itself was implied (all points off-curve), the last curve ends there + ex = sx; + ey = sy; + } + else if (next.OnCurve) + { + ex = next.X; + ey = next.Y; + ++k; + } + else + { + ex = (p.X + next.X) / 2; + ey = (p.Y + next.Y) / 2; + } + if (!(p.X == cx && p.Y == cy && ex == cx && ey == cy)) + { + // degree elevation of the quadratic segment (c, p, e) to a cubic one + pts.Add(Map(cx + 2.0 / 3.0 * (p.X - cx), cy + 2.0 / 3.0 * (p.Y - cy))); + pts.Add(Map(ex + 2.0 / 3.0 * (p.X - ex), ey + 2.0 / 3.0 * (p.Y - ey))); + pts.Add(Map(ex, ey)); + tps.Add(3); + tps.Add(3); + tps.Add(3); + } + cx = ex; + cy = ey; + } + } + if (pts.Count - startIndex < 2) + { // nothing but the start point + pts.RemoveAt(startIndex); + tps.RemoveAt(startIndex); + continue; + } + tps[tps.Count - 1] |= 0x80; + } + points = pts.ToArray(); + types = tps.ToArray(); + } + + private void AddContours(int glyph, List res, int depth) + { + if (glyph < 0 || glyph >= numGlyphs || depth > 8) return; // a deeper nesting is a broken (or malicious) font + int loca = Table("loca"); + int start, end; + if (longLoca) + { + start = (int)U32(loca + 4 * glyph); + end = (int)U32(loca + 4 * glyph + 4); + } + else + { + start = 2 * U16(loca + 2 * glyph); + end = 2 * U16(loca + 2 * glyph + 2); + } + if (end <= start) return; // an empty glyph, e.g. the space + var glyf = tables["glyf"]; + if (end > glyf.length) throw new InvalidDataException("glyph " + glyph + " exceeds the glyf table"); + int g = glyf.offset + start; + int numberOfContours = S16(g); + if (numberOfContours >= 0) ReadSimpleGlyph(g, numberOfContours, res); + else ReadCompositeGlyph(g, res, depth); + } + private void ReadSimpleGlyph(int g, int numberOfContours, List res) + { + int pos = g + 10; + int[] endPts = new int[numberOfContours]; + for (int i = 0; i < numberOfContours; ++i) endPts[i] = U16(pos + 2 * i); + pos += 2 * numberOfContours; + int numPoints = numberOfContours == 0 ? 0 : endPts[numberOfContours - 1] + 1; + int instructionLength = U16(pos); + pos += 2 + instructionLength; + byte[] flags = new byte[numPoints]; + for (int i = 0; i < numPoints;) + { + byte f = data[pos++]; + flags[i++] = f; + if ((f & 8) != 0) + { + int repeat = data[pos++]; + for (int r = 0; r < repeat && i < numPoints; ++r) flags[i++] = f; + } + } + int[] xs = new int[numPoints], ys = new int[numPoints]; + int v = 0; + for (int i = 0; i < numPoints; ++i) + { + byte f = flags[i]; + if ((f & 2) != 0) v += (f & 0x10) != 0 ? data[pos++] : -data[pos++]; + else if ((f & 0x10) == 0) { v += S16(pos); pos += 2; } + xs[i] = v; + } + v = 0; + for (int i = 0; i < numPoints; ++i) + { + byte f = flags[i]; + if ((f & 4) != 0) v += (f & 0x20) != 0 ? data[pos++] : -data[pos++]; + else if ((f & 0x20) == 0) { v += S16(pos); pos += 2; } + ys[i] = v; + } + int startPoint = 0; + for (int c = 0; c < numberOfContours; ++c) + { + int count = endPts[c] - startPoint + 1; + if (count <= 0 || endPts[c] >= numPoints) throw new InvalidDataException("invalid contour"); + GlyphPoint[] contour = new GlyphPoint[count]; + for (int i = 0; i < count; ++i) contour[i] = new GlyphPoint(xs[startPoint + i], ys[startPoint + i], (flags[startPoint + i] & 1) != 0); + res.Add(contour); + startPoint = endPts[c] + 1; + } + } + private void ReadCompositeGlyph(int g, List res, int depth) + { + int pos = g + 10; + int flags; + do + { + flags = U16(pos); + int component = U16(pos + 2); + pos += 4; + int arg1, arg2; + if ((flags & 1) != 0) // ARG_1_AND_2_ARE_WORDS + { + arg1 = S16(pos); + arg2 = S16(pos + 2); + pos += 4; + } + else if ((flags & 2) != 0) + { // offsets are signed + arg1 = (sbyte)data[pos]; + arg2 = (sbyte)data[pos + 1]; + pos += 2; + } + else + { // point numbers are unsigned + arg1 = data[pos]; + arg2 = data[pos + 1]; + pos += 2; + } + double a = 1, b = 0, c = 0, d = 1; + if ((flags & 0x8) != 0) // WE_HAVE_A_SCALE + { + a = d = F2Dot14(pos); + pos += 2; + } + else if ((flags & 0x40) != 0) // WE_HAVE_AN_X_AND_Y_SCALE + { + a = F2Dot14(pos); + d = F2Dot14(pos + 2); + pos += 4; + } + else if ((flags & 0x80) != 0) // WE_HAVE_A_TWO_BY_TWO + { + a = F2Dot14(pos); + b = F2Dot14(pos + 2); + c = F2Dot14(pos + 4); + d = F2Dot14(pos + 6); + pos += 8; + } + List parts = new List(); + AddContours(component, parts, depth + 1); + double dx = 0, dy = 0; + if ((flags & 2) != 0) // ARGS_ARE_XY_VALUES + { + dx = arg1; + dy = arg2; + if ((flags & 0x800) != 0) // SCALED_COMPONENT_OFFSET: the offset is transformed as well + { + double tx = a * dx + c * dy, ty = b * dx + d * dy; + dx = tx; + dy = ty; + } + } + else + { // the component is placed by matching a point of the glyph so far (arg1) with a point of the component (arg2) + GlyphPoint? p1 = PointByIndex(res, arg1), p2 = PointByIndex(parts, arg2); + if (p1.HasValue && p2.HasValue) + { + dx = p1.Value.X - (a * p2.Value.X + c * p2.Value.Y); + dy = p1.Value.Y - (b * p2.Value.X + d * p2.Value.Y); + } + } + foreach (GlyphPoint[] part in parts) + { + GlyphPoint[] moved = new GlyphPoint[part.Length]; + for (int i = 0; i < part.Length; ++i) + { + double x = part[i].X, y = part[i].Y; + moved[i] = new GlyphPoint(a * x + c * y + dx, b * x + d * y + dy, part[i].OnCurve); + } + res.Add(moved); + } + } while ((flags & 0x20) != 0); // MORE_COMPONENTS + } + private static GlyphPoint? PointByIndex(List contours, int index) + { + foreach (GlyphPoint[] contour in contours) + { + if (index < contour.Length) return contour[index]; + index -= contour.Length; + } + return null; + } + + private void SelectCmap() + { + if (!tables.TryGetValue("cmap", out var cmap)) return; + int numTables = U16(cmap.offset + 2); + int best = -1, bestRank = int.MaxValue; + for (int i = 0; i < numTables; ++i) + { + int rec = cmap.offset + 4 + 8 * i; + int platform = U16(rec), encoding = U16(rec + 2); + int sub = cmap.offset + (int)U32(rec + 4); + if (sub + 2 > cmap.offset + cmap.length) continue; + int format = U16(sub); + if (format != 4 && format != 12 && format != 6 && format != 0) continue; + int rank; + if (platform == 3 && encoding == 10 && format == 12) rank = 0; + else if (platform == 0 && format == 12) rank = 1; + else if (platform == 3 && encoding == 1) rank = 2; + else if (platform == 0) rank = 3; + else if (platform == 3 && encoding == 0) rank = 4; // symbol font + else if (platform == 1 && encoding == 0) rank = 5; // Macintosh Roman, only right for ASCII + else continue; + if (rank < bestRank) + { + bestRank = rank; + best = sub; + } + } + if (best < 0) return; + cmapOffset = best; + cmapFormat = U16(best); + if (bestRank == 4) symbolOffset = 0xF000; // symbol fonts map their characters to U+F000 to U+F0FF + } + private int Lookup(int c) + { + int t = cmapOffset; + switch (cmapFormat) + { + case 0: + return c < 256 ? data[t + 6 + c] : 0; + case 6: + { + int firstCode = U16(t + 6), entryCount = U16(t + 8); + int i = c - firstCode; + return i >= 0 && i < entryCount ? U16(t + 10 + 2 * i) : 0; + } + case 4: + { + if (c > 0xFFFF) return 0; + int segCountX2 = U16(t + 6); + int endCodes = t + 14, startCodes = endCodes + segCountX2 + 2, idDeltas = startCodes + segCountX2, idRangeOffsets = idDeltas + segCountX2; + int lo = 0, hi = segCountX2 / 2 - 1; + while (lo <= hi) + { // the segments are sorted by their end code + int mid = (lo + hi) / 2; + if (U16(endCodes + 2 * mid) < c) lo = mid + 1; + else hi = mid - 1; + } + if (lo >= segCountX2 / 2) return 0; + int start = U16(startCodes + 2 * lo); + if (c < start) return 0; + int delta = S16(idDeltas + 2 * lo); + int rangeOffsetPos = idRangeOffsets + 2 * lo; + int rangeOffset = U16(rangeOffsetPos); + if (rangeOffset == 0) return (c + delta) & 0xFFFF; + int glyphPos = rangeOffsetPos + rangeOffset + 2 * (c - start); + int glyph = U16(glyphPos); + return glyph == 0 ? 0 : (glyph + delta) & 0xFFFF; + } + case 12: + { + int numGroups = (int)U32(t + 12); + int lo = 0, hi = numGroups - 1; + while (lo <= hi) + { + int mid = (lo + hi) / 2; + int grp = t + 16 + 12 * mid; + uint startChar = U32(grp), endChar = U32(grp + 4); + if (c < startChar) hi = mid - 1; + else if (c > endChar) lo = mid + 1; + else return (int)(U32(grp + 8) + (c - startChar)); + } + return 0; + } + } + return 0; + } + + private Dictionary ReadKerning() + { + Dictionary res = new Dictionary(); + if (!tables.TryGetValue("kern", out var kern) || kern.length < 4) return res; + if (U16(kern.offset) != 0) return res; // only the version 0 (Windows) table, as GDI reads it + int nTables = U16(kern.offset + 2); + int pos = kern.offset + 4; + for (int i = 0; i < nTables && pos + 6 <= kern.offset + kern.length; ++i) + { + int length = U16(pos + 2); + int coverage = U16(pos + 4); + int format = coverage >> 8; + bool horizontal = (coverage & 1) != 0, minimum = (coverage & 2) != 0, crossStream = (coverage & 4) != 0; + if (format == 0 && horizontal && !minimum && !crossStream) + { + int nPairs = U16(pos + 6); + int p = pos + 14; + for (int k = 0; k < nPairs && p + 6 <= kern.offset + kern.length; ++k, p += 6) + { + int key = (U16(p) << 16) | U16(p + 2); + res.TryGetValue(key, out int value); + res[key] = value + S16(p + 4); + } + } + if (length == 0) break; // some fonts have a wrong length in a single subtable + pos += length; + } + return res; + } + + private void ReadNames(out string family, out string subfamily, out string full, out string typographicFamily) + { + family = subfamily = full = typographicFamily = null; + if (!tables.TryGetValue("name", out var name)) return; + int count = U16(name.offset + 2); + int strings = name.offset + U16(name.offset + 4); + // per name ID: the rank of the record found so far (Windows US English is best) + Dictionary found = new Dictionary(); + for (int i = 0; i < count; ++i) + { + int rec = name.offset + 6 + 12 * i; + int platform = U16(rec), encoding = U16(rec + 2), language = U16(rec + 4), nameId = U16(rec + 6); + if (nameId != 1 && nameId != 2 && nameId != 4 && nameId != 16) continue; + int length = U16(rec + 8), offset = strings + U16(rec + 10); + if (offset + length > data.Length) continue; + int rank; + string value; + if (platform == 3 && (encoding == 1 || encoding == 0 || encoding == 10)) + { + rank = language == 0x409 ? 0 : 1; + value = Encoding.BigEndianUnicode.GetString(data, offset, length); + } + else if (platform == 0) + { + rank = 2; + value = Encoding.BigEndianUnicode.GetString(data, offset, length); + } + else if (platform == 1 && encoding == 0) + { + rank = 3; + value = Encoding.ASCII.GetString(data, offset, length); + } + else continue; + if (!found.TryGetValue(nameId, out var old) || rank < old.rank) found[nameId] = (rank, value); + } + if (found.TryGetValue(1, out var f)) family = f.value; + if (found.TryGetValue(2, out var s)) subfamily = s.value; + if (found.TryGetValue(4, out var u)) full = u.value; + if (found.TryGetValue(16, out var t)) typographicFamily = t.value; + } + + private int Table(string tag) + { + if (!tables.TryGetValue(tag, out var t)) throw new InvalidDataException("the font has no " + tag + " table"); + return t.offset; + } + private int U16(int pos) => (data[pos] << 8) | data[pos + 1]; + private short S16(int pos) => (short)((data[pos] << 8) | data[pos + 1]); + private uint U32(int pos) => ReadU32(data, pos); + private double F2Dot14(int pos) => S16(pos) / 16384.0; + private static uint ReadU32(byte[] d, int pos) => (uint)((d[pos] << 24) | (d[pos + 1] << 16) | (d[pos + 2] << 8) | d[pos + 3]); + } +} diff --git a/LICENSES/OFL-1.1.txt b/LICENSES/OFL-1.1.txt new file mode 100644 index 000000000..6fe84ee21 --- /dev/null +++ b/LICENSES/OFL-1.1.txt @@ -0,0 +1,43 @@ +SIL OPEN FONT LICENSE + +Version 1.1 - 26 February 2007 + +PREAMBLE + +The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others. + +The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives. + +DEFINITIONS + +"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the copyright statement(s). + +"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, or substituting — in part or in whole — any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment. + +"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS + +Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission. + +5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software. + +TERMINATION + +This license becomes null and void if any of the above conditions are not met. + +DISCLAIMER + +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/REUSE.toml b/REUSE.toml index 6ffece05f..d8db5bac2 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -29,3 +29,14 @@ path = [ "CADability/Strings.cs", "CADability/Util.cs", ] + +[[annotations]] +path = [ + "tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.ttf", + "tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.reference.json", +] +SPDX-FileCopyrightText = [ + "2010 Google Corporation with Reserved Font Arimo, Tinos and Cousine", + "2012 Red Hat, Inc. with Reserved Font Name Liberation", +] +SPDX-License-Identifier = "OFL-1.1" diff --git a/tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.reference.json b/tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.reference.json new file mode 100644 index 000000000..a424a5dfe --- /dev/null +++ b/tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.reference.json @@ -0,0 +1 @@ +{"family":"Liberation Sans","subfamily":"Regular","full":"Liberation Sans","unitsPerEm":2048,"ascender":1854,"descender":-434,"lineGap":67,"winAscent":1854,"winDescent":434,"glyphCount":2620,"kerningPairCount":908,"kerning":[{"left":"A","right":"V","value":-152},{"left":"T","right":"o","value":-227},{"left":"L","right":"T","value":-152},{"left":"A","right":"T","value":-152},{"left":"Y","right":"a","value":-152},{"left":"F","right":".","value":-227}],"glyphs":[{"char":" ","glyph":3,"advance":569,"composite":false,"contours":[]},{"char":"!","glyph":4,"advance":569,"composite":false,"contours":[[[359,397,1],[211,397,1],[187,1409,1],[383,1409,1]],[[185,0,1],[185,201,1],[379,201,1],[379,0,1]]]},{"char":"\"","glyph":5,"advance":727,"composite":false,"contours":[[[618,966,1],[476,966,1],[456,1409,1],[640,1409,1]],[[249,966,1],[108,966,1],[87,1409,1],[271,1409,1]]]},{"char":"#","glyph":6,"advance":1139,"composite":false,"contours":[[[896,885,1],[818,516,1],[1078,516,1],[1078,408,1],[795,408,1],[707,0,1],[597,0,1],[683,408,1],[320,408,1],[236,0,1],[126,0,1],[210,408,1],[9,408,1],[9,516,1],[234,516,1],[312,885,1],[60,885,1],[60,993,1],[334,993,1],[423,1401,1],[533,1401,1],[445,993,1],[808,993,1],[896,1401,1],[1006,1401,1],[918,993,1],[1129,993,1],[1129,885,1]],[[425,885,1],[345,516,1],[707,516,1],[785,885,1]]]},{"char":"$","glyph":7,"advance":1139,"composite":false,"contours":[[[518,20,1],[92,38,0],[22,379,1],[192,416,1],[217,297,0],[376,177,0],[518,168,1],[518,664,1],[341,708,0],[206,778,0],[123,869,0],[86,973,0],[86,1046,1],[86,1201,0],[311,1376,0],[518,1385,1],[518,1516,1],[642,1516,1],[642,1385,1],[829,1376,0],[1033,1225,0],[1075,1065,1],[901,1032,1],[881,1126,0],[759,1231,0],[642,1242,1],[642,797,1],[821,755,0],[972,685,0],[1060,597,0],[1106,477,0],[1106,396,1],[1106,231,0],[864,31,0],[642,20,1],[642,-142,1],[518,-142,1]],[[934,394,1],[934,459,0],[882,543,0],[784,598,0],[642,635,1],[642,167,1],[783,176,0],[934,292,0]],[[258,1048,1],[258,989,0],[308,907,0],[407,851,0],[518,823,1],[518,1244,1],[258,1230,0]]]},{"char":"%","glyph":8,"advance":1821,"composite":false,"contours":[[[1748,434,1],[1748,219,0],[1586,-12,0],[1428,-12,1],[1272,-12,0],[1113,213,0],[1113,434,1],[1113,662,0],[1266,885,0],[1432,885,1],[1596,885,0],[1748,656,0]],[[527,0,1],[372,0,1],[1294,1409,1],[1451,1409,1]],[[394,1421,1],[553,1421,0],[707,1197,0],[707,975,1],[707,758,0],[548,524,0],[390,524,1],[232,524,0],[73,756,0],[73,975,1],[73,1198,0],[227,1421,0]],[[1600,434,1],[1600,613,0],[1523,774,0],[1432,774,1],[1341,774,0],[1260,616,0],[1260,434,1],[1260,263,0],[1339,98,0],[1430,98,1],[1518,98,0],[1600,265,0]],[[560,975,1],[560,1151,0],[484,1313,0],[394,1313,1],[300,1313,0],[220,1154,0],[220,975,1],[220,802,0],[300,637,0],[392,637,1],[479,637,0],[560,805,0]]]},{"char":"&","glyph":9,"advance":1366,"composite":false,"contours":[[[1193,-12,1],[1016,-12,0],[895,115,1],[820,50,0],[628,-20,0],[523,-20,1],[308,-20,0],[72,187,0],[72,371,1],[72,649,0],[415,800,1],[382,862,0],[334,1032,0],[334,1102,1],[334,1252,0],[517,1417,0],[685,1417,1],[836,1417,0],[1021,1265,0],[1021,1133,1],[1021,1015,0],[838,831,0],[612,741,1],[723,536,0],[905,329,1],[1018,495,0],[1076,739,1],[1221,696,1],[1158,447,0],[1009,227,1],[1105,129,0],[1217,129,1],[1288,129,0],[1334,145,1],[1334,10,1],[1278,-12,0]],[[869,1133,1],[869,1205,0],[769,1296,0],[683,1296,1],[587,1296,0],[487,1193,0],[487,1102,1],[487,988,0],[552,858,1],[683,911,0],[806,989,0],[869,1079,0]],[[795,217,1],[597,451,0],[476,674,1],[240,574,0],[240,373,1],[240,252,0],[395,111,0],[529,111,1],[600,111,0],[742,166,0]]]},{"char":"'","glyph":10,"advance":391,"composite":false,"contours":[[[266,966,1],[125,966,1],[104,1409,1],[288,1409,1]]]},{"char":"(","glyph":11,"advance":682,"composite":false,"contours":[[[127,532,1],[127,821,0],[308,1281,0],[496,1484,1],[670,1484,1],[483,1276,0],[308,808,0],[308,530,1],[308,253,0],[481,-213,0],[670,-424,1],[496,-424,1],[307,-220,0],[127,241,0],[127,528,1]]]},{"char":")","glyph":12,"advance":682,"composite":false,"contours":[[[555,528,1],[555,239,0],[374,-221,0],[186,-424,1],[12,-424,1],[200,-214,0],[374,251,0],[374,530,1],[374,809,0],[199,1275,0],[12,1484,1],[186,1484,1],[375,1280,0],[555,819,0],[555,532,1]]]},{"char":"*","glyph":13,"advance":797,"composite":false,"contours":[[[456,1114,1],[720,1217,1],[765,1085,1],[483,1012,1],[668,762,1],[549,690,1],[399,948,1],[243,692,1],[124,764,1],[313,1012,1],[33,1085,1],[78,1219,1],[345,1112,1],[333,1409,1],[469,1409,1]]]},{"char":"+","glyph":14,"advance":1196,"composite":false,"contours":[[[671,608,1],[671,180,1],[524,180,1],[524,608,1],[100,608,1],[100,754,1],[524,754,1],[524,1182,1],[671,1182,1],[671,754,1],[1095,754,1],[1095,608,1]]]},{"char":",","glyph":15,"advance":569,"composite":false,"contours":[[[385,219,1],[385,51,1],[385,-55,0],[347,-197,0],[307,-262,1],[184,-262,1],[278,-126,0],[278,0,1],[190,0,1],[190,219,1]]]},{"char":"-","glyph":16,"advance":682,"composite":false,"contours":[[[91,464,1],[91,624,1],[591,624,1],[591,464,1]]]},{"char":".","glyph":17,"advance":569,"composite":false,"contours":[[[187,0,1],[187,219,1],[382,219,1],[382,0,1]]]},{"char":"/","glyph":18,"advance":569,"composite":false,"contours":[[[0,-20,1],[411,1484,1],[569,1484,1],[162,-20,1]]]},{"char":"0","glyph":19,"advance":1139,"composite":false,"contours":[[[1059,705,1],[1059,352,0],[810,-20,0],[567,-20,1],[324,-20,0],[80,350,0],[80,705,1],[80,1068,0],[317,1430,0],[573,1430,1],[822,1430,0],[1059,1064,0]],[[876,705,1],[876,1010,0],[735,1284,0],[573,1284,1],[407,1284,0],[262,1014,0],[262,705,1],[262,405,0],[409,127,0],[569,127,1],[728,127,0],[876,411,0]]]},{"char":"1","glyph":20,"advance":1139,"composite":false,"contours":[[[156,0,1],[156,153,1],[515,153,1],[515,1237,1],[197,1010,1],[197,1180,1],[530,1409,1],[696,1409,1],[696,153,1],[1039,153,1],[1039,0,1]]]},{"char":"2","glyph":21,"advance":1139,"composite":false,"contours":[[[103,0,1],[103,127,1],[154,244,0],[301,423,0],[463,568,0],[622,692,0],[750,816,0],[829,952,0],[829,1038,1],[829,1154,0],[693,1282,0],[572,1282,1],[457,1282,0],[308,1157,0],[295,1044,1],[111,1061,1],[131,1230,0],[378,1430,0],[572,1430,1],[785,1430,0],[1014,1229,0],[1014,1044,1],[1014,962,0],[939,800,0],[791,638,0],[582,468,1],[467,374,0],[331,223,0],[301,153,1],[1036,153,1],[1036,0,1]]]},{"char":"3","glyph":22,"advance":1139,"composite":false,"contours":[[[1049,389,1],[1049,194,0],[801,-20,0],[571,-20,1],[357,-20,0],[102,173,0],[78,362,1],[264,379,1],[300,129,0],[571,129,1],[707,129,0],[862,263,0],[862,395,1],[862,510,0],[685,639,0],[518,639,1],[416,639,1],[416,795,1],[514,795,1],[662,795,0],[825,924,0],[825,1038,1],[825,1151,0],[692,1282,0],[561,1282,1],[442,1282,0],[295,1160,0],[283,1049,1],[102,1063,1],[122,1236,0],[369,1430,0],[563,1430,1],[775,1430,0],[1010,1233,0],[1010,1057,1],[1010,922,0],[859,753,0],[715,723,1],[715,719,1],[873,702,0],[1049,524,0]]]},{"char":"4","glyph":23,"advance":1139,"composite":false,"contours":[[[881,319,1],[881,0,1],[711,0,1],[711,319,1],[47,319,1],[47,459,1],[692,1409,1],[881,1409,1],[881,461,1],[1079,461,1],[1079,319,1]],[[711,1206,1],[709,1200,0],[657,1106,0],[644,1087,1],[283,555,1],[229,481,1],[213,461,1],[711,461,1]]]},{"char":"5","glyph":24,"advance":1139,"composite":false,"contours":[[[1053,459,1],[1053,236,0],[788,-20,0],[553,-20,1],[356,-20,0],[114,152,0],[82,315,1],[264,336,1],[321,127,0],[557,127,1],[702,127,0],[866,302,0],[866,455,1],[866,588,0],[701,752,0],[561,752,1],[488,752,0],[362,706,0],[299,651,1],[123,651,1],[170,1409,1],[971,1409,1],[971,1256,1],[334,1256,1],[307,809,1],[424,899,0],[598,899,1],[806,899,0],[1053,655,0]]]},{"char":"6","glyph":25,"advance":1139,"composite":false,"contours":[[[1049,461,1],[1049,238,0],[807,-20,0],[594,-20,1],[356,-20,0],[104,334,0],[104,672,1],[104,1038,0],[366,1430,0],[608,1430,1],[927,1430,0],[1010,1143,1],[838,1112,1],[785,1284,0],[606,1284,1],[452,1284,0],[283,997,0],[283,725,1],[332,816,0],[510,911,0],[625,911,1],[820,911,0],[1049,667,0]],[[866,453,1],[866,606,0],[716,772,0],[582,772,1],[456,772,0],[301,625,0],[301,496,1],[301,333,0],[462,125,0],[588,125,1],[718,125,0],[866,300,0]]]},{"char":"7","glyph":26,"advance":1139,"composite":false,"contours":[[[1036,1263,1],[820,933,0],[642,559,0],[553,195,0],[553,0,1],[365,0,1],[365,270,0],[594,867,0],[862,1256,1],[105,1256,1],[105,1409,1],[1036,1409,1]]]},{"char":"8","glyph":27,"advance":1139,"composite":false,"contours":[[[1050,393,1],[1050,198,0],[802,-20,0],[570,-20,1],[344,-20,0],[89,194,0],[89,391,1],[89,529,0],[247,717,0],[370,737,1],[370,741,1],[255,768,0],[122,948,0],[122,1069,1],[122,1230,0],[363,1430,0],[566,1430,1],[774,1430,0],[1015,1234,0],[1015,1067,1],[1015,946,0],[881,766,0],[765,743,1],[765,739,1],[900,717,0],[1050,532,0]],[[828,1057,1],[828,1296,0],[566,1296,1],[439,1296,0],[306,1176,0],[306,1057,1],[306,936,0],[443,809,0],[568,809,1],[695,809,0],[828,926,0]],[[863,410,1],[863,541,0],[707,674,0],[566,674,1],[429,674,0],[275,531,0],[275,406,1],[275,115,0],[572,115,1],[719,115,0],[863,256,0]]]},{"char":"9","glyph":28,"advance":1139,"composite":false,"contours":[[[1042,733,1],[1042,370,0],[777,-20,0],[532,-20,1],[367,-20,0],[168,119,0],[125,274,1],[297,301,1],[351,125,0],[535,125,1],[690,125,0],[860,413,0],[864,680,1],[824,590,0],[630,481,0],[514,481,1],[324,481,0],[96,741,0],[96,956,1],[96,1177,0],[344,1430,0],[565,1430,1],[800,1430,0],[1042,1082,0]],[[846,907,1],[846,1077,0],[690,1284,0],[559,1284,1],[429,1284,0],[279,1107,0],[279,956,1],[279,802,0],[429,623,0],[557,623,1],[635,623,0],[769,694,0],[846,824,0]]]},{"char":":","glyph":29,"advance":569,"composite":false,"contours":[[[187,875,1],[187,1082,1],[382,1082,1],[382,875,1]],[[187,0,1],[187,207,1],[382,207,1],[382,0,1]]]},{"char":";","glyph":30,"advance":569,"composite":false,"contours":[[[385,207,1],[385,51,1],[385,-55,0],[347,-197,0],[307,-262,1],[184,-262,1],[278,-126,0],[278,0,1],[190,0,1],[190,207,1]],[[190,875,1],[190,1082,1],[385,1082,1],[385,875,1]]]},{"char":"<","glyph":31,"advance":1196,"composite":false,"contours":[[[101,571,1],[101,776,1],[1096,1194,1],[1096,1040,1],[238,674,1],[1096,307,1],[1096,154,1]]]},{"char":"=","glyph":32,"advance":1196,"composite":false,"contours":[[[100,856,1],[100,1004,1],[1095,1004,1],[1095,856,1]],[[100,344,1],[100,492,1],[1095,492,1],[1095,344,1]]]},{"char":">","glyph":33,"advance":1196,"composite":false,"contours":[[[101,154,1],[101,307,1],[959,674,1],[101,1040,1],[101,1194,1],[1096,776,1],[1096,571,1]]]},{"char":"?","glyph":34,"advance":1139,"composite":false,"contours":[[[1063,1032,1],[1063,957,0],[1019,839,0],[937,739,0],[844,671,1],[764,612,1],[692,560,0],[622,445,0],[621,377,1],[446,377,1],[448,446,0],[487,550,0],[549,630,0],[627,693,0],[707,750,0],[784,807,0],[844,877,0],[881,965,0],[881,1024,1],[881,1138,0],[726,1270,0],[586,1270,1],[446,1270,0],[282,1130,0],[268,1008,1],[84,1020,1],[110,1218,0],[370,1430,0],[584,1430,1],[807,1430,0],[1063,1219,0]],[[438,0,1],[438,201,1],[633,201,1],[633,0,1]]]},{"char":"@","glyph":35,"advance":2079,"composite":false,"contours":[[[1902,755,1],[1902,569,0],[1787,268,0],[1582,104,0],[1455,104,1],[1356,104,0],[1248,192,0],[1248,280,1],[1251,350,1],[1245,350,1],[1179,227,0],[984,104,0],[871,104,1],[714,104,0],[541,308,0],[541,489,1],[541,653,0],[670,935,0],[902,1101,0],[1043,1101,1],[1262,1101,0],[1344,919,1],[1350,919,1],[1389,1079,1],[1545,1079,1],[1429,573,1],[1392,409,0],[1392,320,1],[1392,226,0],[1473,226,1],[1553,226,0],[1688,364,0],[1766,606,0],[1766,753,1],[1766,932,0],[1612,1209,0],[1322,1358,0],[1128,1358,1],[886,1358,0],[514,1144,0],[302,741,0],[302,491,1],[302,298,0],[459,3,0],[756,-155,0],[954,-155,1],[1099,-155,0],[1397,-80,0],[1557,7,1],[1612,-105,1],[1467,-192,0],[1128,-283,0],[954,-283,1],[713,-283,0],[352,-92,0],[161,261,0],[161,491,1],[161,771,0],[410,1229,0],[852,1484,0],[1126,1484,1],[1367,1484,0],[1717,1303,0],[1902,973,0]],[[1296,747,1],[1296,849,0],[1164,974,0],[1054,974,1],[953,974,0],[796,847,0],[706,622,0],[706,491,1],[706,371,0],[801,235,0],[900,235,1],[1025,235,0],[1233,445,0],[1273,602,1],[1296,694,0]]]},{"char":"A","glyph":36,"advance":1366,"composite":false,"contours":[[[1167,0,1],[1006,412,1],[364,412,1],[202,0,1],[4,0,1],[579,1409,1],[796,1409,1],[1362,0,1]],[[685,1265,1],[676,1237,1],[651,1154,0],[602,1024,1],[422,561,1],[949,561,1],[768,1026,1],[740,1095,0],[712,1182,1]]]},{"char":"B","glyph":37,"advance":1366,"composite":false,"contours":[[[1258,397,1],[1258,209,0],[984,0,0],[740,0,1],[168,0,1],[168,1409,1],[680,1409,1],[1176,1409,0],[1176,1067,1],[1176,942,0],[1036,772,0],[908,743,1],[1076,723,0],[1258,538,0]],[[984,1044,1],[984,1158,0],[828,1256,0],[680,1256,1],[359,1256,1],[359,810,1],[680,810,1],[833,810,0],[984,925,0]],[[1065,412,1],[1065,661,0],[715,661,1],[359,661,1],[359,153,1],[730,153,1],[905,153,0],[1065,283,0]]]},{"char":"C","glyph":38,"advance":1479,"composite":false,"contours":[[[792,1274,1],[558,1274,0],[298,973,0],[298,711,1],[298,452,0],[569,137,0],[800,137,1],[1096,137,0],[1245,430,1],[1401,352,1],[1314,170,0],[999,-20,0],[791,-20,1],[578,-20,0],[267,157,0],[104,486,0],[104,711,1],[104,1048,0],[468,1430,0],[790,1430,1],[1015,1430,0],[1317,1254,0],[1388,1081,1],[1207,1021,1],[1158,1144,0],[941,1274,0]]]},{"char":"D","glyph":39,"advance":1479,"composite":false,"contours":[[[1381,719,1],[1381,501,0],[1211,174,0],[899,0,0],[695,0,1],[168,0,1],[168,1409,1],[634,1409,1],[992,1409,0],[1381,1050,0]],[[1189,719,1],[1189,981,0],[902,1256,0],[630,1256,1],[359,1256,1],[359,153,1],[673,153,1],[828,153,0],[1063,289,0],[1189,545,0]]]},{"char":"E","glyph":40,"advance":1366,"composite":false,"contours":[[[168,0,1],[168,1409,1],[1237,1409,1],[1237,1253,1],[359,1253,1],[359,801,1],[1177,801,1],[1177,647,1],[359,647,1],[359,156,1],[1278,156,1],[1278,0,1]]]},{"char":"F","glyph":41,"advance":1251,"composite":false,"contours":[[[359,1253,1],[359,729,1],[1145,729,1],[1145,571,1],[359,571,1],[359,0,1],[168,0,1],[168,1409,1],[1169,1409,1],[1169,1253,1]]]},{"char":"G","glyph":42,"advance":1593,"composite":false,"contours":[[[103,711,1],[103,1054,0],[471,1430,0],[804,1430,1],[1038,1430,0],[1330,1272,0],[1409,1098,1],[1227,1044,1],[1167,1164,0],[956,1274,0],[799,1274,1],[555,1274,0],[297,979,0],[297,711,1],[297,444,0],[571,135,0],[813,135,1],[951,135,0],[1190,219,0],[1264,291,1],[1264,545,1],[843,545,1],[843,705,1],[1440,705,1],[1440,219,1],[1328,105,0],[1003,-20,0],[813,-20,1],[592,-20,0],[272,156,0],[103,487,0]]]},{"char":"H","glyph":43,"advance":1479,"composite":false,"contours":[[[1121,0,1],[1121,653,1],[359,653,1],[359,0,1],[168,0,1],[168,1409,1],[359,1409,1],[359,813,1],[1121,813,1],[1121,1409,1],[1312,1409,1],[1312,0,1]]]},{"char":"I","glyph":44,"advance":569,"composite":false,"contours":[[[189,0,1],[189,1409,1],[380,1409,1],[380,0,1]]]},{"char":"J","glyph":45,"advance":1024,"composite":false,"contours":[[[457,-20,1],[99,-20,0],[32,350,1],[219,381,1],[237,265,0],[363,135,0],[458,135,1],[562,135,0],[682,278,0],[682,416,1],[682,1253,1],[411,1253,1],[411,1409,1],[872,1409,1],[872,420,1],[872,215,0],[650,-20,0]]]},{"char":"K","glyph":46,"advance":1366,"composite":false,"contours":[[[1106,0,1],[543,680,1],[359,540,1],[359,0,1],[168,0,1],[168,1409,1],[359,1409,1],[359,703,1],[1038,1409,1],[1263,1409,1],[663,797,1],[1343,0,1]]]},{"char":"L","glyph":47,"advance":1139,"composite":false,"contours":[[[168,0,1],[168,1409,1],[359,1409,1],[359,156,1],[1071,156,1],[1071,0,1]]]},{"char":"M","glyph":48,"advance":1706,"composite":false,"contours":[[[1366,0,1],[1366,940,1],[1366,1096,0],[1375,1240,1],[1326,1061,0],[1287,960,1],[923,0,1],[789,0,1],[420,960,1],[364,1130,1],[331,1240,1],[334,1129,1],[338,940,1],[338,0,1],[168,0,1],[168,1409,1],[419,1409,1],[794,432,1],[814,373,0],[851,238,0],[857,208,1],[865,248,0],[916,411,0],[925,432,1],[1293,1409,1],[1538,1409,1],[1538,0,1]]]},{"char":"N","glyph":49,"advance":1479,"composite":false,"contours":[[[1082,0,1],[328,1200,1],[333,1103,1],[338,936,1],[338,0,1],[168,0,1],[168,1409,1],[390,1409,1],[1152,201,1],[1140,397,0],[1140,485,1],[1140,1409,1],[1312,1409,1],[1312,0,1]]]},{"char":"O","glyph":50,"advance":1593,"composite":false,"contours":[[[1495,711,1],[1495,490,0],[1326,158,0],[1010,-20,0],[795,-20,1],[578,-20,0],[263,156,0],[97,489,0],[97,711,1],[97,1049,0],[467,1430,0],[797,1430,1],[1012,1430,0],[1328,1259,0],[1495,933,0]],[[1300,711,1],[1300,974,0],[1037,1274,0],[797,1274,1],[555,1274,0],[291,978,0],[291,711,1],[291,446,0],[558,135,0],[795,135,1],[1039,135,0],[1300,436,0]]]},{"char":"P","glyph":51,"advance":1366,"composite":false,"contours":[[[1258,985,1],[1258,785,0],[997,549,0],[773,549,1],[359,549,1],[359,0,1],[168,0,1],[168,1409,1],[761,1409,1],[998,1409,0],[1258,1187,0]],[[1066,983,1],[1066,1256,0],[738,1256,1],[359,1256,1],[359,700,1],[746,700,1],[1066,700,0]]]},{"char":"Q","glyph":52,"advance":1593,"composite":false,"contours":[[[1495,711,1],[1495,413,0],[1195,29,0],[928,-6,1],[969,-132,0],[1102,-244,0],[1204,-244,1],[1259,-244,0],[1319,-231,1],[1319,-365,1],[1226,-387,0],[1141,-387,1],[990,-387,0],[795,-216,0],[733,-16,1],[535,-6,0],[248,175,0],[97,498,0],[97,711,1],[97,1049,0],[467,1430,0],[797,1430,1],[1012,1430,0],[1328,1259,0],[1495,933,0]],[[1300,711,1],[1300,974,0],[1037,1274,0],[797,1274,1],[555,1274,0],[291,978,0],[291,711,1],[291,446,0],[558,135,0],[795,135,1],[1039,135,0],[1300,436,0]]]},{"char":"R","glyph":53,"advance":1479,"composite":false,"contours":[[[1164,0,1],[798,585,1],[359,585,1],[359,0,1],[168,0,1],[168,1409,1],[831,1409,1],[1069,1409,0],[1328,1196,0],[1328,1006,1],[1328,849,0],[1145,635,0],[984,607,1],[1384,0,1]],[[1136,1004,1],[1136,1127,0],[969,1256,0],[812,1256,1],[359,1256,1],[359,736,1],[820,736,1],[971,736,0],[1136,877,0]]]},{"char":"S","glyph":54,"advance":1366,"composite":false,"contours":[[[1272,389,1],[1272,194,0],[967,-20,0],[690,-20,1],[175,-20,0],[93,338,1],[278,375,1],[310,248,0],[518,129,0],[697,129,1],[882,129,0],[1083,256,0],[1083,379,1],[1083,448,0],[1020,534,0],[906,590,0],[748,628,0],[652,650,1],[485,687,0],[312,761,0],[212,852,0],[159,974,0],[159,1053,1],[159,1234,0],[436,1430,0],[694,1430,1],[934,1430,0],[1188,1283,0],[1239,1106,1],[1051,1073,1],[1020,1185,0],[846,1286,0],[692,1286,1],[523,1286,0],[345,1174,0],[345,1063,1],[345,998,0],[414,913,0],[544,854,0],[738,811,1],[803,796,0],[932,765,0],[1050,722,0],[1153,664,0],[1229,580,0],[1272,466,0]]]},{"char":"T","glyph":55,"advance":1251,"composite":false,"contours":[[[720,1253,1],[720,0,1],[530,0,1],[530,1253,1],[46,1253,1],[46,1409,1],[1204,1409,1],[1204,1253,1]]]},{"char":"U","glyph":56,"advance":1479,"composite":false,"contours":[[[731,-20,1],[558,-20,0],[300,106,0],[158,346,0],[158,512,1],[158,1409,1],[349,1409,1],[349,528,1],[349,335,0],[545,135,0],[730,135,1],[920,135,0],[1131,342,0],[1131,541,1],[1131,1409,1],[1321,1409,1],[1321,530,1],[1321,359,0],[1176,111,0],[911,-20,0]]]},{"char":"V","glyph":57,"advance":1366,"composite":false,"contours":[[[782,0,1],[584,0,1],[9,1409,1],[210,1409,1],[600,417,1],[684,168,1],[768,417,1],[1156,1409,1],[1357,1409,1]]]},{"char":"W","glyph":58,"advance":1933,"composite":false,"contours":[[[1511,0,1],[1283,0,1],[1039,895,1],[1015,979,0],[969,1196,1],[943,1080,0],[907,924,0],[652,0,1],[424,0,1],[9,1409,1],[208,1409,1],[461,514,1],[506,346,0],[544,168,1],[568,278,0],[631,538,0],[877,1409,1],[1060,1409,1],[1305,532,1],[1361,317,0],[1393,168,1],[1402,203,1],[1429,318,0],[1463,463,0],[1727,1409,1],[1926,1409,1]]]},{"char":"X","glyph":59,"advance":1366,"composite":false,"contours":[[[1112,0,1],[689,616,1],[257,0,1],[46,0,1],[582,732,1],[87,1409,1],[298,1409,1],[690,856,1],[1071,1409,1],[1282,1409,1],[800,739,1],[1323,0,1]]]},{"char":"Y","glyph":60,"advance":1366,"composite":false,"contours":[[[777,584,1],[777,0,1],[587,0,1],[587,584,1],[45,1409,1],[255,1409,1],[684,738,1],[1111,1409,1],[1321,1409,1]]]},{"char":"Z","glyph":61,"advance":1251,"composite":false,"contours":[[[1187,0,1],[65,0,1],[65,143,1],[923,1253,1],[138,1253,1],[138,1409,1],[1140,1409,1],[1140,1270,1],[282,156,1],[1187,156,1]]]},{"char":"[","glyph":62,"advance":569,"composite":false,"contours":[[[146,-425,1],[146,1484,1],[553,1484,1],[553,1355,1],[320,1355,1],[320,-296,1],[553,-296,1],[553,-425,1]]]},{"char":"\\","glyph":63,"advance":569,"composite":false,"contours":[[[407,-20,1],[0,1484,1],[158,1484,1],[569,-20,1]]]},{"char":"]","glyph":64,"advance":569,"composite":false,"contours":[[[16,-425,1],[16,-296,1],[249,-296,1],[249,1355,1],[16,1355,1],[16,1484,1],[423,1484,1],[423,-425,1]]]},{"char":"^","glyph":65,"advance":961,"composite":false,"contours":[[[787,673,1],[478,1306,1],[172,673,1],[10,673,1],[378,1409,1],[581,1409,1],[951,673,1]]]},{"char":"_","glyph":66,"advance":1139,"composite":false,"contours":[[[-31,-407,1],[-31,-277,1],[1162,-277,1],[1162,-407,1]]]},{"char":"`","glyph":67,"advance":682,"composite":false,"contours":[[[436,1201,1],[106,1479,1],[106,1508,1],[313,1508,1],[530,1221,1],[530,1201,1]]]},{"char":"a","glyph":68,"advance":1139,"composite":false,"contours":[[[414,-20,1],[251,-20,0],[87,152,0],[87,302,1],[87,470,0],[308,650,0],[554,656,1],[797,660,1],[797,719,1],[797,851,0],[685,965,0],[565,965,1],[444,965,0],[334,883,0],[323,793,1],[135,810,1],[181,1102,0],[569,1102,1],[773,1102,0],[979,915,0],[979,738,1],[979,272,1],[979,192,0],[1021,111,0],[1080,111,1],[1106,111,0],[1139,118,1],[1139,6,1],[1071,-10,0],[1000,-10,1],[900,-10,0],[809,95,0],[803,207,1],[797,207,1],[728,83,0],[545,-20,0]],[[455,115,1],[554,115,0],[708,205,0],[797,362,0],[797,445,1],[797,534,1],[600,530,1],[473,528,0],[342,480,0],[272,380,0],[272,299,1],[272,211,0],[367,115,0]]]},{"char":"b","glyph":69,"advance":1139,"composite":false,"contours":[[[1053,546,1],[1053,-20,0],[655,-20,1],[532,-20,0],[369,69,0],[318,168,1],[316,168,1],[316,137,0],[308,10,0],[306,0,1],[132,0,1],[138,54,0],[138,223,1],[138,1484,1],[318,1484,1],[318,1061,1],[318,996,0],[314,908,1],[318,908,1],[368,1012,0],[533,1102,0],[655,1102,1],[860,1102,0],[1053,826,0]],[[864,540,1],[864,767,0],[744,963,0],[609,963,1],[457,963,0],[318,755,0],[318,529,1],[318,316,0],[454,113,0],[607,113,1],[743,113,0],[864,314,0]]]},{"char":"c","glyph":70,"advance":1024,"composite":false,"contours":[[[275,546,1],[275,330,0],[411,122,0],[548,122,1],[644,122,0],[773,226,0],[788,334,1],[970,322,1],[949,166,0],[725,-20,0],[553,-20,1],[326,-20,0],[87,267,0],[87,542,1],[87,815,0],[327,1102,0],[551,1102,1],[717,1102,0],[936,930,0],[964,779,1],[779,765,1],[765,855,0],[651,961,0],[546,961,1],[403,961,0],[275,771,0]]]},{"char":"d","glyph":71,"advance":1139,"composite":false,"contours":[[[821,174,1],[771,70,0],[606,-20,0],[484,-20,1],[279,-20,0],[86,256,0],[86,536,1],[86,1102,0],[484,1102,1],[607,1102,0],[771,1012,0],[821,914,1],[823,914,1],[821,1035,1],[821,1484,1],[1001,1484,1],[1001,223,1],[1001,54,0],[1007,0,1],[835,0,1],[832,16,0],[825,132,0],[825,174,1]],[[275,542,1],[275,315,0],[395,119,0],[530,119,1],[683,119,0],[821,331,0],[821,554,1],[821,769,0],[683,969,0],[532,969,1],[396,969,0],[275,768,0]]]},{"char":"e","glyph":72,"advance":1139,"composite":false,"contours":[[[276,503,1],[276,317,0],[430,115,0],[578,115,1],[695,115,0],[836,209,0],[861,281,1],[1019,236,1],[922,-20,0],[578,-20,1],[338,-20,0],[87,266,0],[87,548,1],[87,816,0],[338,1102,0],[571,1102,1],[1048,1102,0],[1048,527,1],[1048,503,1]],[[862,641,1],[847,812,0],[703,969,0],[568,969,1],[437,969,0],[284,794,0],[278,641,1]]]},{"char":"f","glyph":73,"advance":569,"composite":false,"contours":[[[361,951,1],[361,0,1],[181,0,1],[181,951,1],[29,951,1],[29,1082,1],[181,1082,1],[181,1204,1],[181,1352,0],[311,1482,0],[445,1482,1],[520,1482,0],[572,1470,1],[572,1333,1],[527,1341,0],[492,1341,1],[423,1341,0],[361,1271,0],[361,1179,1],[361,1082,1],[572,1082,1],[572,951,1]]]},{"char":"g","glyph":74,"advance":1139,"composite":false,"contours":[[[548,-425,1],[371,-425,0],[161,-286,0],[131,-158,1],[312,-132,1],[330,-207,0],[453,-288,0],[553,-288,1],[822,-288,0],[822,27,1],[822,201,1],[820,201,1],[769,97,0],[591,-8,0],[472,-8,1],[273,-8,0],[86,256,0],[86,539,1],[86,826,0],[287,1099,0],[492,1099,1],[607,1099,0],[776,994,0],[822,897,1],[824,897,1],[824,927,0],[832,1075,0],[836,1082,1],[1007,1082,1],[1001,1028,0],[1001,858,1],[1001,31,1],[1001,-425,0]],[[822,541,1],[822,673,0],[750,864,0],[619,965,0],[536,965,1],[398,965,0],[272,765,0],[272,541,1],[272,319,0],[390,125,0],[533,125,1],[618,125,0],[750,225,0],[822,412,0]]]},{"char":"h","glyph":75,"advance":1139,"composite":false,"contours":[[[317,897,1],[375,1003,0],[538,1102,0],[663,1102,1],[839,1102,0],[1006,927,0],[1006,721,1],[1006,0,1],[825,0,1],[825,686,1],[825,800,0],[783,911,0],[687,963,0],[602,963,1],[475,963,0],[322,787,0],[322,638,1],[322,0,1],[142,0,1],[142,1484,1],[322,1484,1],[322,1098,1],[322,1037,0],[315,907,0],[314,897,1]]]},{"char":"i","glyph":76,"advance":455,"composite":false,"contours":[[[137,1312,1],[137,1484,1],[317,1484,1],[317,1312,1]],[[137,0,1],[137,1082,1],[317,1082,1],[317,0,1]]]},{"char":"j","glyph":77,"advance":455,"composite":false,"contours":[[[137,1312,1],[137,1484,1],[317,1484,1],[317,1312,1]],[[317,-134,1],[317,-287,0],[197,-425,0],[77,-425,1],[0,-425,0],[-50,-416,1],[-50,-277,1],[12,-283,1],[81,-283,0],[137,-211,0],[137,-107,1],[137,1082,1],[317,1082,1]]]},{"char":"k","glyph":78,"advance":1024,"composite":false,"contours":[[[816,0,1],[450,494,1],[318,385,1],[318,0,1],[138,0,1],[138,1484,1],[318,1484,1],[318,557,1],[793,1082,1],[1004,1082,1],[565,617,1],[1027,0,1]]]},{"char":"l","glyph":79,"advance":455,"composite":false,"contours":[[[138,0,1],[138,1484,1],[318,1484,1],[318,0,1]]]},{"char":"m","glyph":80,"advance":1706,"composite":false,"contours":[[[768,0,1],[768,686,1],[768,843,0],[682,963,0],[570,963,1],[455,963,0],[321,787,0],[321,627,1],[321,0,1],[142,0,1],[142,851,1],[142,1040,0],[136,1082,1],[306,1082,1],[307,1077,0],[309,1033,0],[312,976,0],[314,897,1],[317,897,1],[375,1012,0],[525,1102,0],[633,1102,1],[756,1102,0],[899,1004,0],[927,897,1],[930,897,1],[986,1006,0],[1145,1102,0],[1258,1102,1],[1422,1102,0],[1571,924,0],[1571,721,1],[1571,0,1],[1393,0,1],[1393,686,1],[1393,843,0],[1307,963,0],[1195,963,1],[1077,963,0],[946,788,0],[946,627,1],[946,0,1]]]},{"char":"n","glyph":81,"advance":1139,"composite":false,"contours":[[[825,0,1],[825,686,1],[825,793,0],[783,911,0],[691,963,0],[602,963,1],[472,963,0],[322,785,0],[322,627,1],[322,0,1],[142,0,1],[142,851,1],[142,1040,0],[136,1082,1],[306,1082,1],[307,1077,0],[309,1033,0],[312,976,0],[314,897,1],[317,897,1],[379,1009,0],[542,1102,0],[663,1102,1],[841,1102,0],[1006,925,0],[1006,721,1],[1006,0,1]]]},{"char":"o","glyph":82,"advance":1139,"composite":false,"contours":[[[1053,542,1],[1053,258,0],[803,-20,0],[565,-20,1],[328,-20,0],[86,269,0],[86,542,1],[86,1102,0],[571,1102,1],[819,1102,0],[1053,829,0]],[[864,542,1],[864,766,0],[731,969,0],[574,969,1],[416,969,0],[275,762,0],[275,542,1],[275,328,0],[414,113,0],[563,113,1],[725,113,0],[864,321,0]]]},{"char":"p","glyph":83,"advance":1139,"composite":false,"contours":[[[1053,546,1],[1053,-20,0],[655,-20,1],[405,-20,0],[319,168,1],[314,168,1],[318,160,0],[318,-2,1],[318,-425,1],[138,-425,1],[138,861,1],[138,1028,0],[132,1082,1],[306,1082,1],[307,1078,0],[311,1029,0],[316,927,0],[316,908,1],[320,908,1],[368,1008,0],[526,1101,0],[655,1101,1],[855,1101,0],[1053,833,0]],[[864,542,1],[864,768,0],[742,962,0],[609,962,1],[502,962,0],[381,872,0],[318,681,0],[318,528,1],[318,315,0],[454,113,0],[607,113,1],[741,113,0],[864,310,0]]]},{"char":"q","glyph":84,"advance":1139,"composite":false,"contours":[[[484,-20,1],[278,-20,0],[86,258,0],[86,536,1],[86,1102,0],[484,1102,1],[607,1102,0],[767,1015,0],[821,914,1],[823,914,1],[823,944,0],[831,1091,0],[835,1096,1],[1008,1096,1],[1001,1037,0],[1001,801,1],[1001,-425,1],[821,-425,1],[821,14,1],[825,178,1],[823,178,1],[769,71,0],[611,-20,0]],[[821,554,1],[821,765,0],[683,969,0],[532,969,1],[395,969,0],[275,765,0],[275,542,1],[275,315,0],[396,119,0],[530,119,1],[683,119,0],[821,337,0]]]},{"char":"r","glyph":85,"advance":682,"composite":false,"contours":[[[142,0,1],[142,830,1],[142,944,0],[136,1082,1],[306,1082,1],[314,898,0],[314,861,1],[318,861,1],[361,1000,0],[473,1102,0],[575,1102,1],[611,1102,0],[648,1092,1],[648,927,1],[612,937,0],[552,937,1],[440,937,0],[322,744,0],[322,564,1],[322,0,1]]]},{"char":"s","glyph":86,"advance":1024,"composite":false,"contours":[[[950,299,1],[950,146,0],[719,-20,0],[511,-20,1],[309,-20,0],[90,113,0],[57,254,1],[216,285,1],[239,198,0],[383,117,0],[511,117,1],[648,117,0],[775,201,0],[775,285,1],[775,349,0],[687,429,0],[589,455,1],[460,489,1],[305,529,0],[174,606,0],[100,716,0],[100,796,1],[100,944,0],[311,1099,0],[513,1099,1],[692,1099,0],[903,973,0],[931,834,1],[769,814,1],[754,886,0],[623,963,0],[513,963,1],[391,963,0],[275,889,0],[275,814,1],[275,768,0],[323,708,0],[417,666,0],[568,629,1],[711,593,0],[837,532,0],[910,458,0],[950,361,0]]]},{"char":"t","glyph":87,"advance":569,"composite":false,"contours":[[[554,8,1],[465,-16,0],[372,-16,1],[156,-16,0],[156,229,1],[156,951,1],[31,951,1],[31,1082,1],[163,1082,1],[216,1324,1],[336,1324,1],[336,1082,1],[536,1082,1],[536,951,1],[336,951,1],[336,268,1],[336,190,0],[387,127,0],[450,127,1],[486,127,0],[554,141,1]]]},{"char":"u","glyph":88,"advance":1139,"composite":false,"contours":[[[314,1082,1],[314,396,1],[314,289,0],[356,171,0],[448,119,0],[537,119,1],[667,119,0],[817,297,0],[817,455,1],[817,1082,1],[997,1082,1],[997,231,1],[997,42,0],[1003,0,1],[833,0,1],[832,5,0],[830,49,0],[827,106,0],[825,185,1],[822,185,1],[760,73,0],[597,-20,0],[476,-20,1],[298,-20,0],[133,157,0],[133,361,1],[133,1082,1]]]},{"char":"v","glyph":89,"advance":1024,"composite":false,"contours":[[[613,0,1],[400,0,1],[7,1082,1],[199,1082,1],[437,378,1],[450,338,0],[506,141,1],[541,258,1],[580,376,1],[826,1082,1],[1017,1082,1]]]},{"char":"w","glyph":90,"advance":1479,"composite":false,"contours":[[[1174,0,1],[965,0,1],[776,765,1],[740,934,1],[731,889,0],[693,720,0],[508,0,1],[300,0,1],[-3,1082,1],[175,1082,1],[358,347,1],[365,323,0],[401,149,1],[418,223,1],[644,1082,1],[837,1082,1],[1026,339,1],[1072,149,1],[1103,288,1],[1308,1082,1],[1484,1082,1]]]},{"char":"x","glyph":91,"advance":1024,"composite":false,"contours":[[[801,0,1],[510,444,1],[217,0,1],[23,0,1],[408,556,1],[41,1082,1],[240,1082,1],[510,661,1],[778,1082,1],[979,1082,1],[612,558,1],[1002,0,1]]]},{"char":"y","glyph":92,"advance":1024,"composite":false,"contours":[[[191,-425,1],[117,-425,0],[67,-414,1],[67,-279,1],[105,-285,0],[151,-285,1],[319,-285,0],[417,-38,1],[434,5,1],[5,1082,1],[197,1082,1],[425,484,1],[430,470,0],[444,431,0],[520,209,0],[523,196,1],[593,393,1],[830,1082,1],[1020,1082,1],[604,0,1],[537,-173,0],[421,-342,0],[280,-425,0]]]},{"char":"z","glyph":93,"advance":1024,"composite":false,"contours":[[[83,0,1],[83,137,1],[688,943,1],[117,943,1],[117,1082,1],[901,1082,1],[901,945,1],[295,139,1],[922,139,1],[922,0,1]]]},{"char":"{","glyph":94,"advance":684,"composite":false,"contours":[[[513,-425,1],[386,-425,0],[249,-271,0],[249,-132,1],[249,229,1],[249,346,0],[144,461,0],[34,466,1],[34,593,1],[143,597,0],[249,712,0],[249,829,1],[249,1191,1],[249,1332,0],[382,1484,0],[513,1484,1],[648,1484,1],[648,1355,1],[585,1355,1],[494,1355,0],[417,1248,0],[417,1140,1],[417,784,1],[417,690,0],[311,553,0],[223,532,1],[223,530,1],[312,509,0],[417,373,0],[417,276,1],[417,-81,1],[417,-187,0],[494,-296,0],[585,-296,1],[648,-296,1],[648,-425,1]]]},{"char":"|","glyph":95,"advance":532,"composite":false,"contours":[[[183,-434,1],[183,1484,1],[349,1484,1],[349,-434,1]]]},{"char":"}","glyph":96,"advance":684,"composite":false,"contours":[[[94,-296,1],[185,-296,0],[264,-187,0],[264,-81,1],[264,276,1],[264,374,0],[368,510,0],[457,530,1],[457,532,1],[371,552,0],[264,687,0],[264,784,1],[264,1140,1],[264,1248,0],[185,1355,0],[94,1355,1],[34,1355,1],[34,1484,1],[166,1484,1],[297,1484,0],[430,1332,0],[430,1191,1],[430,829,1],[430,713,0],[536,597,0],[647,593,1],[647,466,1],[535,462,0],[430,346,0],[430,229,1],[430,-132,1],[430,-270,0],[293,-425,0],[166,-425,1],[34,-425,1],[34,-296,1]]]},{"char":"~","glyph":97,"advance":1196,"composite":false,"contours":[[[844,553,1],[775,553,0],[630,597,0],[557,623,1],[428,668,0],[340,668,1],[273,668,0],[157,627,0],[92,580,1],[92,723,1],[203,807,0],[355,807,1],[407,807,0],[534,781,0],[664,735,1],[695,723,0],[815,690,0],[860,690,1],[990,690,0],[1104,782,1],[1104,633,1],[1046,591,0],[929,553,0]]]},{"char":"Ä","glyph":134,"advance":1366,"composite":true,"contours":[[[1167,0,1],[1006,412,1],[364,412,1],[202,0,1],[4,0,1],[579,1409,1],[796,1409,1],[1362,0,1]],[[685,1265,1],[676,1237,1],[651,1154,0],[602,1024,1],[422,561,1],[949,561,1],[768,1026,1],[740,1095,0],[712,1182,1]],[[803,1530,1],[803,1714,1],[966,1714,1],[966,1530,1]],[[409,1530,1],[409,1714,1],[574,1714,1],[574,1530,1]]]},{"char":"Ö","glyph":152,"advance":1593,"composite":true,"contours":[[[1495,711,1],[1495,490,0],[1326,158,0],[1010,-20,0],[795,-20,1],[578,-20,0],[263,156,0],[97,489,0],[97,711,1],[97,1049,0],[467,1430,0],[797,1430,1],[1012,1430,0],[1328,1259,0],[1495,933,0]],[[1300,711,1],[1300,974,0],[1037,1274,0],[797,1274,1],[555,1274,0],[291,978,0],[291,711,1],[291,446,0],[558,135,0],[795,135,1],[1039,135,0],[1300,436,0]],[[910,1530,1],[910,1714,1],[1073,1714,1],[1073,1530,1]],[[516,1530,1],[516,1714,1],[681,1714,1],[681,1530,1]]]},{"char":"Ü","glyph":158,"advance":1479,"composite":true,"contours":[[[731,-20,1],[558,-20,0],[300,106,0],[158,346,0],[158,512,1],[158,1409,1],[349,1409,1],[349,528,1],[349,335,0],[545,135,0],[730,135,1],[920,135,0],[1131,342,0],[1131,541,1],[1131,1409,1],[1321,1409,1],[1321,530,1],[1321,359,0],[1176,111,0],[911,-20,0]],[[853,1530,1],[853,1714,1],[1016,1714,1],[1016,1530,1]],[[459,1530,1],[459,1714,1],[624,1714,1],[624,1530,1]]]},{"char":"ä","glyph":166,"advance":1139,"composite":true,"contours":[[[414,-20,1],[251,-20,0],[87,152,0],[87,302,1],[87,470,0],[308,650,0],[554,656,1],[797,660,1],[797,719,1],[797,851,0],[685,965,0],[565,965,1],[444,965,0],[334,883,0],[323,793,1],[135,810,1],[181,1102,0],[569,1102,1],[773,1102,0],[979,915,0],[979,738,1],[979,272,1],[979,192,0],[1021,111,0],[1080,111,1],[1106,111,0],[1139,118,1],[1139,6,1],[1071,-10,0],[1000,-10,1],[900,-10,0],[809,95,0],[803,207,1],[797,207,1],[728,83,0],[545,-20,0]],[[455,115,1],[554,115,0],[708,205,0],[797,362,0],[797,445,1],[797,534,1],[600,530,1],[473,528,0],[342,480,0],[272,380,0],[272,299,1],[272,211,0],[367,115,0]],[[674,1219,1],[674,1403,1],[837,1403,1],[837,1219,1]],[[280,1219,1],[280,1403,1],[445,1403,1],[445,1219,1]]]},{"char":"ö","glyph":184,"advance":1139,"composite":true,"contours":[[[1053,542,1],[1053,258,0],[803,-20,0],[565,-20,1],[328,-20,0],[86,269,0],[86,542,1],[86,1102,0],[571,1102,1],[819,1102,0],[1053,829,0]],[[864,542,1],[864,766,0],[731,969,0],[574,969,1],[416,969,0],[275,762,0],[275,542,1],[275,328,0],[414,113,0],[563,113,1],[725,113,0],[864,321,0]],[[689,1219,1],[689,1403,1],[852,1403,1],[852,1219,1]],[[295,1219,1],[295,1403,1],[460,1403,1],[460,1219,1]]]},{"char":"ü","glyph":190,"advance":1139,"composite":true,"contours":[[[320,1082,1],[320,396,1],[320,289,0],[362,171,0],[454,119,0],[543,119,1],[673,119,0],[823,297,0],[823,455,1],[823,1082,1],[1003,1082,1],[1003,231,1],[1003,42,0],[1009,0,1],[839,0,1],[838,5,0],[836,49,0],[833,106,0],[831,185,1],[828,185,1],[766,73,0],[603,-20,0],[482,-20,1],[304,-20,0],[139,157,0],[139,361,1],[139,1082,1]],[[676,1219,1],[676,1403,1],[839,1403,1],[839,1219,1]],[[282,1219,1],[282,1403,1],[447,1403,1],[447,1219,1]]]},{"char":"ß","glyph":161,"advance":1251,"composite":false,"contours":[[[1167,295,1],[1167,145,0],[979,-20,0],[809,-20,1],[638,-20,0],[526,29,1],[524,193,1],[576,164,0],[736,124,0],[805,124,1],[897,124,0],[995,210,0],[995,289,1],[995,353,0],[910,455,0],[813,513,1],[721,567,0],[630,699,0],[630,785,1],[630,846,0],[687,946,0],[741,991,1],[799,1039,0],[852,1123,0],[852,1173,1],[852,1250,0],[718,1343,0],[609,1343,1],[461,1343,0],[322,1183,0],[322,1021,1],[322,0,1],[142,0,1],[142,1027,1],[142,1254,0],[377,1484,0],[609,1484,1],[799,1484,0],[1024,1323,0],[1024,1187,1],[1024,1044,0],[911,941,1],[832,869,0],[798,820,0],[798,791,1],[798,753,0],[853,694,0],[935,637,0],[983,605,1],[1167,481,0]]]},{"char":"é","glyph":171,"advance":1139,"composite":true,"contours":[[[276,503,1],[276,317,0],[430,115,0],[578,115,1],[695,115,0],[836,209,0],[861,281,1],[1019,236,1],[922,-20,0],[578,-20,1],[338,-20,0],[87,266,0],[87,548,1],[87,816,0],[338,1102,0],[571,1102,1],[1048,1102,0],[1048,527,1],[1048,503,1]],[[862,641,1],[847,812,0],[703,969,0],[568,969,1],[437,969,0],[284,794,0],[278,641,1]],[[440,1201,1],[440,1221,1],[657,1508,1],[864,1508,1],[864,1479,1],[534,1201,1]]]},{"char":"Å","glyph":135,"advance":1366,"composite":true,"contours":[[[1167,0,1],[1006,412,1],[364,412,1],[202,0,1],[4,0,1],[579,1409,1],[796,1409,1],[1362,0,1]],[[685,1265,1],[676,1237,1],[651,1154,0],[602,1024,1],[422,561,1],[949,561,1],[768,1026,1],[740,1095,0],[712,1182,1]],[[928,1546,1],[928,1446,0],[786,1304,0],[686,1304,1],[586,1304,0],[444,1446,0],[444,1546,1],[444,1647,0],[586,1787,0],[686,1787,1],[785,1787,0],[928,1646,0]],[[820,1546,1],[820,1602,0],[742,1680,0],[686,1680,1],[629,1680,0],[551,1602,0],[551,1546,1],[551,1491,0],[627,1409,0],[686,1409,1],[744,1409,0],[820,1490,0]]]},{"char":"€","glyph":2088,"advance":1139,"composite":false,"contours":[[[691,126,1],[791,126,0],[911,226,0],[925,317,1],[1110,303,1],[1080,148,0],[861,-20,0],[687,-20,1],[470,-20,0],[217,242,0],[187,502,1],[16,502,1],[56,631,1],[176,631,1],[173,709,1],[176,795,1],[16,795,1],[56,922,1],[185,922,1],[217,1179,0],[465,1430,0],[684,1430,1],[859,1430,0],[1078,1261,0],[1107,1107,1],[922,1093,1],[908,1184,0],[787,1284,0],[681,1284,1],[539,1284,0],[388,1110,0],[367,922,1],[813,922,1],[773,795,1],[360,795,1],[357,771,0],[357,709,1],[357,669,0],[361,631,1],[813,631,1],[773,502,1],[369,502,1],[391,307,0],[553,126,0]]]},{"char":"°","glyph":114,"advance":819,"composite":false,"contours":[[[696,1145,1],[696,1026,0],[527,860,0],[409,860,1],[291,860,0],[122,1028,0],[122,1145,1],[122,1262,0],[289,1430,0],[409,1430,1],[529,1430,0],[696,1264,0]],[[587,1145,1],[587,1221,0],[484,1325,0],[409,1325,1],[334,1325,0],[231,1219,0],[231,1145,1],[231,1071,0],[336,965,0],[409,965,1],[483,965,0],[587,1070,0]]]},{"char":"±","glyph":115,"advance":1124,"composite":false,"contours":[[[636,680,1],[636,285,1],[489,285,1],[489,680,1],[65,680,1],[65,825,1],[489,825,1],[489,1219,1],[636,1219,1],[636,825,1],[1060,825,1],[1060,680,1]],[[65,0,1],[65,145,1],[1060,145,1],[1060,0,1]]]},{"char":"Ì","glyph":142,"advance":569,"composite":true,"contours":[[[189,0,1],[189,1409,1],[380,1409,1],[380,0,1]],[[319,1530,1],[9,1747,1],[9,1776,1],[216,1776,1],[433,1550,1],[433,1530,1]]]},{"char":"Î","glyph":144,"advance":569,"composite":true,"contours":[[[189,0,1],[189,1409,1],[380,1409,1],[380,0,1]],[[616,1550,1],[616,1530,1],[511,1530,1],[292,1669,1],[290,1669,1],[58,1530,1],[-46,1530,1],[-46,1550,1],[188,1790,1],[392,1790,1]]]},{"char":"ï","glyph":177,"advance":569,"composite":true,"contours":[[[194,0,1],[194,1082,1],[374,1082,1],[374,0,1]],[[402,1219,1],[402,1403,1],[565,1403,1],[565,1219,1]],[[8,1219,1],[8,1403,1],[173,1403,1],[173,1219,1]]]},{"char":"Ī","glyph":236,"advance":569,"composite":true,"contours":[[[189,0,1],[189,1409,1],[380,1409,1],[380,0,1]],[[561,1565,1],[12,1565,1],[12,1692,1],[561,1692,1]]]},{"char":"¼","glyph":126,"advance":1708,"composite":true,"contours":[[[56,563,1],[56,670,1],[267,670,1],[267,1290,1],[65,1152,1],[65,1272,1],[275,1409,1],[398,1409,1],[398,670,1],[613,670,1],[613,563,1]],[[493,0,1],[345,0,1],[1226,1409,1],[1371,1409,1]],[[1605,148,1],[1605,-36,1],[1497,-36,1],[1497,148,1],[1076,148,1],[1076,229,1],[1485,776,1],[1605,776,1],[1605,230,1],[1730,230,1],[1730,148,1]],[[1497,659,1],[1488,635,0],[1454,590,1],[1226,284,1],[1191,241,1],[1181,230,1],[1497,230,1]]]}]} \ No newline at end of file diff --git a/tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.ttf b/tests/CADability.Tests/Files/Fonts/LiberationSans-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..7769c4119cbdee3d2d121262ad2c9c07da4a69e9 GIT binary patch literal 410820 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 + /// The managed TrueType reader, which replaces GDI for text on every platform (issue #412, step 6). The reference data + /// (LiberationSans-Regular.reference.json) was written by fontTools from the same font file, so every value here is + /// checked against an independent implementation. + /// + [TestClass] + public class TrueTypeFontTests + { + private static string FontFile(string name, [CallerFilePath] string thisFile = "") + => Path.Combine(Path.GetDirectoryName(thisFile), "Files", "Fonts", name); + + private static TrueTypeFont LoadLiberationSans() + { + TrueTypeFont[] fonts = TrueTypeFont.Load(FontFile("LiberationSans-Regular.ttf")); + Assert.AreEqual(1, fonts.Length); + return fonts[0]; + } + private static JsonElement Reference() + { + return JsonDocument.Parse(File.ReadAllText(FontFile("LiberationSans-Regular.reference.json"))).RootElement; + } + + [TestMethod] + public void NamesAndMetricsMatchFontTools() + { + TrueTypeFont font = LoadLiberationSans(); + JsonElement reference = Reference(); + Assert.AreEqual(reference.GetProperty("family").GetString(), font.FamilyName); + Assert.AreEqual(reference.GetProperty("subfamily").GetString(), font.SubfamilyName); + Assert.AreEqual(reference.GetProperty("full").GetString(), font.FullName); + Assert.AreEqual(reference.GetProperty("unitsPerEm").GetInt32(), font.UnitsPerEm); + Assert.AreEqual(reference.GetProperty("ascender").GetInt32(), font.Ascender); + Assert.AreEqual(reference.GetProperty("descender").GetInt32(), font.Descender); + Assert.AreEqual(reference.GetProperty("lineGap").GetInt32(), font.LineGap); + Assert.AreEqual(reference.GetProperty("winAscent").GetInt32(), font.WinAscent); + Assert.AreEqual(reference.GetProperty("winDescent").GetInt32(), font.WinDescent); + Assert.AreEqual(reference.GetProperty("glyphCount").GetInt32(), font.GlyphCount); + Assert.IsFalse(font.IsBold); + Assert.IsFalse(font.IsItalic); + Assert.IsTrue(font.HasTrueTypeOutlines); + } + + [TestMethod] + public void GlyphsMatchFontTools() + { + TrueTypeFont font = LoadLiberationSans(); + int composites = 0; + foreach (JsonElement expected in Reference().GetProperty("glyphs").EnumerateArray()) + { + string c = expected.GetProperty("char").GetString(); + int glyph = font.GetGlyphIndex(c[0]); + Assert.AreEqual(expected.GetProperty("glyph").GetInt32(), glyph, $"glyph index of '{c}'"); + Assert.AreEqual(expected.GetProperty("advance").GetInt32(), font.GetAdvanceWidth(glyph), $"advance width of '{c}'"); + if (expected.GetProperty("composite").GetBoolean()) ++composites; + List contours = font.GetContours(glyph); + JsonElement expectedContours = expected.GetProperty("contours"); + Assert.AreEqual(expectedContours.GetArrayLength(), contours.Count, $"number of contours of '{c}'"); + int ci = 0; + foreach (JsonElement expectedContour in expectedContours.EnumerateArray()) + { + GlyphPoint[] contour = contours[ci++]; + Assert.AreEqual(expectedContour.GetArrayLength(), contour.Length, $"points of contour {ci} of '{c}'"); + int pi = 0; + foreach (JsonElement p in expectedContour.EnumerateArray()) + { + GlyphPoint actual = contour[pi++]; + // fontTools rounds the points of transformed components to integers + Assert.AreEqual(p[0].GetInt32(), actual.X, 0.51, $"x of point {pi} of '{c}'"); + Assert.AreEqual(p[1].GetInt32(), actual.Y, 0.51, $"y of point {pi} of '{c}'"); + Assert.AreEqual(p[2].GetInt32() == 1, actual.OnCurve, $"on curve flag of point {pi} of '{c}'"); + } + } + } + Assert.IsTrue(composites > 0, "the reference contains composite glyphs (e.g. Ä)"); + } + + [TestMethod] + public void KerningMatchesFontTools() + { + TrueTypeFont font = LoadLiberationSans(); + JsonElement reference = Reference(); + Assert.AreEqual(reference.GetProperty("kerningPairCount").GetInt32(), font.GetKerningPairs().Count()); + foreach (JsonElement pair in reference.GetProperty("kerning").EnumerateArray()) + { + int left = font.GetGlyphIndex(pair.GetProperty("left").GetString()[0]); + int right = font.GetGlyphIndex(pair.GetProperty("right").GetString()[0]); + Assert.AreEqual(pair.GetProperty("value").GetInt32(), font.GetKerning(left, right), pair.GetProperty("left").GetString() + pair.GetProperty("right").GetString()); + } + Assert.AreEqual(0, font.GetKerning(font.GetGlyphIndex('H'), font.GetGlyphIndex('H')), "no kerning between straight letters"); + } + + [TestMethod] + public void MissingCharacterAndSpace() + { + TrueTypeFont font = LoadLiberationSans(); + Assert.AreEqual(0, font.GetGlyphIndex(0x10FFFF), "a character the font does not have gives the missing glyph"); + int space = font.GetGlyphIndex(' '); + Assert.AreNotEqual(0, space); + Assert.AreEqual(0, font.GetContours(space).Count); + font.GetPathData(space, out PointF[] points, out byte[] types); + Assert.AreEqual(0, points.Length); + Assert.AreEqual(0, types.Length); + } + + [TestMethod] + public void FontCollectionIsRead() + { + // a .ttc file with the same font twice: the table offsets of a collection count from the start of the file + byte[] ttf = File.ReadAllBytes(FontFile("LiberationSans-Regular.ttf")); + byte[] ttc = MakeCollection(ttf, 2); + TrueTypeFont[] fonts = TrueTypeFont.Load(ttc); + Assert.AreEqual(2, fonts.Length); + TrueTypeFont single = LoadLiberationSans(); + foreach (TrueTypeFont font in fonts) + { + Assert.AreEqual(single.FamilyName, font.FamilyName); + int glyph = font.GetGlyphIndex('g'); + Assert.AreEqual(single.GetGlyphIndex('g'), glyph); + List expected = single.GetContours(glyph), actual = font.GetContours(glyph); + Assert.AreEqual(expected.Count, actual.Count); + for (int i = 0; i < expected.Count; ++i) CollectionAssert.AreEqual(expected[i], actual[i]); + } + } + + [TestMethod] + public void PathDataIsInTheGdiPlusLayout() + { + TrueTypeFont font = LoadLiberationSans(); + double em = font.UnitsPerEm; + // the "H" stands on the baseline, which is at CellAscent / em below the top of the cell + font.GetPathData(font.GetGlyphIndex('H'), out PointF[] points, out byte[] types); + Assert.AreEqual(0, types[0], "a figure starts with a start point"); + Assert.AreEqual(1, types.Count(t => (t & 0x80) != 0), "the H is a single closed figure"); + Assert.IsTrue(types.All(t => (t & 0x7F) == 0 || (t & 0x7F) == 1), "the H consists of lines only"); + Assert.AreEqual(font.CellAscent / em, points.Max(p => p.Y), 1e-6, "the bottom of the H is the baseline"); + // the "o" has two closed figures made of Bezier segments + font.GetPathData(font.GetGlyphIndex('o'), out points, out types); + Assert.AreEqual(2, types.Count(t => (t & 0x80) != 0)); + Assert.IsTrue(types.Count(t => (t & 0x7F) == 3) % 3 == 0, "Bezier points come in groups of three"); + } + + [TestMethod] + public void OutlinesAreTheExactQuadraticCurves() + { + // the cubic segments of the path data are the degree elevation of the quadratic segments of the font: points of the + // quadratic curves lie on the outline that GlyphShapeBuilder builds with the highest precision (exact splines) + TrueTypeFont font = LoadLiberationSans(); + double em = font.UnitsPerEm; + foreach (char c in "S@&8g") + { + int glyph = font.GetGlyphIndex(c); + font.GetPathData(glyph, out PointF[] points, out byte[] types); + Path2D[] paths = GlyphShapeBuilder.PathDataToPath2D(points, types, 2); + Assert.AreEqual(font.GetContours(glyph).Count, paths.Length, $"one path per contour of '{c}'"); + Assert.IsTrue(paths.All(p => p.IsClosed), $"all paths of '{c}' are closed"); + foreach (GeoPoint2D p in PointsOnQuadraticSegments(font.GetContours(glyph))) + { + GeoPoint2D mapped = new GeoPoint2D(p.x / em, 1.0 - (font.CellAscent - p.y) / em); // PathDataToPath2D turns y upwards + double distance = paths.Min(path => path.MinDistance(mapped)); + Assert.AreEqual(0.0, distance, 1e-5, $"point {p} of '{c}'"); + } + } + } + + [TestMethod] + public void InvalidDataIsRejected() + { + Assert.ThrowsException(() => TrueTypeFont.Load(new byte[0])); + Assert.ThrowsException(() => TrueTypeFont.Load(System.Text.Encoding.ASCII.GetBytes("this is not a font file at all"))); + } + + private static IEnumerable PointsOnQuadraticSegments(List contours) + { + foreach (GlyphPoint[] contour in contours) + { + int n = contour.Length; + for (int i = 0; i < n; ++i) + { + GlyphPoint p = contour[i]; + if (p.OnCurve) continue; + GlyphPoint prev = contour[(i + n - 1) % n], next = contour[(i + 1) % n]; + // implied on-curve points between two off-curve points + GeoPoint2D s = prev.OnCurve ? new GeoPoint2D(prev.X, prev.Y) : new GeoPoint2D((prev.X + p.X) / 2, (prev.Y + p.Y) / 2); + GeoPoint2D e = next.OnCurve ? new GeoPoint2D(next.X, next.Y) : new GeoPoint2D((next.X + p.X) / 2, (next.Y + p.Y) / 2); + GeoPoint2D ctrl = new GeoPoint2D(p.X, p.Y); + foreach (double t in new[] { 0.25, 0.5, 0.75 }) + { + double u = 1 - t; + yield return new GeoPoint2D(u * u * s.x + 2 * u * t * ctrl.x + t * t * e.x, u * u * s.y + 2 * u * t * ctrl.y + t * t * e.y); + } + } + } + } + private static byte[] MakeCollection(byte[] ttf, int count) + { + int header = 12 + 4 * count; + byte[] res = new byte[header + count * ttf.Length]; + void Put32(int pos, uint v) { res[pos] = (byte)(v >> 24); res[pos + 1] = (byte)(v >> 16); res[pos + 2] = (byte)(v >> 8); res[pos + 3] = (byte)v; } + res[0] = (byte)'t'; res[1] = (byte)'t'; res[2] = (byte)'c'; res[3] = (byte)'f'; + Put32(4, 0x00010000); + Put32(8, (uint)count); + int numTables = (ttf[4] << 8) | ttf[5]; + for (int f = 0; f < count; ++f) + { + int start = header + f * ttf.Length; + Put32(12 + 4 * f, (uint)start); + Array.Copy(ttf, 0, res, start, ttf.Length); + for (int i = 0; i < numTables; ++i) + { // move the table offsets of the table directory + int rec = start + 12 + 16 * i + 8; + uint offset = (uint)((res[rec] << 24) | (res[rec + 1] << 16) | (res[rec + 2] << 8) | res[rec + 3]); + Put32(rec, offset + (uint)start); + } + } + return res; + } + } +} From cb05b5b0ade2865a20170e695b9ea10ef5d06229 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 08:41:51 +0000 Subject: [PATCH 2/6] Get the fonts of texts from a font service of the kernel instead of GDI (#412) Second part of step 6 of #412. Text and FontCache no longer use GDI or GDI+: the glyph outlines, advance widths, kerning, metrics and the list of font families come from the static, settable FontProvider.Current, not from FrameImpl.MainFrame, so texts work without a frame and on every platform. - FontProvider and FontFace (public, new) describe what the kernel needs. A face returns its glyphs as Bezier path data in the layout of GraphicsPath.AddString, which the kernel turns into geometry with GlyphShapeBuilder as before, so no provider can flatten the outlines. - ManagedFontProvider (public, new) is the default on all platforms, as decided for #412. It indexes the TrueType fonts of the system font directories by reading only their header tables, and loads a font file when a face is used. Files and font data can be added. - FontCache no longer creates a GDI device context in its constructor or deletes it in a finalizer, which ended the process outside Windows. - Without any font, painting and the extent of a text leave it out; GetOutline and similar explicit requests throw an InvalidOperationException that says what to do. - ShowPropertyFont and DimensionStyle list the families of the provider. - CADability.Forms.GdiFontProvider (public, new) is the old GDI implementation for applications that want it. TrueTypeFont reads the underline and strikeout metrics, adds both as rectangles like GDI+ does, and can read only the header tables of a font file. Tests: both test projects register Liberation Sans in an assembly initializer, so texts have a font even on a machine without fonts. The text tests that were inconclusive on Linux ("text needs GDI") now run there; new FontProviderTests cover text without a frame, the default face for unknown fonts, the exception without fonts, underline and strikeout, style fallback, header-only reading and the finalizer. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 --- CADability.Forms/GdiFontProvider.cs | 205 +++++++++++ CADability/DimensionStyle.cs | 5 +- CADability/FontProvider.cs | 316 +++++++++++++++++ CADability/Text.cs | 319 ++++++------------ CADability/TrueTypeFont.cs | 131 +++++++ .../CADability.ImportTests.csproj | 7 + .../DxfDimensionTest.cs | 9 - .../DxfOcsExportTest.cs | 10 - tests/CADability.ImportTests/TestFonts.cs | 20 ++ tests/CADability.Tests/FontCacheGlyphTests.cs | 12 +- tests/CADability.Tests/FontProviderTests.cs | 170 ++++++++++ tests/CADability.Tests/ProjectTest.cs | 16 - tests/CADability.Tests/TestFonts.cs | 20 ++ 13 files changed, 983 insertions(+), 257 deletions(-) create mode 100644 CADability.Forms/GdiFontProvider.cs create mode 100644 CADability/FontProvider.cs create mode 100644 tests/CADability.ImportTests/TestFonts.cs create mode 100644 tests/CADability.Tests/FontProviderTests.cs create mode 100644 tests/CADability.Tests/TestFonts.cs diff --git a/CADability.Forms/GdiFontProvider.cs b/CADability.Forms/GdiFontProvider.cs new file mode 100644 index 000000000..6773de1c1 --- /dev/null +++ b/CADability.Forms/GdiFontProvider.cs @@ -0,0 +1,205 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Runtime.InteropServices; + +namespace CADability.Forms +{ + /// + /// A that gets the fonts from GDI and GDI+, as CADability did before issue #412: the glyph + /// outlines from , the widths and + /// the kerning from gdi32. It knows every font Windows knows (also OpenType fonts with CFF outlines) and synthesizes bold + /// and italic faces a family does not have. Set to an instance of this class to use it; + /// the default is the . + /// + public class GdiFontProvider : FontProvider + { + private readonly Dictionary<(string family, bool bold, bool italic), GdiFontFace> faces = new Dictionary<(string, bool, bool), GdiFontFace>(); + private HashSet familyNames; // upper case + + /// + public override string[] GetFamilyNames() + { + return FontFamily.Families.Select(f => f.Name).ToArray(); + } + /// + public override FontFace GetFace(string familyName, bool bold, bool italic) + { + if (string.IsNullOrEmpty(familyName)) return null; + lock (faces) + { + if (familyNames == null) familyNames = new HashSet(GetFamilyNames().Select(n => n.ToUpperInvariant())); + if (!familyNames.Contains(familyName.ToUpperInvariant())) return null; + if (!faces.TryGetValue((familyName.ToUpperInvariant(), bold, italic), out GdiFontFace face)) + { + face = new GdiFontFace(new FontFamily(familyName), bold, italic); + faces[(familyName.ToUpperInvariant(), bold, italic)] = face; + } + return face; + } + } + /// + /// The generic sans serif family of GDI+, as before issue #412. + /// + public override FontFace GetDefaultFace(bool bold, bool italic) + { + using (FontFamily sansSerif = new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif)) + { + return GetFace(sansSerif.Name, bold, italic); + } + } + } + + /// + /// A face of the . + /// + internal sealed class GdiFontFace : FontFace + { + private readonly FontFamily family; + private readonly FontStyle style; // bold and italic only + private readonly int em; + private readonly Dictionary widths = new Dictionary(); + private Dictionary<(char, char), double> kerning; + + public GdiFontFace(FontFamily family, bool bold, bool italic) + { + this.family = family; + FontStyle requested = (bold ? FontStyle.Bold : FontStyle.Regular) | (italic ? FontStyle.Italic : FontStyle.Regular); + style = requested; + if (!family.IsStyleAvailable(style)) + { + if (family.IsStyleAvailable(FontStyle.Regular)) style = FontStyle.Regular; + else if (family.IsStyleAvailable(FontStyle.Bold)) style = FontStyle.Bold; + else if (family.IsStyleAvailable(FontStyle.Italic)) style = FontStyle.Italic; + } + em = family.GetEmHeight(style); + if (em == 0) em = 1000; + } + public override string FamilyName => family.Name; + public override bool IsBold => (style & FontStyle.Bold) != 0; + public override bool IsItalic => (style & FontStyle.Italic) != 0; + public override int EmHeight => em; + public override int CellAscent => family.GetCellAscent(style); + public override int CellDescent => family.GetCellDescent(style); + public override int LineSpacing => family.GetLineSpacing(style); + + public override double GetAdvanceWidth(char c) + { + lock (widths) + { + if (widths.TryGetValue(c, out double width)) return width; + WithSelectedFont(hdc => + { + ABC[] abc = new ABC[1]; + GetCharABCWidths(hdc, c, c, abc); + width = (abc[0].abcA + abc[0].abcB + abc[0].abcC) / (double)em; + }); + widths[c] = width; + return width; + } + } + public override double GetKerning(char left, char right) + { + lock (widths) + { + if (kerning == null) + { + Dictionary<(char, char), double> pairs = new Dictionary<(char, char), double>(); + WithSelectedFont(hdc => + { + int num = GetKerningPairs(hdc, 0, null); + if (num > 0) + { + KERNINGPAIR[] kp = new KERNINGPAIR[num]; + GetKerningPairs(hdc, num, kp); + for (int i = 0; i < kp.Length; ++i) pairs[((char)kp[i].wFirst, (char)kp[i].wSecond)] = kp[i].iKernAmount / (double)em; + } + }); + kerning = pairs; + } + kerning.TryGetValue((left, right), out double res); + return res; + } + } + public override void GetGlyphPath(char c, bool underline, bool strikeout, out PointF[] points, out byte[] types) + { + FontStyle fs = style | (underline ? FontStyle.Underline : FontStyle.Regular) | (strikeout ? FontStyle.Strikeout : FontStyle.Regular); + using (GraphicsPath path = new GraphicsPath()) + using (StringFormat sf = (StringFormat)StringFormat.GenericTypographic.Clone()) + { + sf.LineAlignment = StringAlignment.Near; + sf.Alignment = StringAlignment.Near; + path.AddString(c.ToString(), family, (int)fs, 1.0f, new PointF(0.0f, 0.0f), sf); + if (path.PointCount == 0) + { + points = new PointF[0]; + types = new byte[0]; + } + else + { + points = path.PathPoints; + types = path.PathTypes; + } + } + } + + /// + /// Selects this face with the em height as pixel size into a device context for the duration of . + /// The device context only lives during the call, so nothing is left to a finalizer. + /// + private void WithSelectedFont(Action action) + { + IntPtr hdc = CreateCompatibleDC(IntPtr.Zero); + try + { + using (Font font = new Font(family, em, style, GraphicsUnit.Pixel)) + { + IntPtr hfont = font.ToHfont(); + IntPtr oldFont = SelectObject(hdc, hfont); + try + { + action(hdc); + } + finally + { + SelectObject(hdc, oldFont); + DeleteObject(hfont); + } + } + } + finally + { + DeleteDC(hdc); + } + } + + [StructLayout(LayoutKind.Sequential)] + private struct ABC + { + public int abcA; + public uint abcB; + public int abcC; + } + [StructLayout(LayoutKind.Sequential)] + private struct KERNINGPAIR + { + public short wFirst; + public short wSecond; + public int iKernAmount; + } + [DllImport("gdi32.dll", EntryPoint = "GetCharABCWidthsW")] + private static extern bool GetCharABCWidths(IntPtr hdc, uint uFirstChar, uint uLastChar, [Out] ABC[] lpabc); + [DllImport("gdi32.dll", EntryPoint = "GetKerningPairsW")] + private static extern int GetKerningPairs(IntPtr hdc, int cPairs, [Out] KERNINGPAIR[] lpkrnpair); + [DllImport("gdi32.dll")] + private static extern IntPtr CreateCompatibleDC(IntPtr hdc); + [DllImport("gdi32.dll")] + private static extern bool DeleteDC(IntPtr hdc); + [DllImport("gdi32.dll")] + private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); + [DllImport("gdi32.dll")] + private static extern bool DeleteObject(IntPtr hObject); + } +} diff --git a/CADability/DimensionStyle.cs b/CADability/DimensionStyle.cs index a0d7e4214..41fd15c17 100644 --- a/CADability/DimensionStyle.cs +++ b/CADability/DimensionStyle.cs @@ -1,6 +1,7 @@ using CADability.GeoObject; using CADability.UserInterface; using System; +using System.Linq; using System.Runtime.Serialization; namespace CADability.Attribute @@ -2056,9 +2057,7 @@ public override IPropertyEntry[] SubItems options.AddSubEntry(new CheckProperty(this, "DimRadiusBend", "DimensionStyle.DimRadiusBend")); ShowPropertyGroup font = new ShowPropertyGroup("DimensionStyle.Font"); - System.Drawing.FontFamily[] families = System.Drawing.FontFamily.Families; - string[] choices = new string[families.Length]; - for (int i = 0; i < families.Length; i++) choices[i] = families[i].Name; + string[] choices = FontProvider.Current.GetFamilyNames().OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase).ToArray(); MultipleChoiceProperty multipropfont = new MultipleChoiceProperty("DimensionStyle.TextFont", choices, TextFont); multipropfont.ValueChangedEvent += new ValueChangedDelegate(FontValueChanged); font.AddSubEntry(multipropfont); diff --git a/CADability/FontProvider.cs b/CADability/FontProvider.cs new file mode 100644 index 000000000..707d5cbb7 --- /dev/null +++ b/CADability/FontProvider.cs @@ -0,0 +1,316 @@ +using CADability.GeoObject; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Threading; +using Path = System.IO.Path; + +namespace CADability +{ + /// + /// Provides the fonts for and dimensions: the names of the font families, their metrics and the outlines + /// of the glyphs. The kernel gets all fonts from , which works without any user interface. The default + /// is a , which reads the TrueType font files of the system itself and therefore works on + /// every platform. CADability.Forms contains a provider based on GDI (GdiFontProvider) for applications that want the + /// fonts exactly as Windows renders them. + /// + public abstract class FontProvider + { + private static FontProvider current; + /// + /// The font provider the kernel uses. Setting it clears the cached glyphs, so that texts are rebuilt with the new + /// provider. + /// + public static FontProvider Current + { + get => LazyInitializer.EnsureInitialized(ref current, () => new ManagedFontProvider()); + set + { + current = value ?? throw new ArgumentNullException(nameof(value)); + FontCache.GlobalFontCache.Clear(); + Text.ResetFontFamilyNames(); + } + } + /// + /// The names of all font families this provider knows, e.g. for a list to choose from. + /// + public abstract string[] GetFamilyNames(); + /// + /// Returns the face of the font family (not case sensitive) that matches the requested + /// style best, or null if the provider does not know this family. + /// + /// The name of the font family, e.g. "Arial" + /// Bold requested + /// Italic requested + public abstract FontFace GetFace(string familyName, bool bold, bool italic); + /// + /// Returns the face to use for a family that does not know. The default implementation tries some + /// common sans serif fonts and then takes the first family. Returns null when there is no font at all. + /// + public virtual FontFace GetDefaultFace(bool bold, bool italic) + { + foreach (string name in new[] { "Microsoft Sans Serif", "Arial", "Liberation Sans", "DejaVu Sans", "Helvetica", "Noto Sans", "FreeSans" }) + { + FontFace face = GetFace(name, bold, italic); + if (face != null) return face; + } + string first = GetFamilyNames().OrderBy(n => n, StringComparer.OrdinalIgnoreCase).FirstOrDefault(); + return first == null ? null : GetFace(first, bold, italic); + } + } + + /// + /// A face of a font family (e.g. "Arial Bold") as a provides it. Metrics are in font units, + /// widths and kerning in units of the em height, and the glyph outlines are Bezier curves, so that all providers produce + /// the same exact geometry. + /// + public abstract class FontFace + { + /// + /// The name of the font family. + /// + public abstract string FamilyName { get; } + /// + /// True if this face is bold. + /// + public abstract bool IsBold { get; } + /// + /// True if this face is italic. + /// + public abstract bool IsItalic { get; } + /// + /// The height of the em square in font units (e.g. 2048 for most TrueType fonts). + /// + public abstract int EmHeight { get; } + /// + /// The ascent of the character cell in font units. + /// + public abstract int CellAscent { get; } + /// + /// The descent of the character cell in font units, positive downwards. + /// + public abstract int CellDescent { get; } + /// + /// The distance between the baselines of two lines in font units. + /// + public abstract int LineSpacing { get; } + /// + /// The advance width of the character in units of the em height. + /// + public abstract double GetAdvanceWidth(char c); + /// + /// The kerning between two characters in units of the em height, usually negative; 0 if there is none. + /// + public abstract double GetKerning(char left, char right); + /// + /// Returns the outline of the character in the layout of a GDI+ GraphicsPath after + /// AddString with an em size of 1 and StringFormat.GenericTypographic: in units of the em height, y + /// downwards, the top of the character cell at 0 and the baseline at /. + /// The point types are those of GDI+ (0 start, 1 line, 3 cubic Bezier, 0x80 closes the figure). Curves must be returned + /// as Bezier segments and not be flattened. Underline and strikeout are added as rectangles when requested. + /// + public abstract void GetGlyphPath(char c, bool underline, bool strikeout, out PointF[] points, out byte[] types); + } + + /// + /// The default : reads TrueType font files (.ttf, .ttc) itself, so it needs neither GDI nor any + /// other part of the operating system and works on Windows, Linux, macOS and in the browser. By default it uses the font + /// directories of the system; more files or directories can be added. OpenType fonts with CFF outlines are not supported + /// and are left out. A bold or italic face that a family does not have is not synthesized: the closest face is used. + /// + public class ManagedFontProvider : FontProvider + { + private sealed class Entry + { + public string FileName; // or + public byte[] Data; + public int Index; // in a collection + public string Family; + public bool Bold, Italic; + public ManagedFontFace Face; // loaded on demand + } + private readonly object locker = new object(); + private readonly List directories = new List(); + private readonly List added = new List(); + private Dictionary> families; // upper case family name -> faces, built on demand + + /// + /// Creates a provider for the fonts installed on the system. + /// + public ManagedFontProvider() : this(null, true) + { + } + /// + /// Creates a provider for the fonts in (with their subdirectories) and, if + /// is true, for the fonts installed on the system. + /// + public ManagedFontProvider(IEnumerable fontDirectories, bool includeSystemFonts) + { + if (fontDirectories != null) directories.AddRange(fontDirectories); + if (includeSystemFonts) directories.AddRange(SystemFontDirectories()); + } + /// + /// The directories where the fonts of the system are installed on the current platform. + /// + public static IEnumerable SystemFontDirectories() + { + string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + string fonts = Environment.GetFolderPath(Environment.SpecialFolder.Fonts); // Windows + if (!string.IsNullOrEmpty(fonts)) yield return fonts; + string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + if (!string.IsNullOrEmpty(localAppData)) yield return Path.Combine(localAppData, "Microsoft", "Windows", "Fonts"); // fonts installed per user + yield return "/usr/share/fonts"; + yield return "/usr/local/share/fonts"; + yield return "/System/Library/Fonts"; + yield return "/Library/Fonts"; + if (!string.IsNullOrEmpty(home)) + { + yield return Path.Combine(home, ".fonts"); + yield return Path.Combine(home, ".local", "share", "fonts"); + yield return Path.Combine(home, "Library", "Fonts"); + } + } + /// + /// Adds the fonts of a font file (.ttf or .ttc). + /// + public void AddFontFile(string fileName) + { + TrueTypeFont[] fonts = TrueTypeFont.ReadHeaders(fileName); + lock (locker) + { + for (int i = 0; i < fonts.Length; ++i) AddEntry(new Entry { FileName = fileName, Index = i }, fonts[i], added); + families = null; + } + } + /// + /// Adds the fonts of a font file given as its content, e.g. an embedded resource. + /// + public void AddFontData(byte[] data) + { + TrueTypeFont[] fonts = TrueTypeFont.Load(data); + lock (locker) + { + for (int i = 0; i < fonts.Length; ++i) AddEntry(new Entry { Data = data, Index = i }, fonts[i], added); + families = null; + } + } + /// + public override string[] GetFamilyNames() + { + lock (locker) + { + return Families().Values.Select(l => l[0].Family).ToArray(); + } + } + /// + public override FontFace GetFace(string familyName, bool bold, bool italic) + { + if (string.IsNullOrEmpty(familyName)) return null; + Entry best; + lock (locker) + { + if (!Families().TryGetValue(familyName.ToUpperInvariant(), out List faces)) return null; + // the exact style, then the same weight, then regular, then any face + best = faces.FirstOrDefault(f => f.Bold == bold && f.Italic == italic) + ?? faces.FirstOrDefault(f => f.Bold == bold) + ?? faces.FirstOrDefault(f => !f.Bold && !f.Italic) + ?? faces[0]; + if (best.Face == null) + { + TrueTypeFont[] fonts = best.Data != null ? TrueTypeFont.Load(best.Data) : TrueTypeFont.Load(best.FileName); + best.Face = new ManagedFontFace(fonts[best.Index]); + } + } + return best.Face; + } + + private static void AddEntry(Entry entry, TrueTypeFont font, List list) + { + if (!font.HasTrueTypeOutlines || string.IsNullOrEmpty(font.FamilyName)) return; // CFF or bitmap fonts cannot be used + entry.Family = font.FamilyName; + entry.Bold = font.IsBold; + entry.Italic = font.IsItalic; + list.Add(entry); + } + private Dictionary> Families() + { + if (families != null) return families; + List all = new List(); + HashSet seen = new HashSet(StringComparer.OrdinalIgnoreCase); + foreach (string directory in directories) + { + if (!Directory.Exists(directory)) continue; + IEnumerable files; + try + { + files = Directory.EnumerateFiles(directory, "*", SearchOption.AllDirectories).ToArray(); + } + catch (Exception e) when (e is IOException || e is UnauthorizedAccessException) + { + continue; + } + foreach (string file in files) + { + string ext = Path.GetExtension(file).ToLowerInvariant(); + if (ext != ".ttf" && ext != ".ttc") continue; + if (!seen.Add(Path.GetFullPath(file))) continue; + try + { + TrueTypeFont[] fonts = TrueTypeFont.ReadHeaders(file); + for (int i = 0; i < fonts.Length; ++i) AddEntry(new Entry { FileName = file, Index = i }, fonts[i], all); + } + catch (Exception e) when (e is IOException || e is UnauthorizedAccessException || e is InvalidDataException || e is IndexOutOfRangeException || e is ArgumentException) + { // a broken or unreadable font file is left out + } + } + } + all.AddRange(added); + families = new Dictionary>(); + foreach (Entry e in all) + { + string key = e.Family.ToUpperInvariant(); + if (!families.TryGetValue(key, out List list)) families[key] = list = new List(); + if (!list.Any(f => f.Bold == e.Bold && f.Italic == e.Italic)) list.Add(e); // the first file of a style wins + } + return families; + } + } + + /// + /// A face read by the . + /// + internal sealed class ManagedFontFace : FontFace + { + private readonly TrueTypeFont font; + private readonly object locker = new object(); + public ManagedFontFace(TrueTypeFont font) + { + this.font = font; + } + internal TrueTypeFont Font => font; + public override string FamilyName => font.FamilyName; + public override bool IsBold => font.IsBold; + public override bool IsItalic => font.IsItalic; + public override int EmHeight => font.UnitsPerEm; + public override int CellAscent => font.CellAscent; + public override int CellDescent => font.CellDescent; + public override int LineSpacing => font.LineSpacing; + public override double GetAdvanceWidth(char c) + { + return font.GetAdvanceWidth(font.GetGlyphIndex(c)) / (double)font.UnitsPerEm; + } + public override double GetKerning(char left, char right) + { + lock (locker) + { // the kerning pairs are read on the first call + return font.GetKerning(font.GetGlyphIndex(left), font.GetGlyphIndex(right)) / (double)font.UnitsPerEm; + } + } + public override void GetGlyphPath(char c, bool underline, bool strikeout, out PointF[] points, out byte[] types) + { + font.GetPathData(font.GetGlyphIndex(c), underline, strikeout, out points, out types); + } + } +} diff --git a/CADability/Text.cs b/CADability/Text.cs index 43cbaaa77..3972cf2f9 100644 --- a/CADability/Text.cs +++ b/CADability/Text.cs @@ -14,7 +14,6 @@ //using Point = System.Drawing.Point; //#endif using System.Drawing; -using System.Drawing.Drawing2D; using System.Linq; using System.Runtime.Serialization; using System.Threading; @@ -44,15 +43,7 @@ public ShowPropertyFont(Text t) private MultipleChoiceProperty InitFontList() { - FontFamily[] families = FontFamily.Families; - string[] choices = new string[families.Length]; - for (int i = 0; i < families.Length; i++) - { - choices[i] = families[i].Name; - int emS = families[i].GetEmHeight(FontStyle.Regular); - int asc = families[i].GetCellAscent(FontStyle.Regular); - int desc = families[i].GetCellDescent(FontStyle.Regular); - } + string[] choices = FontProvider.Current.GetFamilyNames().OrderBy(n => n, StringComparer.CurrentCultureIgnoreCase).ToArray(); MultipleChoiceProperty res = new MultipleChoiceProperty("Text.Font", choices, text.Font); res.ValueChangedEvent += FontChanged; return res; @@ -412,9 +403,8 @@ string IGeoObjectShowProperty.GetContextMenuId() internal class FontCache { - // Holds CompoundShapes for all characters requested so far - // and a kerning table for all fonts used so far - // Maintains a DeviceContext throughout its lifetime, where only one font is switched. + // Holds CompoundShapes for all characters requested so far + // and the font faces used so far, which come from FontProvider.Current (issue #412: no GDI here). // The unit of the font is size 1.0, allowing easy work without scaling. // However, the faces are only generated when a string is created. // It might also be possible to keep a list of faces for each character in different sizes, @@ -470,28 +460,6 @@ public override bool Equals(object obj) return false; } } - struct KerningKey - { - public readonly string fontName; - public readonly int fontStyle; - public KerningKey(string fontName, int fontStyle) - { - this.fontName = fontName; - this.fontStyle = fontStyle; - } - public override int GetHashCode() - { - return fontName.GetHashCode() ^ fontStyle; - } - public override bool Equals(object obj) - { - if (obj is KerningKey other) - { - return other.fontName == fontName && other.fontStyle == fontStyle; - } - return false; - } - } struct DictVal { public CompoundShape shape; // hat die Splines statt der Polygone @@ -516,8 +484,7 @@ struct FontCharacteristics readonly Dictionary cache; // Cache von CompundShapes und Breiten für einzelne zeichen readonly Dictionary centerLineCache; // Cache für die CenterLines, Größe 1 readonly Dictionary fontCharacteristicsCache; // Cache für die FontCharacteristics - readonly Dictionary> kerning; // Kerningtabellen - readonly IntPtr hDC; // fester DeviceContext (für die ganze Lebensdauer) + readonly Dictionary faces; // the faces of FontProvider.Current, per font name and bold/italic public FontCache() { FontPrecision = Settings.GlobalSettings.GetIntValue("Font.Precision", 1); // |grob|mittel|fein @@ -525,65 +492,50 @@ public FontCache() cache = new Dictionary(); centerLineCache = new Dictionary(); fontCharacteristicsCache = new Dictionary(); - kerning = new Dictionary>(); - hDC = Gdi.CreateCompatibleDC(IntPtr.Zero); + faces = new Dictionary(); } - ~FontCache() + /// + /// Returns the face of for a font name and a font style (the flags of + /// System.Drawing.FontStyle: 1 bold, 2 italic, 4 underline, 8 strikeout). An unknown font name gives the default face of + /// the provider. + /// + /// when the provider has no font at all + internal FontFace GetFace(string fontName, int fontStyle) { - // the constructor throws where there is no GDI (e.g. on Linux), and an exception in a finalizer ends the process - if (hDC != IntPtr.Zero) Gdi.DeleteDC(hDC); + bool bold = (fontStyle & 1) != 0, italic = (fontStyle & 2) != 0; + FontKey key = new FontKey(fontName ?? "", fontStyle & 3); + lock (faces) + { + if (faces.TryGetValue(key, out FontFace face)) return face; + FontProvider provider = FontProvider.Current; + face = provider.GetFace(fontName, bold, italic) ?? provider.GetDefaultFace(bold, italic); + if (face == null) throw new InvalidOperationException("No font is available for texts. Install TrueType fonts, add font files to a ManagedFontProvider or set FontProvider.Current."); + faces[key] = face; + return face; + } } - public Path2D[] GetOutline2D(string fontName, int fontStyle, char c, out double width) + /// + /// Like , but returns null instead of throwing when there is no font at all: painting and the + /// extent of a text leave the text out then, instead of failing. + /// + internal FontFace TryGetFace(string fontName, int fontStyle) { - using GraphicsPath path = new GraphicsPath(); - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = Text.FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - StringFormat sf = StringFormat.GenericTypographic.Clone() as StringFormat; - sf.LineAlignment = StringAlignment.Near; - sf.Alignment = StringAlignment.Near; - if (!ff.IsStyleAvailable((FontStyle)fontStyle)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fontStyle = (int)FontStyle.Regular; - if (ff.IsStyleAvailable(FontStyle.Bold)) fontStyle = (int)FontStyle.Bold; - if (ff.IsStyleAvailable(FontStyle.Italic)) fontStyle = (int)FontStyle.Italic; - if (ff.IsStyleAvailable(FontStyle.Strikeout)) fontStyle = (int)FontStyle.Strikeout; - if (ff.IsStyleAvailable(FontStyle.Underline)) fontStyle = (int)FontStyle.Underline; - } - int fs = fontStyle; - int em = ff.GetEmHeight((FontStyle)fs); - if (em == 0) em = 1000; - using Font font = new Font(ff, em, (FontStyle)fs, GraphicsUnit.Pixel); - IntPtr hfont = font.ToHfont(); - IntPtr oldfont = Gdi.SelectObject(hDC, hfont); - Gdi.ABC[] abc = new Gdi.ABC[1]; - Gdi.GetCharABCWidths(hDC, (uint)c, (uint)c, abc); - //int[] widths = new int[1]; // liefert das selbe wie GetCharABCWidths, nur die Summe halt. - //Gdi.GetCharWidth32(hDC, (uint)c, (uint)c, widths); - if (!kerning.ContainsKey(new KerningKey(fontName, fontStyle))) - { - KerningKey kk = new KerningKey(fontName, fontStyle); - Dictionary<(char Left, char Right), double> pairs = new Dictionary<(char Left, char Right), double>(); - kerning[kk] = pairs; - int num = Gdi.GetKerningPairs(hDC, 0, null); - if (num > 0) - { - Gdi.KERNINGPAIR[] kp = new Gdi.KERNINGPAIR[num]; - int ok = Gdi.GetKerningPairs(hDC, num, kp); - for (int i = 0; i < kp.Length; ++i) - { - pairs[((char)kp[i].wFirst, (char)kp[i].wSecond)] = kp[i].iKernAmount / (double)em; - } - } + try + { + return GetFace(fontName, fontStyle); + } + catch (InvalidOperationException) + { + return null; } - Gdi.SelectObject(hDC, oldfont); - Gdi.DeleteObject(hfont); - - width = (abc[0].abcA + abc[0].abcB + abc[0].abcC) / (double)em; - - path.AddString(c.ToString(), ff, fs, 1.0f, new PointF(0.0f, 0.0f), sf); - if (path.PointCount == 0) return new Path2D[0]; - return GlyphShapeBuilder.PathDataToPath2D(path.PathPoints, path.PathTypes, FontPrecision); + } + public Path2D[] GetOutline2D(string fontName, int fontStyle, char c, out double width) + { + FontFace face = GetFace(fontName, fontStyle); + width = face.GetAdvanceWidth(c); + face.GetGlyphPath(c, (fontStyle & 4) != 0, (fontStyle & 8) != 0, out PointF[] points, out byte[] types); + if (points.Length == 0) return new Path2D[0]; + return GlyphShapeBuilder.PathDataToPath2D(points, types, FontPrecision); } public CompoundShape Get(string font, int fontStyle, char c, out double width) { // soll verschwinden!! @@ -606,13 +558,7 @@ public CompoundShape Get(string font, int fontStyle, char c, out double width) } public double GetKerning(string font, int fontStyle, char a, char b) { - Dictionary<(char Left, char Right), double> pairs; - double res = 0.0; - if (kerning.TryGetValue(new KerningKey(font, fontStyle), out pairs)) - { - pairs.TryGetValue((a, b), out res); - } - return res; + return GetFace(font, fontStyle).GetKerning(a, b); } public IPaintTo3DList Get(string font, int fontStyle, char c, out double width, IPaintTo3D paintTo3D) { @@ -794,7 +740,13 @@ private static bool PaintGlyphFaces(Path2D[] paths, IPaintTo3D paintTo3D) } public void Clear() { - cache.Clear(); + lock (this) + { + cache.Clear(); + centerLineCache.Clear(); + fontCharacteristicsCache.Clear(); + } + lock (faces) faces.Clear(); } public GeoObjectList GetCenterLines(string font, int fontStyle, char c, out double width) { @@ -971,9 +923,16 @@ public enum LineAlignMode private LineAlignMode lineAlignment; // private IGeoObject[] cachedDisplayItems; private static HashSet fontFamilyNames; + /// + /// The upper case names of the font families of . + /// internal static HashSet FontFamilyNames => LazyInitializer.EnsureInitialized(ref fontFamilyNames, () => - new HashSet(FontFamily.Families.Select(f => f.Name.ToUpper()))); + new HashSet(FontProvider.Current.GetFamilyNames().Select(f => f.ToUpper()))); + internal static void ResetFontFamilyNames() + { + fontFamilyNames = null; + } #region polymorph construction /// @@ -1019,37 +978,28 @@ private void calcExtent() { try { - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - FontStyle fs = fontStyle; - if (!ff.IsStyleAvailable(fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = FontStyle.Underline; // irgend einen muss es ja wohl geben, oder? - } - int em = ff.GetEmHeight(fs); - int asc = ff.GetCellAscent(fs); - int desc = ff.GetCellDescent(fs); - int lsp = ff.GetLineSpacing(fs); - double dbg = (double)(asc + desc) / (double)lsp; + int fs = (int)fontStyle; + FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 + int em = ff.EmHeight; + int asc = ff.CellAscent; + int desc = ff.CellDescent; + int lsp = ff.LineSpacing; // asc+desc kann mehr als em sein. ich nehme jedoch an, dass em für die Größe ausschlaggebend ist // wenn man em als 1 annimmt, dann muss man um desc/em nach unten verschieben um auf die Grundlinie zu kommen emSizeFactor = (double)(asc + desc) / (double)em; emAscDescFactor = (double)(desc) / (double)asc; emDescDiff = (double)(em - asc) / (double)em; this.desc = (double)(desc) / (double)em; - // the bitmap only serves as a device for measuring; it must be disposed as well, otherwise - // 400 KB of unmanaged GDI+ memory stay alive per measured text until the finalizer runs - using (Bitmap measureBitmap = new Bitmap(1000, 100)) - using (Graphics graphics = Graphics.FromImage(measureBitmap)) - using (Font font = new Font(fontName, (float)(glyphDirection.Length), GraphicsUnit.Pixel)) + // the size of the text string with the font size glyphDirection.Length: the advance widths and the kerning + // of the characters and the line spacing (this used to be Graphics.MeasureString of GDI+) + double width = 0.0; + for (int i = 0; i < textString.Length; ++i) { - sizeExtent = graphics.MeasureString(textString, font); + width += ff.GetAdvanceWidth(textString[i]); + if (i > 0) width += ff.GetKerning(textString[i - 1], textString[i]); } + double size = glyphDirection.Length; + sizeExtent = new SizeF((float)(width * size), (float)(size * lsp / em)); isValidExtent = true; } catch (Exception e) @@ -1268,22 +1218,12 @@ public virtual GeoObjectList GetOutline() if (Bold) fs |= (int)FontStyle.Bold; if (Strikeout) fs |= (int)FontStyle.Strikeout; PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - if (!ff.IsStyleAvailable((FontStyle)fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = (int)FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = (int)FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = (int)FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = (int)FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = (int)FontStyle.Underline; // irgend einen muss es ja wohl geben, oder? - } + FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 string txt = GetDisplayString(fs); - int em = ff.GetEmHeight((FontStyle)fs); - int ls = ff.GetLineSpacing((FontStyle)fs); - int dc = ff.GetCellDescent((FontStyle)fs); - int ac = ff.GetCellAscent((FontStyle)fs); + int em = ff.EmHeight; + int ls = ff.LineSpacing; + int dc = ff.CellDescent; + int ac = ff.CellAscent; double lsp = ls / em; if (lineSpacing > 0.0) lsp = lineSpacing; // explicit baseline distance as factor of the text size double dx = 0.0; @@ -1425,22 +1365,12 @@ public CompoundShape[] GetShapes() if (Bold) fs |= (int)FontStyle.Bold; if (Strikeout) fs |= (int)FontStyle.Strikeout; PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - if (!ff.IsStyleAvailable((FontStyle)fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = (int)FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = (int)FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = (int)FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = (int)FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = (int)FontStyle.Underline; // irgend einen muss es ja wohl geben, oder? - } + FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 string txt = GetDisplayString(fs); - int em = ff.GetEmHeight((FontStyle)fs); - int ls = ff.GetLineSpacing((FontStyle)fs); - int dc = ff.GetCellDescent((FontStyle)fs); - int ac = ff.GetCellAscent((FontStyle)fs); + int em = ff.EmHeight; + int ls = ff.LineSpacing; + int dc = ff.CellDescent; + int ac = ff.CellAscent; double lsp = ls / em; if (lineSpacing > 0.0) lsp = lineSpacing; // explicit baseline distance as factor of the text size double dx = 0.0; @@ -1568,22 +1498,12 @@ public virtual GeoObjectList GetCenterline() if (Bold) fs |= (int)FontStyle.Bold; if (Strikeout) fs |= (int)FontStyle.Strikeout; PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - if (!ff.IsStyleAvailable((FontStyle)fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = (int)FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = (int)FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = (int)FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = (int)FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = (int)FontStyle.Underline; // irgend einen muss es ja wohl geben, oder? - } + FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 string txt = GetDisplayString(fs); - int em = ff.GetEmHeight((FontStyle)fs); - int ls = ff.GetLineSpacing((FontStyle)fs); - int dc = ff.GetCellDescent((FontStyle)fs); - int ac = ff.GetCellAscent((FontStyle)fs); + int em = ff.EmHeight; + int ls = ff.LineSpacing; + int dc = ff.CellDescent; + int ac = ff.CellAscent; double lsp = ls / em; if (lineSpacing > 0.0) lsp = lineSpacing; // explicit baseline distance as factor of the text size double dx = 0.0; @@ -1972,22 +1892,13 @@ private void CalcExtent() { // bei sehr kleinen lineDirection u.s.w. gibts hier ein Problem pls = new PlaneSurface(location, GeoVector.XAxis, GeoVector.YAxis, GeoVector.ZAxis); } - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - if (!ff.IsStyleAvailable((FontStyle)fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = (int)FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = (int)FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = (int)FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = (int)FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = (int)FontStyle.Underline; // irgend einen muss es ja wohl geben, oder? - } + FontFace ff = FontCache.GlobalFontCache.TryGetFace(fontName, fs); // FontProvider.Current, see issue #412 + if (ff == null) return; // no font at all: nothing to paint or measure string txt = GetDisplayString(fs); - int em = ff.GetEmHeight((FontStyle)fs); - int ls = ff.GetLineSpacing((FontStyle)fs); - int dc = ff.GetCellDescent((FontStyle)fs); - int ac = ff.GetCellAscent((FontStyle)fs); + int em = ff.EmHeight; + int ls = ff.LineSpacing; + int dc = ff.CellDescent; + int ac = ff.CellAscent; double lsp; if (em > 0) lsp = (double)ls / em; else lsp = 1.0; @@ -2612,21 +2523,12 @@ public override void PrePaintTo3D(IPaintTo3D paintTo3D) if (Strikeout) fs |= (int)FontStyle.Strikeout; List res = new List(); PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - if (!ff.IsStyleAvailable((FontStyle)fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = (int)FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = (int)FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = (int)FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = (int)FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = (int)FontStyle.Underline; // there must be one - } - int em = ff.GetEmHeight((FontStyle)fs); - int ls = ff.GetLineSpacing((FontStyle)fs); - int dc = ff.GetCellDescent((FontStyle)fs); - int ac = ff.GetCellAscent((FontStyle)fs); + FontFace ff = FontCache.GlobalFontCache.TryGetFace(fontName, fs); // FontProvider.Current, see issue #412 + if (ff == null) return; // no font at all: nothing to paint or measure + int em = ff.EmHeight; + int ls = ff.LineSpacing; + int dc = ff.CellDescent; + int ac = ff.CellAscent; for (int i = 0; i < textString.Length; ++i) { double width; @@ -2685,22 +2587,13 @@ public override void PaintTo3D(IPaintTo3D paintTo3D) if (Strikeout) fs |= (int)FontStyle.Strikeout; List res = new List(); PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); - // FontFamily wraps an unmanaged GDI+ object: dispose it at the end of the block instead of - // leaving it to the finalizer (this code runs for every text on every repaint) - using FontFamily ff = FontFamilyNames.Contains(fontName.ToUpper()) ? new FontFamily(fontName) : new FontFamily(System.Drawing.Text.GenericFontFamilies.SansSerif); - if (!ff.IsStyleAvailable((FontStyle)fs)) - { - if (ff.IsStyleAvailable(FontStyle.Regular)) fs = (int)FontStyle.Regular; - else if (ff.IsStyleAvailable(FontStyle.Bold)) fs = (int)FontStyle.Bold; - else if (ff.IsStyleAvailable(FontStyle.Italic)) fs = (int)FontStyle.Italic; - else if (ff.IsStyleAvailable(FontStyle.Strikeout)) fs = (int)FontStyle.Strikeout; - else if (ff.IsStyleAvailable(FontStyle.Underline)) fs = (int)FontStyle.Underline; // irgend einen muss es ja wohl geben, oder? - } + FontFace ff = FontCache.GlobalFontCache.TryGetFace(fontName, fs); // FontProvider.Current, see issue #412 + if (ff == null) return; // no font at all: nothing to paint or measure string txt = GetDisplayString(fs); - int em = ff.GetEmHeight((FontStyle)fs); - int ls = ff.GetLineSpacing((FontStyle)fs); - int dc = ff.GetCellDescent((FontStyle)fs); - int ac = ff.GetCellAscent((FontStyle)fs); + int em = ff.EmHeight; + int ls = ff.LineSpacing; + int dc = ff.CellDescent; + int ac = ff.CellAscent; double lsp = ls / em; if (lineSpacing > 0.0) lsp = lineSpacing; // explicit baseline distance as factor of the text size if (lineSpacing > 0.0) lsp = lineSpacing; // explicit baseline distance as factor of the text size diff --git a/CADability/TrueTypeFont.cs b/CADability/TrueTypeFont.cs index 9ad4c2369..7428e83de 100644 --- a/CADability/TrueTypeFont.cs +++ b/CADability/TrueTypeFont.cs @@ -64,6 +64,20 @@ internal sealed class TrueTypeFont public int WinAscent { get; } public int WinDescent { get; } /// + /// The top of the underline above the baseline in font units (negative: below), from the "post" table. + /// + public int UnderlinePosition { get; } + public int UnderlineThickness { get; } + /// + /// The top of the strikeout line above the baseline in font units, from the "OS/2" table. + /// + public int StrikeoutPosition { get; } + public int StrikeoutSize { get; } + /// + /// True for a font read by : it knows its names, style and metrics, but has no glyphs. + /// + public bool IsHeaderOnly { get; private set; } + /// /// False for an OpenType font with CFF outlines and for a bitmap font, whose glyphs cannot be read. /// public bool HasTrueTypeOutlines { get; } @@ -123,14 +137,26 @@ private TrueTypeFont(byte[] data, int offset) bool bold = (macStyle & 1) != 0, italic = (macStyle & 2) != 0; WinAscent = Ascender; WinDescent = -Descender; + // defaults like the ones GDI uses when the tables are missing + UnderlineThickness = Math.Max(1, UnitsPerEm / 14); + UnderlinePosition = -UnitsPerEm / 10; + StrikeoutSize = UnderlineThickness; + StrikeoutPosition = UnitsPerEm / 4; if (tables.TryGetValue("OS/2", out var os2) && os2.length >= 78) { + StrikeoutSize = S16(os2.offset + 26); + StrikeoutPosition = S16(os2.offset + 28); int fsSelection = U16(os2.offset + 62); italic = (fsSelection & 1) != 0; bold = (fsSelection & 0x20) != 0; WinAscent = U16(os2.offset + 74); WinDescent = U16(os2.offset + 76); } + if (tables.TryGetValue("post", out var post) && post.length >= 12) + { + UnderlinePosition = S16(post.offset + 8); + UnderlineThickness = S16(post.offset + 10); + } IsBold = bold; IsItalic = italic; ReadNames(out string family, out string subfamily, out string full, out string typographicFamily); @@ -160,6 +186,91 @@ public static TrueTypeFont[] Load(byte[] data) return new TrueTypeFont[] { new TrueTypeFont(data, 0) }; } /// + /// Reads the names, styles and metrics of all fonts of a font file without reading the whole file: only the tables + /// "head", "hhea", "maxp", "OS/2", "post" and "name" are read. This is how a directory with hundreds of fonts is indexed + /// quickly. The fonts returned have set; load the file with to + /// get the glyphs. + /// + public static TrueTypeFont[] ReadHeaders(string fileName) + { + using (FileStream stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read)) + { + byte[] head = ReadBytes(stream, 0, 12); + if (head[0] == 't' && head[1] == 't' && head[2] == 'c' && head[3] == 'f') + { + int count = (int)ReadU32(head, 8); + if (count <= 0 || count > 10000) throw new InvalidDataException("invalid font collection"); + byte[] offsets = ReadBytes(stream, 12, 4 * count); + TrueTypeFont[] res = new TrueTypeFont[count]; + for (int i = 0; i < count; ++i) res[i] = ReadHeader(stream, ReadU32(offsets, 4 * i)); + return res; + } + return new TrueTypeFont[] { ReadHeader(stream, 0) }; + } + } + private static TrueTypeFont ReadHeader(FileStream stream, uint offset) + { + // a small font made of the needed tables only, with the same table directory layout, so that the constructor reads it + byte[] directory = ReadBytes(stream, offset, 12); + int numTables = (directory[4] << 8) | directory[5]; + if (numTables > 1000) throw new InvalidDataException("invalid table directory"); + byte[] records = ReadBytes(stream, offset + 12, 16 * numTables); + string[] needed = { "head", "hhea", "maxp", "OS/2", "post", "name" }; + List<(byte[] record, byte[] content)> copied = new List<(byte[], byte[])>(); + for (int i = 0; i < numTables; ++i) + { + string tag = Encoding.ASCII.GetString(records, 16 * i, 4); + byte[] record = new byte[16]; + Array.Copy(records, 16 * i, record, 0, 16); + if (Array.IndexOf(needed, tag) >= 0) + { + uint length = ReadU32(records, 16 * i + 12); + if (length > stream.Length) throw new InvalidDataException("table " + tag + " exceeds the file"); + copied.Add((record, ReadBytes(stream, ReadU32(records, 16 * i + 8), (int)length))); + } + else if (tag == "glyf" || tag == "loca") + { // only their presence matters (HasTrueTypeOutlines) + for (int k = 8; k < 16; ++k) record[k] = 0; + copied.Add((record, new byte[0])); + } + } + int size = 12 + 16 * copied.Count; + foreach (var (_, content) in copied) size += content.Length; + byte[] data = new byte[size]; + Array.Copy(directory, data, 12); + data[4] = (byte)(copied.Count >> 8); + data[5] = (byte)copied.Count; + int pos = 12 + 16 * copied.Count; + for (int i = 0; i < copied.Count; ++i) + { + byte[] record = copied[i].record, content = copied[i].content; + if (content.Length > 0) + { + record[8] = (byte)(pos >> 24); record[9] = (byte)(pos >> 16); record[10] = (byte)(pos >> 8); record[11] = (byte)pos; + } + Array.Copy(record, 0, data, 12 + 16 * i, 16); + Array.Copy(content, 0, data, pos, content.Length); + pos += content.Length; + } + TrueTypeFont res = new TrueTypeFont(data, 0); + res.IsHeaderOnly = true; + return res; + } + private static byte[] ReadBytes(FileStream stream, long offset, int count) + { + if (count < 0 || offset + count > stream.Length) throw new InvalidDataException("unexpected end of the font file"); + byte[] res = new byte[count]; + stream.Seek(offset, SeekOrigin.Begin); + int read = 0; + while (read < count) + { + int n = stream.Read(res, read, count - read); + if (n <= 0) throw new InvalidDataException("unexpected end of the font file"); + read += n; + } + return res; + } + /// /// Reads all fonts of the font file , see . /// public static TrueTypeFont[] Load(string fileName) @@ -213,6 +324,7 @@ public int GetKerning(int leftGlyph, int rightGlyph) public List GetContours(int glyph) { if (!HasTrueTypeOutlines) throw new NotSupportedException("The outlines of the font \"" + FullName + "\" cannot be read: they are in CFF format or the font has bitmaps only"); + if (IsHeaderOnly) throw new InvalidOperationException("The font \"" + FullName + "\" was read by ReadHeaders and has no glyphs, load it with Load"); List res = new List(); AddContours(glyph, res, 0); return res; @@ -226,6 +338,14 @@ public List GetContours(int glyph) /// 0 start, 1 line, 3 Bezier, 0x80 closes the figure. This is the input of GlyphShapeBuilder.PathDataToPath2D. /// public void GetPathData(int glyph, out PointF[] points, out byte[] types) + { + GetPathData(glyph, false, false, out points, out types); + } + /// + /// Like , and adds the underline and the strikeout line as + /// rectangles over the advance width of the glyph, as GDI+ does for an underlined or struck out font. + /// + public void GetPathData(int glyph, bool underline, bool strikeout, out PointF[] points, out byte[] types) { List pts = new List(); List tps = new List(); @@ -308,6 +428,17 @@ public void GetPathData(int glyph, out PointF[] points, out byte[] types) } tps[tps.Count - 1] |= 0x80; } + void AddBar(int top, int thickness) + { + if (thickness <= 0) return; + double width = GetAdvanceWidth(glyph); + pts.Add(Map(0, top)); tps.Add(0); + pts.Add(Map(width, top)); tps.Add(1); + pts.Add(Map(width, top - thickness)); tps.Add(1); + pts.Add(Map(0, top - thickness)); tps.Add(0x81); + } + if (underline) AddBar(UnderlinePosition, UnderlineThickness); + if (strikeout) AddBar(StrikeoutPosition, StrikeoutSize); points = pts.ToArray(); types = tps.ToArray(); } diff --git a/tests/CADability.ImportTests/CADability.ImportTests.csproj b/tests/CADability.ImportTests/CADability.ImportTests.csproj index 3ab5a792d..bdea3af5c 100644 --- a/tests/CADability.ImportTests/CADability.ImportTests.csproj +++ b/tests/CADability.ImportTests/CADability.ImportTests.csproj @@ -13,6 +13,13 @@ + + + + PreserveNewest + + + PreserveNewest diff --git a/tests/CADability.ImportTests/DxfDimensionTest.cs b/tests/CADability.ImportTests/DxfDimensionTest.cs index 6cc380d30..a24bca2ef 100644 --- a/tests/CADability.ImportTests/DxfDimensionTest.cs +++ b/tests/CADability.ImportTests/DxfDimensionTest.cs @@ -367,7 +367,6 @@ public void import_dxf_dimension_with_missing_block_is_drawn_from_its_definition [TestMethod] public void import_dxf_dimension_without_block_shows_its_measurement() { - RequireGdi(); string dxf = Header + Tables + EntitiesStart + AlignedDimension(null) + Epilogue; Block block = AssertRegeneratedDimension(ImportDxf(dxf).GetActiveModel()); Text text = block.Children.OfType().SingleOrDefault(); @@ -759,7 +758,6 @@ public void export_dimension_writes_its_xdata() [TestMethod] public void export_dimension_round_trips_as_a_dxf_dimension() { - RequireGdi(); Project project = MakeProjectWithDimension(new GeoPoint(0, 0, 0), new GeoPoint(100, 0, 0)); Model reimported = WithChoice("DxfDwg.ExportDimension", 1, () => ExportAndImport(project)); @@ -1607,13 +1605,6 @@ private static Block SingleDimensionBlock(Model model) return block; } - // Adding a Text to a model computes its extent through GDI, which only exists on Windows. - private static void RequireGdi() - { - if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) - Assert.Inconclusive("text needs GDI, which is only available on Windows"); - } - private Project ImportDxf(string dxf) { string file = this.TestContext.TestName + ".dxf"; diff --git a/tests/CADability.ImportTests/DxfOcsExportTest.cs b/tests/CADability.ImportTests/DxfOcsExportTest.cs index 5e436801b..3f81fd850 100644 --- a/tests/CADability.ImportTests/DxfOcsExportTest.cs +++ b/tests/CADability.ImportTests/DxfOcsExportTest.cs @@ -90,7 +90,6 @@ public void export_dxf_tilted_circle_and_arc_keep_position() [TestMethod] public void export_dxf_flipped_text_keeps_position() { - RequireGdi(); foreach (bool aligned in new[] { false, true }) { Text text = MakeText("Flipped", new GeoPoint(10, 5, 2)); @@ -119,7 +118,6 @@ public void export_dxf_flipped_text_keeps_position() [TestMethod] public void export_dxf_tilted_text_keeps_position() { - RequireGdi(); Text text = MakeText("Tilted", new GeoPoint(-3, 8, 1)); text.Modify(ModOp.Rotate(new GeoPoint(2, 3, 4), new GeoVector(1, 1, 0.3), new SweepAngle(0.65))); AssertSameText(text, ReimportSingle(Export(text))); @@ -130,7 +128,6 @@ public void export_dxf_tilted_text_keeps_position() [TestMethod] public void export_dxf_flipped_and_tilted_mtext_keep_position() { - RequireGdi(); Text text = MakeText("first line\nsecond line", new GeoPoint(10, 5, 2)); text.Modify(ModOp.Rotate(GeoPoint.Origin, GeoVector.XAxis, new SweepAngle(Math.PI))); string file = Export(text); @@ -151,13 +148,6 @@ public void export_dxf_flipped_and_tilted_mtext_keep_position() // --- helpers ------------------------------------------------------------------------- - // Adding a Text to a model computes its extent through GDI, which only exists on Windows. - private static void RequireGdi() - { - if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) - Assert.Inconclusive("text needs GDI, which is only available on Windows"); - } - private static Text MakeText(string value, GeoPoint location) { Text text = Text.Construct(); diff --git a/tests/CADability.ImportTests/TestFonts.cs b/tests/CADability.ImportTests/TestFonts.cs new file mode 100644 index 000000000..1be6eb083 --- /dev/null +++ b/tests/CADability.ImportTests/TestFonts.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CADability.ImportTests +{ + /// + /// Texts get their fonts from FontProvider.Current. The tests add Liberation Sans (Files/Fonts) to the fonts of the system, + /// so that texts have a font on every machine, also on a build server without any fonts installed (issue #412). + /// + [TestClass] + public static class TestFonts + { + public static string Directory => System.IO.Path.Combine(AppContext.BaseDirectory, "Files", "Fonts"); + + [AssemblyInitialize] + public static void UseTestFonts(TestContext context) + { + FontProvider.Current = new ManagedFontProvider(new[] { Directory }, true); + } + } +} diff --git a/tests/CADability.Tests/FontCacheGlyphTests.cs b/tests/CADability.Tests/FontCacheGlyphTests.cs index d0eaf1156..04f64367b 100644 --- a/tests/CADability.Tests/FontCacheGlyphTests.cs +++ b/tests/CADability.Tests/FontCacheGlyphTests.cs @@ -7,7 +7,8 @@ namespace CADability.Tests { /// /// Sends all printable ASCII characters of installed fonts through - /// and checks that every glyph with a closed outline is painted with triangles. Needs Windows (GDI) and the font. + /// and checks that every glyph with a closed outline is painted with triangles. Needs the font: Liberation Sans comes with + /// the tests (see ), the others are Windows fonts. /// [TestClass] public class FontCacheGlyphTests @@ -105,7 +106,6 @@ public void SetClip(Rectangle clipRectangle) { } private static string CheckFontAvailable(string fontName) { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return "glyph outlines need GDI, which is only available on Windows"; if (!GeoObject.Text.FontFamilyNames.Contains(fontName.ToUpper())) { // name the installed families that start with the same word, which shows a misspelled or a wrong instance name @@ -145,6 +145,7 @@ private static (List withoutTriangles, List withoutList) CheckFont(s } [DataTestMethod] + [DataRow("Liberation Sans")] [DataRow("Arial")] [DataRow("Calibri")] [DataRow("Bahnschrift")] @@ -179,18 +180,17 @@ public void ReportGlyphsWithoutTrianglesForAllInstalledFonts() { if (!System.Diagnostics.Debugger.IsAttached && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CADABILITY_FONT_SURVEY"))) Assert.Inconclusive("survey over all installed fonts, takes a few minutes: start it with Debug Test or set CADABILITY_FONT_SURVEY=1"); - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) Assert.Inconclusive("glyph outlines need GDI, which is only available on Windows"); int fontsWithProblems = 0; - foreach (FontFamily ff in FontFamily.Families) + foreach (string family in FontProvider.Current.GetFamilyNames()) { try { - var (withoutTriangles, withoutList) = CheckFont(ff.Name, (int)FontStyle.Regular, TestContext); + var (withoutTriangles, withoutList) = CheckFont(family, (int)FontStyle.Regular, TestContext); if (withoutTriangles.Count + withoutList.Count > 0) ++fontsWithProblems; } catch (Exception ex) { - TestContext.WriteLine("{0}: exception {1}", ff.Name, ex.Message); + TestContext.WriteLine("{0}: exception {1}", family, ex.Message); ++fontsWithProblems; } } diff --git a/tests/CADability.Tests/FontProviderTests.cs b/tests/CADability.Tests/FontProviderTests.cs new file mode 100644 index 000000000..8e2b7be03 --- /dev/null +++ b/tests/CADability.Tests/FontProviderTests.cs @@ -0,0 +1,170 @@ +using CADability.Curve2D; +using CADability.GeoObject; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Runtime.CompilerServices; +using Path = System.IO.Path; + +namespace CADability.Tests +{ + /// + /// Texts get their fonts from , a static service of the kernel, and no longer from GDI or + /// the frame (issue #412, step 6): they work without a frame and on every platform. The tests that replace the provider + /// do not run in parallel with other tests and restore the provider of . + /// + [TestClass] + public class FontProviderTests + { + private static string FontDirectory([CallerFilePath] string thisFile = "") + => Path.Combine(Path.GetDirectoryName(thisFile), "Files", "Fonts"); + + private static Text MakeText(string font, string textString, double size) + { + Text text = Text.Construct(); + text.Font = font; + text.TextString = textString; + text.Location = GeoPoint.Origin; + text.LineDirection = GeoVector.XAxis; + text.GlyphDirection = GeoVector.YAxis; + text.TextSize = size; + return text; + } + private static void WithProvider(FontProvider provider, Action action) + { + try + { + FontProvider.Current = provider; + action(); + } + finally + { + TestFonts.UseTestFonts(null); + } + } + + [TestMethod] + [DoNotParallelize] + public void TextWorksWithoutFrameAndWithoutGdi() + { + Assert.IsNull(FrameImpl.MainFrame, "the tests run without a frame"); + WithProvider(new ManagedFontProvider(new[] { FontDirectory() }, false), () => + { + Text text = MakeText("Liberation Sans", "Hello", 10.0); + GeoObjectList outline = text.GetOutline(); + Assert.IsTrue(outline.Count > 0, "the text has an outline"); + BoundingCube ext = text.GetBoundingCube(); + Assert.IsFalse(ext.IsEmpty); + + // the width of the text is the sum of the advance widths and the kerning of the font + TrueTypeFont font = TrueTypeFont.Load(Path.Combine(FontDirectory(), "LiberationSans-Regular.ttf"))[0]; + double advance = 0.0; + string s = "Hello"; + for (int i = 0; i < s.Length; ++i) + { + advance += font.GetAdvanceWidth(font.GetGlyphIndex(s[i])); + if (i > 0) advance += font.GetKerning(font.GetGlyphIndex(s[i - 1]), font.GetGlyphIndex(s[i])); + } + double width = 10.0 * advance / font.UnitsPerEm; + Assert.AreEqual(width, text.GetBoundingCube().Xmax - text.GetBoundingCube().Xmin, 0.15 * 10.0, "the extent is about the advance width (the glyphs do not fill their whole advance)"); + }); + } + + [TestMethod] + [DoNotParallelize] + public void UnknownFontUsesTheDefaultFace() + { + WithProvider(new ManagedFontProvider(new[] { FontDirectory() }, false), () => + { + FontFace face = FontCache.GlobalFontCache.GetFace("A Font That Does Not Exist", 0); + Assert.AreEqual("Liberation Sans", face.FamilyName); + Text known = MakeText("Liberation Sans", "Ag", 1.0), unknown = MakeText("A Font That Does Not Exist", "Ag", 1.0); + Assert.AreEqual(known.GetOutline().GetExtent().Size, unknown.GetOutline().GetExtent().Size, 1e-9); + }); + } + + [TestMethod] + [DoNotParallelize] + public void WithoutAnyFontTheExceptionSaysWhatToDo() + { + string empty = Path.Combine(Path.GetTempPath(), "CADability-no-fonts-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(empty); + try + { + WithProvider(new ManagedFontProvider(new[] { empty }, false), () => + { + InvalidOperationException ex = Assert.ThrowsException(() => FontCache.GlobalFontCache.GetFace("Arial", 0)); + StringAssert.Contains(ex.Message, "FontProvider.Current"); + // the extent and the glyphs for painting are empty instead of failing, an explicit request for the outline throws + FontCache.GlobalFontCache.Get("Arial", 0, 'A', out double width, null); + Text text = MakeText("Arial", "no font", 1.0); + text.GetBoundingCube(); + Assert.ThrowsException(() => text.GetOutline()); + }); + } + finally + { + Directory.Delete(empty, true); + } + } + + [TestMethod] + [DoNotParallelize] + public void UnderlineAndStrikeoutAddBars() + { + WithProvider(new ManagedFontProvider(new[] { FontDirectory() }, false), () => + { + Path2D[] plain = FontCache.GlobalFontCache.GetOutline2D("Liberation Sans", 0, 'o', out double w1); + Path2D[] underlined = FontCache.GlobalFontCache.GetOutline2D("Liberation Sans", 4, 'o', out double w2); + Path2D[] both = FontCache.GlobalFontCache.GetOutline2D("Liberation Sans", 4 | 8, 'o', out double w3); + Assert.AreEqual(plain.Length + 1, underlined.Length); + Assert.AreEqual(plain.Length + 2, both.Length); + Assert.AreEqual(w1, w2); + // the underline is below the baseline (y = 1 - CellAscent/em after PathDataToPath2D) + FontFace face = FontCache.GlobalFontCache.GetFace("Liberation Sans", 0); + double baseline = 1.0 - face.CellAscent / (double)face.EmHeight; + Assert.IsTrue(underlined.Last().GetExtent().Top < baseline); + }); + } + + [TestMethod] + [DoNotParallelize] + public void MissingStyleFallsBackToAnExistingFace() + { + ManagedFontProvider provider = new ManagedFontProvider(new[] { FontDirectory() }, false); + CollectionAssert.AreEqual(new[] { "Liberation Sans" }, provider.GetFamilyNames()); + FontFace bold = provider.GetFace("liberation sans", true, false); + Assert.IsNotNull(bold, "the family name is not case sensitive"); + Assert.IsFalse(bold.IsBold, "only the regular face is there"); + Assert.IsNull(provider.GetFace("Arial", false, false)); + Assert.AreSame(bold, provider.GetFace("Liberation Sans", false, false), "faces are loaded once"); + } + + [TestMethod] + public void HeadersAreReadWithoutLoadingTheGlyphs() + { + string file = Path.Combine(FontDirectory(), "LiberationSans-Regular.ttf"); + TrueTypeFont header = TrueTypeFont.ReadHeaders(file).Single(), full = TrueTypeFont.Load(file).Single(); + Assert.IsTrue(header.IsHeaderOnly); + Assert.IsFalse(full.IsHeaderOnly); + Assert.AreEqual(full.FamilyName, header.FamilyName); + Assert.AreEqual(full.SubfamilyName, header.SubfamilyName); + Assert.AreEqual(full.IsBold, header.IsBold); + Assert.AreEqual(full.IsItalic, header.IsItalic); + Assert.AreEqual(full.UnitsPerEm, header.UnitsPerEm); + Assert.AreEqual(full.CellAscent, header.CellAscent); + Assert.AreEqual(full.UnderlinePosition, header.UnderlinePosition); + Assert.IsTrue(header.HasTrueTypeOutlines); + Assert.ThrowsException(() => header.GetContours(5)); + } + + [TestMethod] + public void FontCacheHasNoFinalizerThatCanCrashTheProcess() + { + // the FontCache used to create a GDI device context in its constructor and delete it in its finalizer; without GDI + // the finalizer threw and ended the process (issue #412) + for (int i = 0; i < 10; ++i) new FontCache(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + GC.Collect(); + } + } +} diff --git a/tests/CADability.Tests/ProjectTest.cs b/tests/CADability.Tests/ProjectTest.cs index 9923a8a34..03274956f 100644 --- a/tests/CADability.Tests/ProjectTest.cs +++ b/tests/CADability.Tests/ProjectTest.cs @@ -15,16 +15,6 @@ public class ProjectTest public TestContext TestContext { get; set; } - /// - /// Text entities are measured and triangulated with GDI (System.Drawing and gdi32 via FontCache), - /// which only exists on Windows. Tests that import or create text are inconclusive elsewhere. - /// - private static void RequiresGdiText() - { - if (!System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(System.Runtime.InteropServices.OSPlatform.Windows)) - Assert.Inconclusive("text needs GDI, which is only available on Windows"); - } - /// /// Renders with OpenGL and compares it with the reference bitmap. /// Rendering needs CADability.Forms (WinForms, OpenGL), so the check only runs on Windows. @@ -103,7 +93,6 @@ public void import_step_issue101_succeds() [TestMethod] public void export_dxf_issue_129_succeds() { - RequiresGdiText(); // in master@82ffb34 exporting a text object to txt does not set the location, // so all texts are all not in the correct location / orientation @@ -172,7 +161,6 @@ public void import_dxf_issue171_succeds() [DeploymentItem(@"Files/Dxf/Z273.dxf", nameof(import_dxf_Z273_succeeds))] public void import_dxf_Z273_succeeds() { - RequiresGdiText(); // AC1009 (R12) DXF with no *Paper_Space block — importing this used to throw // KeyNotFoundException when FillPaperSpace accessed the missing block record. var file = Path.Combine(this.TestContext.DeploymentDirectory, this.TestContext.TestName, "Z273.dxf"); @@ -323,7 +311,6 @@ public void import_dxf_arc_crossing_zero_sweep_is_correct() [TestMethod] public void import_dxf_text_rotation_correct() { - RequiresGdiText(); // Regression: TextEntity.Rotation from ACadSharp is in RADIANS. // Old code called Angle.Deg() on it, making 90° text appear at ~1.57° (nearly horizontal). // A text at DXF rotation=90° must import with LineDirection ≈ (0, 1, 0). @@ -509,7 +496,6 @@ public void import_dxf_0583667_succeeds() [TestMethod] public void import_dxf_mtext_topcenter_placement_correct() { - RequiresGdiText(); // Regression: CreateMText ignored AttachmentPoint so all MTEXT rendered as // Left/Baseline. For table cells with attachment TopCenter (71=2) the text // appears shifted right and down of where it should be. @@ -567,7 +553,6 @@ public void import_dxf_mtext_topcenter_placement_correct() [TestMethod] public void import_dxf_mtext_bottomright_placement_correct() { - RequiresGdiText(); // MTEXT with attachment BottomRight (71=9) must have Right+Bottom alignment. const string dxf = @" 0 SECTION @@ -710,7 +695,6 @@ public void import_dxf_spline_degenerate_does_not_crash() [TestMethod] public void import_dxf_tolerance_fcf_imported_as_text() { - RequiresGdiText(); // DXF TOLERANCE (AcDbFcf / Feature Control Frame) entities were silently dropped // because there was no case for ACadSharp.Entities.Tolerance in GeoObjectFromEntity. // They must now be imported as Text objects at the insertion point. diff --git a/tests/CADability.Tests/TestFonts.cs b/tests/CADability.Tests/TestFonts.cs new file mode 100644 index 000000000..ea18ad0f9 --- /dev/null +++ b/tests/CADability.Tests/TestFonts.cs @@ -0,0 +1,20 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace CADability.Tests +{ + /// + /// Texts get their fonts from FontProvider.Current. The tests add Liberation Sans (Files/Fonts) to the fonts of the system, + /// so that texts have a font on every machine, also on a build server without any fonts installed (issue #412). + /// + [TestClass] + public static class TestFonts + { + public static string Directory => System.IO.Path.Combine(AppContext.BaseDirectory, "Files", "Fonts"); + + [AssemblyInitialize] + public static void UseTestFonts(TestContext context) + { + FontProvider.Current = new ManagedFontProvider(new[] { Directory }, true); + } + } +} From fc5326edc8af27c28cb10148b9fa151694d51868 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 15 Oct 2026 09:03:53 +0000 Subject: [PATCH 3/6] Read the CFF outlines of OpenType fonts in the managed font reader (#412) Step 6, part 2b of #412. The managed reader of #466 skipped OpenType fonts with CFF outlines, which many .otf files have, so a text in such a font fell back to the default face. The new internal CffOutlines reads the "CFF " table: - INDEX and DICT structures, the FontMatrix (which is stored rounded, e.g. 0.00048828 for 1/2048) - the Type 2 charstring interpreter: all path operators including the specialized curves and lines, flex, hflex, hflex1, flex1, local and global subroutines with their bias, widths, hints and hint masks (skipped, they do not change the outline), the arithmetic operators - CID-keyed fonts with FDArray and FDSelect (formats 0 and 3) The cubic curves go into the GDI+ path data unchanged. Not supported: CFF2 (variable OpenType fonts) and the deprecated accent composition of endchar (seac). TrueTypeFont gets HasCffOutlines and HasOutlines, and ManagedFontProvider indexes .otf and .otc files too. Tests: make_cff_test_fonts.py makes two small test fonts from a subset of Inter (SIL OFL 1.1): InterCffTest.otf rewritten to use subroutines, the specialized operators, flex and hint masks, and InterCidTest.otf, the same glyphs as a CID-keyed font with two font dicts. The outlines of both are compared command by command with fontTools. Checked locally against fontTools on 18,672 glyphs of the 47 CFF fonts of the Linux container, 7 of them CID-keyed: all equal. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 --- CADability/CffOutlines.cs | 505 ++++++++++++++++++ CADability/FontProvider.cs | 15 +- CADability/TrueTypeFont.cs | 94 +++- REUSE.toml | 9 + tests/CADability.Tests/CffOutlinesTests.cs | 125 +++++ .../Files/Fonts/InterCffTest.otf | Bin 0 -> 11908 bytes .../Files/Fonts/InterCffTest.reference.json | 1 + .../Files/Fonts/InterCidTest.otf | Bin 0 -> 12200 bytes .../Files/Fonts/make_cff_test_fonts.py | 187 +++++++ tests/CADability.Tests/FontProviderTests.cs | 2 +- 10 files changed, 925 insertions(+), 13 deletions(-) create mode 100644 CADability/CffOutlines.cs create mode 100644 tests/CADability.Tests/CffOutlinesTests.cs create mode 100644 tests/CADability.Tests/Files/Fonts/InterCffTest.otf create mode 100644 tests/CADability.Tests/Files/Fonts/InterCffTest.reference.json create mode 100644 tests/CADability.Tests/Files/Fonts/InterCidTest.otf create mode 100644 tests/CADability.Tests/Files/Fonts/make_cff_test_fonts.py diff --git a/CADability/CffOutlines.cs b/CADability/CffOutlines.cs new file mode 100644 index 000000000..d5bfeee2b --- /dev/null +++ b/CADability/CffOutlines.cs @@ -0,0 +1,505 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace CADability +{ + /// + /// Receives the outline of a glyph in font units: contours of lines and cubic Bezier segments. + /// + internal interface IGlyphOutlineSink + { + void MoveTo(double x, double y); + void LineTo(double x, double y); + void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3); + void ClosePath(); + } + + /// + /// Reads the glyph outlines of the "CFF " table of an OpenType font: the Compact Font Format with Type 2 charstrings, as used + /// by many .otf files. Name-keyed and CID-keyed fonts (FDArray, FDSelect) are supported, with local and global subroutines, + /// hint masks and the flex operators. Hints are skipped, they do not change the outline. Not supported: CFF2 (variable fonts) + /// and the deprecated accent composition of endchar (seac), which modern fonts do not use. + /// + internal sealed class CffOutlines + { + private readonly byte[] data; + private readonly List<(int start, int length)> charStrings; + private readonly List<(int start, int length)> globalSubrs; + private readonly List> localSubrs = new List>(); // per font dict + private readonly byte[] fdSelect; // font dict index per glyph, null for a name-keyed font + private readonly double scale; // FontMatrix * units per em, 1 in almost all fonts + + /// + /// The number of glyphs (charstrings). + /// + public int GlyphCount => charStrings.Count; + /// + /// True for a CID-keyed font. + /// + public bool IsCid => fdSelect != null; + + /// the font file + /// the start of the "CFF " table + /// its length + /// from the "head" table, to check the FontMatrix + public CffOutlines(byte[] data, int offset, int length, int unitsPerEm) + { + this.data = data; + int end = offset + length; + if (length < 4 || data[offset] != 1) throw new InvalidDataException("not a CFF table (CFF2 is not supported)"); + int pos = offset + data[offset + 2]; // header size + ReadIndex(ref pos, end); // names + List<(int start, int length)> topDicts = ReadIndex(ref pos, end); + ReadIndex(ref pos, end); // strings + globalSubrs = ReadIndex(ref pos, end); + if (topDicts.Count == 0) throw new InvalidDataException("CFF table without a font"); + Dictionary top = ReadDict(topDicts[0].start, topDicts[0].length); + if (!top.TryGetValue(17, out double[] charStringsOffset)) throw new InvalidDataException("CFF font without charstrings"); + if (top.TryGetValue(1206, out double[] charstringType) && charstringType[0] != 2) throw new InvalidDataException("only Type 2 charstrings are supported"); + int csPos = offset + (int)charStringsOffset[0]; + charStrings = ReadIndex(ref csPos, end); + scale = 1.0; + if (top.TryGetValue(1207, out double[] fontMatrix) && fontMatrix.Length >= 4 && fontMatrix[0] != 0) + { + // the FontMatrix is stored rounded (e.g. 0.00048828 for 1/2048): a factor this close to 1 is 1 + double s = fontMatrix[0] * unitsPerEm; + if (Math.Abs(s - 1.0) > 1e-3) scale = s; + } + if (top.TryGetValue(1236, out double[] fdArrayOffset) && top.TryGetValue(1237, out double[] fdSelectOffset)) + { // CID-keyed: one private dict (with its own local subroutines) per font dict + int fdPos = offset + (int)fdArrayOffset[0]; + foreach (var fd in ReadIndex(ref fdPos, end)) localSubrs.Add(ReadLocalSubrs(offset, ReadDict(fd.start, fd.length), end)); + fdSelect = ReadFdSelect(offset + (int)fdSelectOffset[0], charStrings.Count, end); + } + else localSubrs.Add(ReadLocalSubrs(offset, top, end)); + } + + /// + /// Sends the outline of to . + /// + public void Draw(int glyph, IGlyphOutlineSink sink) + { + if (glyph < 0 || glyph >= charStrings.Count) return; + int fd = 0; + if (fdSelect != null) + { + fd = fdSelect[glyph]; + if (fd >= localSubrs.Count) throw new InvalidDataException("invalid FDSelect"); + } + new Interpreter(this, localSubrs[fd], scale == 1.0 ? sink : new ScalingSink(sink, scale)).Run(charStrings[glyph]); + } + + private List<(int start, int length)> ReadLocalSubrs(int cffOffset, Dictionary dict, int end) + { + if (!dict.TryGetValue(18, out double[] priv) || priv.Length < 2) return new List<(int, int)>(); + int privSize = (int)priv[0], privOffset = cffOffset + (int)priv[1]; + if (privSize <= 0) return new List<(int, int)>(); + Dictionary privateDict = ReadDict(privOffset, privSize); + if (!privateDict.TryGetValue(19, out double[] subrs)) return new List<(int, int)>(); + int pos = privOffset + (int)subrs[0]; // relative to the private dict + return ReadIndex(ref pos, end); + } + private byte[] ReadFdSelect(int pos, int numGlyphs, int end) + { + byte[] res = new byte[numGlyphs]; + int format = data[pos]; + if (format == 0) + { + if (pos + 1 + numGlyphs > end) throw new InvalidDataException("invalid FDSelect"); + Array.Copy(data, pos + 1, res, 0, numGlyphs); + } + else if (format == 3) + { + int nRanges = U16(pos + 1); + for (int i = 0; i < nRanges; ++i) + { + int r = pos + 3 + 3 * i; + int first = U16(r), fd = data[r + 2], next = U16(r + 3); + for (int g = first; g < next && g < numGlyphs; ++g) res[g] = (byte)fd; + } + } + else throw new InvalidDataException("unsupported FDSelect format " + format); + return res; + } + private List<(int start, int length)> ReadIndex(ref int pos, int end) + { + List<(int, int)> res = new List<(int, int)>(); + if (pos + 2 > end) throw new InvalidDataException("CFF INDEX exceeds the table"); + int count = U16(pos); + if (count == 0) + { + pos += 2; + return res; + } + int offSize = data[pos + 2]; + if (offSize < 1 || offSize > 4) throw new InvalidDataException("invalid CFF INDEX"); + int offsets = pos + 3; + int dataStart = offsets + (count + 1) * offSize - 1; // offsets count from 1 + int Offset(int i) + { + int v = 0; + for (int k = 0; k < offSize; ++k) v = (v << 8) | data[offsets + i * offSize + k]; + return v; + } + for (int i = 0; i < count; ++i) + { + int s = Offset(i), e = Offset(i + 1); + if (e < s || dataStart + e > end) throw new InvalidDataException("invalid CFF INDEX"); + res.Add((dataStart + s, e - s)); + } + pos = dataStart + Offset(count); + return res; + } + /// + /// Reads a DICT: operator (two-byte operators as 1200 + second byte) to its operands. + /// + private Dictionary ReadDict(int pos, int length) + { + Dictionary res = new Dictionary(); + List operands = new List(); + int end = pos + length; + if (end > data.Length) throw new InvalidDataException("CFF DICT exceeds the file"); + while (pos < end) + { + int b0 = data[pos++]; + if (b0 <= 21) + { + int op = b0; + if (b0 == 12) op = 1200 + data[pos++]; + res[op] = operands.ToArray(); + operands.Clear(); + } + else if (b0 == 28) { operands.Add((short)((data[pos] << 8) | data[pos + 1])); pos += 2; } + else if (b0 == 29) { operands.Add((data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3]); pos += 4; } + else if (b0 == 30) operands.Add(ReadReal(ref pos)); + else if (b0 >= 32 && b0 <= 246) operands.Add(b0 - 139); + else if (b0 >= 247 && b0 <= 250) operands.Add((b0 - 247) * 256 + data[pos++] + 108); + else if (b0 >= 251 && b0 <= 254) operands.Add(-(b0 - 251) * 256 - data[pos++] - 108); + else throw new InvalidDataException("invalid CFF DICT"); + } + return res; + } + private double ReadReal(ref int pos) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + while (true) + { + int b = data[pos++]; + foreach (int nibble in new[] { b >> 4, b & 0xF }) + { + if (nibble <= 9) sb.Append((char)('0' + nibble)); + else if (nibble == 0xA) sb.Append('.'); + else if (nibble == 0xB) sb.Append('E'); + else if (nibble == 0xC) sb.Append("E-"); + else if (nibble == 0xE) sb.Append('-'); + else if (nibble == 0xF) + { + double.TryParse(sb.ToString(), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out double v); + return v; + } + } + } + } + private int U16(int pos) => (data[pos] << 8) | data[pos + 1]; + private static int Bias(int count) => count < 1240 ? 107 : count < 33900 ? 1131 : 32768; + + private sealed class ScalingSink : IGlyphOutlineSink + { + private readonly IGlyphOutlineSink sink; + private readonly double s; + public ScalingSink(IGlyphOutlineSink sink, double s) { this.sink = sink; this.s = s; } + public void MoveTo(double x, double y) => sink.MoveTo(x * s, y * s); + public void LineTo(double x, double y) => sink.LineTo(x * s, y * s); + public void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) => sink.CurveTo(x1 * s, y1 * s, x2 * s, y2 * s, x3 * s, y3 * s); + public void ClosePath() => sink.ClosePath(); + } + + /// + /// The Type 2 charstring interpreter (Adobe Technical Note #5177). + /// + private sealed class Interpreter + { + private readonly CffOutlines font; + private readonly List<(int start, int length)> local; + private readonly IGlyphOutlineSink sink; + private readonly List stack = new List(48); + private readonly double[] transient = new double[32]; + private double x, y; + private int stems; + private bool haveWidth, open, finished; + private int depth; + private readonly Random random = new Random(0); + + public Interpreter(CffOutlines font, List<(int start, int length)> local, IGlyphOutlineSink sink) + { + this.font = font; + this.local = local; + this.sink = sink; + } + public void Run((int start, int length) charString) + { + Execute(charString.start, charString.start + charString.length); + if (open) sink.ClosePath(); + open = false; + } + + private void Execute(int pos, int end) + { + if (++depth > 12) throw new InvalidDataException("CFF subroutines nested too deeply"); + byte[] d = font.data; + while (pos < end && !finished) + { + int b0 = d[pos++]; + if (b0 >= 32 || b0 == 28) + { // an operand + if (b0 == 28) { stack.Add((short)((d[pos] << 8) | d[pos + 1])); pos += 2; } + else if (b0 <= 246) stack.Add(b0 - 139); + else if (b0 <= 250) stack.Add((b0 - 247) * 256 + d[pos++] + 108); + else if (b0 <= 254) stack.Add(-(b0 - 251) * 256 - d[pos++] - 108); + else { stack.Add(((d[pos] << 24) | (d[pos + 1] << 16) | (d[pos + 2] << 8) | d[pos + 3]) / 65536.0); pos += 4; } + continue; + } + switch (b0) + { + case 1: // hstem + case 3: // vstem + case 18: // hstemhm + case 23: // vstemhm + Stems(); + break; + case 19: // hintmask + case 20: // cntrmask + Stems(); // the operands of an implicit vstemhm + pos += (stems + 7) / 8; + break; + case 21: // rmoveto + Width(2); + MoveTo(x + Arg(0), y + Arg(1)); + stack.Clear(); + break; + case 22: // hmoveto + Width(1); + MoveTo(x + Arg(0), y); + stack.Clear(); + break; + case 4: // vmoveto + Width(1); + MoveTo(x, y + Arg(0)); + stack.Clear(); + break; + case 5: // rlineto + for (int i = 0; i + 1 < stack.Count; i += 2) LineTo(x + stack[i], y + stack[i + 1]); + stack.Clear(); + break; + case 6: // hlineto + case 7: // vlineto + { + bool horizontal = b0 == 6; + for (int i = 0; i < stack.Count; ++i, horizontal = !horizontal) + { + if (horizontal) LineTo(x + stack[i], y); + else LineTo(x, y + stack[i]); + } + stack.Clear(); + break; + } + case 8: // rrcurveto + for (int i = 0; i + 5 < stack.Count; i += 6) Curve(stack[i], stack[i + 1], stack[i + 2], stack[i + 3], stack[i + 4], stack[i + 5]); + stack.Clear(); + break; + case 24: // rcurveline + { + int i = 0; + for (; i + 7 < stack.Count; i += 6) Curve(stack[i], stack[i + 1], stack[i + 2], stack[i + 3], stack[i + 4], stack[i + 5]); + if (i + 1 < stack.Count) LineTo(x + stack[i], y + stack[i + 1]); + stack.Clear(); + break; + } + case 25: // rlinecurve + { + int i = 0; + for (; i + 7 < stack.Count; i += 2) LineTo(x + stack[i], y + stack[i + 1]); + if (i + 5 < stack.Count) Curve(stack[i], stack[i + 1], stack[i + 2], stack[i + 3], stack[i + 4], stack[i + 5]); + stack.Clear(); + break; + } + case 26: // vvcurveto + { + int i = 0; + double dx1 = 0; + if (stack.Count % 2 == 1) dx1 = stack[i++]; + for (; i + 3 < stack.Count; i += 4) + { + Curve(dx1, stack[i], stack[i + 1], stack[i + 2], 0, stack[i + 3]); + dx1 = 0; + } + stack.Clear(); + break; + } + case 27: // hhcurveto + { + int i = 0; + double dy1 = 0; + if (stack.Count % 2 == 1) dy1 = stack[i++]; + for (; i + 3 < stack.Count; i += 4) + { + Curve(stack[i], dy1, stack[i + 1], stack[i + 2], stack[i + 3], 0); + dy1 = 0; + } + stack.Clear(); + break; + } + case 30: // vhcurveto + case 31: // hvcurveto + { + bool horizontal = b0 == 31; + int i = 0; + while (i + 3 < stack.Count) + { + double extra = stack.Count - i == 5 ? stack[i + 4] : 0; // the last curve may have a fifth operand + if (horizontal) Curve(stack[i], 0, stack[i + 1], stack[i + 2], extra, stack[i + 3]); + else Curve(0, stack[i], stack[i + 1], stack[i + 2], stack[i + 3], extra); + horizontal = !horizontal; + i += 4; + } + stack.Clear(); + break; + } + case 10: // callsubr + case 29: // callgsubr + { + List<(int start, int length)> subrs = b0 == 10 ? local : font.globalSubrs; + int index = (int)Pop() + Bias(subrs.Count); + if (index < 0 || index >= subrs.Count) throw new InvalidDataException("invalid CFF subroutine index"); + Execute(subrs[index].start, subrs[index].start + subrs[index].length); + break; + } + case 11: // return + --depth; + return; + case 14: // endchar (the accent composition "seac" with four operands is not supported) + if (!haveWidth && (stack.Count == 1 || stack.Count == 5)) stack.RemoveAt(0); + haveWidth = true; + if (open) sink.ClosePath(); + open = false; + finished = true; + stack.Clear(); + break; + case 12: + Escape(d[pos++]); + break; + default: // reserved operators are ignored + stack.Clear(); + break; + } + } + --depth; + } + + private void Escape(int op) + { + switch (op) + { + case 35: // flex + Curve(Arg(0), Arg(1), Arg(2), Arg(3), Arg(4), Arg(5)); + Curve(Arg(6), Arg(7), Arg(8), Arg(9), Arg(10), Arg(11)); + break; + case 34: // hflex + Curve(Arg(0), 0, Arg(1), Arg(2), Arg(3), 0); + Curve(Arg(4), 0, Arg(5), -Arg(2), Arg(6), 0); + break; + case 36: // hflex1 + { + double dy1 = Arg(1), dy2 = Arg(3), dy5 = Arg(7); + Curve(Arg(0), dy1, Arg(2), dy2, Arg(4), 0); + Curve(Arg(5), 0, Arg(6), dy5, Arg(8), -(dy1 + dy2 + dy5)); + break; + } + case 37: // flex1 + { + double dx = 0, dy = 0; + for (int i = 0; i < 10; i += 2) { dx += Arg(i); dy += Arg(i + 1); } + Curve(Arg(0), Arg(1), Arg(2), Arg(3), Arg(4), Arg(5)); + if (Math.Abs(dx) > Math.Abs(dy)) Curve(Arg(6), Arg(7), Arg(8), Arg(9), Arg(10), -dy); + else Curve(Arg(6), Arg(7), Arg(8), Arg(9), -dx, Arg(10)); + break; + } + // arithmetic and storage operators, rarely used + case 3: { double b = Pop(), a = Pop(); stack.Add(a != 0 && b != 0 ? 1 : 0); return; } // and + case 4: { double b = Pop(), a = Pop(); stack.Add(a != 0 || b != 0 ? 1 : 0); return; } // or + case 5: stack.Add(Pop() == 0 ? 1 : 0); return; // not + case 9: stack.Add(Math.Abs(Pop())); return; // abs + case 10: { double b = Pop(), a = Pop(); stack.Add(a + b); return; } // add + case 11: { double b = Pop(), a = Pop(); stack.Add(a - b); return; } // sub + case 12: { double b = Pop(), a = Pop(); stack.Add(b == 0 ? 0 : a / b); return; } // div + case 14: stack.Add(-Pop()); return; // neg + case 15: { double b = Pop(), a = Pop(); stack.Add(a == b ? 1 : 0); return; } // eq + case 18: Pop(); return; // drop + case 20: { int i = (int)Pop(); double v = Pop(); if (i >= 0 && i < transient.Length) transient[i] = v; return; } // put + case 21: { int i = (int)Pop(); stack.Add(i >= 0 && i < transient.Length ? transient[i] : 0); return; } // get + case 22: { double v2 = Pop(), v1 = Pop(), s2 = Pop(), s1 = Pop(); stack.Add(v1 <= v2 ? s1 : s2); return; } // ifelse + case 23: stack.Add(random.NextDouble() + double.Epsilon); return; // random + case 24: { double b = Pop(), a = Pop(); stack.Add(a * b); return; } // mul + case 26: stack.Add(Math.Sqrt(Math.Abs(Pop()))); return; // sqrt + case 27: { double a = Pop(); stack.Add(a); stack.Add(a); return; } // dup + case 28: { double b = Pop(), a = Pop(); stack.Add(b); stack.Add(a); return; } // exch + case 29: { int i = (int)Pop(); stack.Add(i >= 0 && i < stack.Count ? stack[stack.Count - 1 - i] : stack.Count > 0 ? stack[stack.Count - 1] : 0); return; } // index + case 30: // roll + { + int j = (int)Pop(), n = (int)Pop(); + if (n > 0 && n <= stack.Count) + { + double[] part = stack.GetRange(stack.Count - n, n).ToArray(); + for (int i = 0; i < n; ++i) stack[stack.Count - n + ((i + j) % n + n) % n] = part[i]; + } + return; + } + } + stack.Clear(); + } + + private void Stems() + { + if (!haveWidth && stack.Count % 2 == 1) stack.RemoveAt(0); + haveWidth = true; + stems += stack.Count / 2; + stack.Clear(); + } + private void Width(int expected) + { + if (!haveWidth && stack.Count > expected) stack.RemoveAt(0); + haveWidth = true; + } + private double Arg(int i) => i < stack.Count ? stack[i] : 0; + private double Pop() + { + if (stack.Count == 0) return 0; + double v = stack[stack.Count - 1]; + stack.RemoveAt(stack.Count - 1); + return v; + } + private void MoveTo(double nx, double ny) + { + if (open) sink.ClosePath(); + x = nx; + y = ny; + sink.MoveTo(x, y); + open = true; + } + private void LineTo(double nx, double ny) + { + x = nx; + y = ny; + sink.LineTo(x, y); + } + private void Curve(double dx1, double dy1, double dx2, double dy2, double dx3, double dy3) + { + double x1 = x + dx1, y1 = y + dy1, x2 = x1 + dx2, y2 = y1 + dy2; + x = x2 + dx3; + y = y2 + dy3; + sink.CurveTo(x1, y1, x2, y2, x, y); + } + } + } +} diff --git a/CADability/FontProvider.cs b/CADability/FontProvider.cs index 707d5cbb7..54cdb37c0 100644 --- a/CADability/FontProvider.cs +++ b/CADability/FontProvider.cs @@ -115,10 +115,11 @@ public abstract class FontFace } /// - /// The default : reads TrueType font files (.ttf, .ttc) itself, so it needs neither GDI nor any - /// other part of the operating system and works on Windows, Linux, macOS and in the browser. By default it uses the font - /// directories of the system; more files or directories can be added. OpenType fonts with CFF outlines are not supported - /// and are left out. A bold or italic face that a family does not have is not synthesized: the closest face is used. + /// The default : reads TrueType and OpenType font files (.ttf, .ttc, .otf, .otc) itself, so it + /// needs neither GDI nor any other part of the operating system and works on Windows, Linux, macOS and in the browser. By + /// default it uses the font directories of the system; more files or directories can be added. Bitmap fonts and variable + /// OpenType fonts with CFF2 outlines are left out. A bold or italic face that a family does not have is not synthesized: + /// the closest face is used. /// public class ManagedFontProvider : FontProvider { @@ -173,7 +174,7 @@ public static IEnumerable SystemFontDirectories() } } /// - /// Adds the fonts of a font file (.ttf or .ttc). + /// Adds the fonts of a font file (.ttf, .ttc, .otf or .otc). /// public void AddFontFile(string fileName) { @@ -228,7 +229,7 @@ public override FontFace GetFace(string familyName, bool bold, bool italic) private static void AddEntry(Entry entry, TrueTypeFont font, List list) { - if (!font.HasTrueTypeOutlines || string.IsNullOrEmpty(font.FamilyName)) return; // CFF or bitmap fonts cannot be used + if (!font.HasOutlines || string.IsNullOrEmpty(font.FamilyName)) return; // bitmap fonts and CFF2 cannot be used entry.Family = font.FamilyName; entry.Bold = font.IsBold; entry.Italic = font.IsItalic; @@ -254,7 +255,7 @@ private Dictionary> Families() foreach (string file in files) { string ext = Path.GetExtension(file).ToLowerInvariant(); - if (ext != ".ttf" && ext != ".ttc") continue; + if (ext != ".ttf" && ext != ".ttc" && ext != ".otf" && ext != ".otc") continue; if (!seen.Add(Path.GetFullPath(file))) continue; try { diff --git a/CADability/TrueTypeFont.cs b/CADability/TrueTypeFont.cs index 7428e83de..01230fab7 100644 --- a/CADability/TrueTypeFont.cs +++ b/CADability/TrueTypeFont.cs @@ -26,8 +26,8 @@ public GlyphPoint(double x, double y, bool onCurve) /// Reads a TrueType font (.ttf, or one font of a .ttc collection) without any help of the operating system, so that text /// works on every platform (issue #412). It reads the glyph outlines (exact quadratic curves from the "glyf" table, /// composite glyphs included), the advance widths, the metrics, the kerning pairs of the "kern" table and the names. - /// OpenType fonts with CFF outlines (some .otf files) are recognized, but their outlines cannot be read - /// ( is false). + /// OpenType fonts with CFF outlines (most .otf files) are read with : their cubic curves go into + /// the path data unchanged. /// internal sealed class TrueTypeFont { @@ -81,6 +81,16 @@ internal sealed class TrueTypeFont /// False for an OpenType font with CFF outlines and for a bitmap font, whose glyphs cannot be read. /// public bool HasTrueTypeOutlines { get; } + /// + /// True for an OpenType font with outlines in the "CFF " table (Compact Font Format, cubic curves). + /// + public bool HasCffOutlines { get; } + /// + /// True if the glyph outlines can be read: TrueType or CFF outlines. False for bitmap fonts and for CFF2 (variable + /// OpenType fonts). + /// + public bool HasOutlines => HasTrueTypeOutlines || HasCffOutlines; + private CffOutlines cff; // created on demand public int GlyphCount => numGlyphs; /// @@ -123,6 +133,7 @@ private TrueTypeFont(byte[] data, int offset) } // a TrueType font without outlines is a bitmap font (e.g. color emoji) HasTrueTypeOutlines = trueType && tables.ContainsKey("glyf") && tables.ContainsKey("loca"); + HasCffOutlines = !trueType && tables.ContainsKey("CFF "); int head = Table("head"); UnitsPerEm = U16(head + 18); if (UnitsPerEm == 0) UnitsPerEm = 1000; @@ -228,8 +239,8 @@ private static TrueTypeFont ReadHeader(FileStream stream, uint offset) if (length > stream.Length) throw new InvalidDataException("table " + tag + " exceeds the file"); copied.Add((record, ReadBytes(stream, ReadU32(records, 16 * i + 8), (int)length))); } - else if (tag == "glyf" || tag == "loca") - { // only their presence matters (HasTrueTypeOutlines) + else if (tag == "glyf" || tag == "loca" || tag == "CFF ") + { // only their presence matters (HasTrueTypeOutlines, HasCffOutlines) for (int k = 8; k < 16; ++k) record[k] = 0; copied.Add((record, new byte[0])); } @@ -351,7 +362,8 @@ public void GetPathData(int glyph, bool underline, bool strikeout, out PointF[] List tps = new List(); double scale = 1.0 / UnitsPerEm; PointF Map(double x, double y) => new PointF((float)(x * scale), (float)((CellAscent - y) * scale)); - foreach (GlyphPoint[] contour in GetContours(glyph)) + if (HasCffOutlines) DrawCff(glyph, new PathDataSink(pts, tps, Map)); + else foreach (GlyphPoint[] contour in GetContours(glyph)) { if (contour.Length < 2) continue; int n = contour.Length; @@ -443,6 +455,78 @@ void AddBar(int top, int thickness) types = tps.ToArray(); } + /// + /// Sends the outline of a glyph of a font with CFF outlines to , in font units. + /// + internal void DrawCff(int glyph, IGlyphOutlineSink sink) + { + if (IsHeaderOnly) throw new InvalidOperationException("The font \"" + FullName + "\" was read by ReadHeaders and has no glyphs, load it with Load"); + if (!HasCffOutlines) throw new NotSupportedException("The font \"" + FullName + "\" has no CFF outlines"); + if (cff == null) + { + var t = tables["CFF "]; + cff = new CffOutlines(data, t.offset, t.length, UnitsPerEm); + } + cff.Draw(glyph, sink); + } + /// + /// Collects an outline as GDI+ path data (see ). + /// + private sealed class PathDataSink : IGlyphOutlineSink + { + private readonly List pts; + private readonly List tps; + private readonly Func map; + private int start = -1; // index of the start point of the open figure + private double cx, cy; + public PathDataSink(List pts, List tps, Func map) + { + this.pts = pts; + this.tps = tps; + this.map = map; + } + public void MoveTo(double x, double y) + { + ClosePath(); + start = pts.Count; + pts.Add(map(x, y)); + tps.Add(0); + cx = x; + cy = y; + } + public void LineTo(double x, double y) + { + if (start < 0 || (x == cx && y == cy)) return; + pts.Add(map(x, y)); + tps.Add(1); + cx = x; + cy = y; + } + public void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) + { + if (start < 0) return; + if (x1 == cx && y1 == cy && x2 == cx && y2 == cy && x3 == cx && y3 == cy) return; // no length + pts.Add(map(x1, y1)); + pts.Add(map(x2, y2)); + pts.Add(map(x3, y3)); + tps.Add(3); + tps.Add(3); + tps.Add(3); + cx = x3; + cy = y3; + } + public void ClosePath() + { + if (start < 0) return; + if (pts.Count - start < 2) + { // nothing but the start point + pts.RemoveRange(start, pts.Count - start); + tps.RemoveRange(start, tps.Count - start); + } + else tps[tps.Count - 1] |= 0x80; + start = -1; + } + } private void AddContours(int glyph, List res, int depth) { if (glyph < 0 || glyph >= numGlyphs || depth > 8) return; // a deeper nesting is a broken (or malicious) font diff --git a/REUSE.toml b/REUSE.toml index d8db5bac2..194493650 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -40,3 +40,12 @@ SPDX-FileCopyrightText = [ "2012 Red Hat, Inc. with Reserved Font Name Liberation", ] SPDX-License-Identifier = "OFL-1.1" + +[[annotations]] +path = [ + "tests/CADability.Tests/Files/Fonts/InterCffTest.otf", + "tests/CADability.Tests/Files/Fonts/InterCidTest.otf", + "tests/CADability.Tests/Files/Fonts/InterCffTest.reference.json", +] +SPDX-FileCopyrightText = "2016 The Inter Project Authors" +SPDX-License-Identifier = "OFL-1.1" diff --git a/tests/CADability.Tests/CffOutlinesTests.cs b/tests/CADability.Tests/CffOutlinesTests.cs new file mode 100644 index 000000000..6937bc68c --- /dev/null +++ b/tests/CADability.Tests/CffOutlinesTests.cs @@ -0,0 +1,125 @@ +using CADability.GeoObject; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Drawing; +using System.Runtime.CompilerServices; +using System.Text.Json; +using Path = System.IO.Path; + +namespace CADability.Tests +{ + /// + /// The outlines of OpenType fonts with CFF outlines (issue #412, step 6). InterCffTest.otf (name-keyed) and InterCidTest.otf + /// (CID-keyed, two font dicts) are made by make_cff_test_fonts.py from a subset of Inter so that they use subroutines, + /// the specialized curve and line operators, flex and hint masks; InterCffTest.reference.json holds the outlines as fontTools + /// draws them, the same for both fonts. + /// + [TestClass] + public class CffOutlinesTests + { + private static string FontFile(string name, [CallerFilePath] string thisFile = "") + => Path.Combine(Path.GetDirectoryName(thisFile), "Files", "Fonts", name); + + private sealed class Recorder : IGlyphOutlineSink + { + public readonly List<(string op, double[] args)> Commands = new List<(string, double[])>(); + public void MoveTo(double x, double y) => Commands.Add(("moveTo", new[] { x, y })); + public void LineTo(double x, double y) => Commands.Add(("lineTo", new[] { x, y })); + public void CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) => Commands.Add(("curveTo", new[] { x1, y1, x2, y2, x3, y3 })); + public void ClosePath() => Commands.Add(("closePath", new double[0])); + } + + private static void AssertOutlinesMatchFontTools(string fontFile) + { + TrueTypeFont font = TrueTypeFont.Load(FontFile(fontFile)).Single(); + Assert.IsTrue(font.HasCffOutlines); + Assert.IsFalse(font.HasTrueTypeOutlines); + Assert.IsTrue(font.HasOutlines); + JsonElement glyphs = JsonDocument.Parse(File.ReadAllText(FontFile("InterCffTest.reference.json"))).RootElement.GetProperty("glyphs"); + int compared = 0; + foreach (JsonElement expected in glyphs.EnumerateArray()) + { + string c = expected.GetProperty("char").GetString(); + int glyph = font.GetGlyphIndex(c[0]); + Assert.AreEqual(expected.GetProperty("glyph").GetInt32(), glyph, $"glyph index of '{c}'"); + Recorder recorder = new Recorder(); + font.DrawCff(glyph, recorder); + JsonElement commands = expected.GetProperty("commands"); + Assert.AreEqual(commands.GetArrayLength(), recorder.Commands.Count, $"number of drawing commands of '{c}'"); + int i = 0; + foreach (JsonElement command in commands.EnumerateArray()) + { + (string op, double[] args) = recorder.Commands[i++]; + Assert.AreEqual(command[0].GetString(), op, $"command {i} of '{c}'"); + double[] expectedArgs = command[1].EnumerateArray().Select(v => v.GetDouble()).ToArray(); + Assert.AreEqual(expectedArgs.Length, args.Length, $"arguments of command {i} of '{c}'"); + for (int k = 0; k < args.Length; ++k) Assert.AreEqual(expectedArgs[k], args[k], 1e-9, $"argument {k} of command {i} of '{c}'"); + } + ++compared; + } + Assert.AreEqual(104, compared); + } + + [TestMethod] + public void NameKeyedOutlinesMatchFontTools() + { + AssertOutlinesMatchFontTools("InterCffTest.otf"); + } + + [TestMethod] + public void CidKeyedOutlinesMatchFontTools() + { + AssertOutlinesMatchFontTools("InterCidTest.otf"); + } + + [TestMethod] + public void PathDataOfACffGlyph() + { + TrueTypeFont font = TrueTypeFont.Load(FontFile("InterCffTest.otf")).Single(); + Recorder recorder = new Recorder(); + int glyph = font.GetGlyphIndex('B'); + font.DrawCff(glyph, recorder); + font.GetPathData(glyph, out PointF[] points, out byte[] types); + Assert.AreEqual(recorder.Commands.Count(c => c.op == "closePath"), types.Count(t => (t & 0x80) != 0), "one closed figure per contour"); + Assert.IsTrue(types.All(t => new[] { 0, 1, 3 }.Contains(t & 0x7F))); + Assert.AreEqual(recorder.Commands.Count(c => c.op == "curveTo") * 3, types.Count(t => (t & 0x7F) == 3), "the cubic curves go in unchanged"); + Assert.AreEqual(font.CellAscent / (double)font.UnitsPerEm, points.Max(p => p.Y), 1e-6, "the B stands on the baseline"); + } + + [TestMethod] + public void HeadersOfAnOpenTypeFontAreRead() + { + TrueTypeFont header = TrueTypeFont.ReadHeaders(FontFile("InterCidTest.otf")).Single(); + Assert.IsTrue(header.IsHeaderOnly); + Assert.IsTrue(header.HasCffOutlines); + Assert.AreEqual("Inter", header.FamilyName); + } + + [TestMethod] + [DoNotParallelize] + public void TextWithAnOpenTypeFont() + { + string directory = Path.Combine(Path.GetTempPath(), "CADability-otf-" + Guid.NewGuid().ToString("N")); + Directory.CreateDirectory(directory); + try + { + File.Copy(FontFile("InterCffTest.otf"), Path.Combine(directory, "InterCffTest.otf")); + FontProvider.Current = new ManagedFontProvider(new[] { directory }, false); + FontFace face = FontCache.GlobalFontCache.GetFace("Inter", 0); + Assert.AreEqual("Inter", face.FamilyName); + Text text = Text.Construct(); + text.Font = "Inter"; + text.TextString = "CFF"; + text.Location = GeoPoint.Origin; + text.LineDirection = GeoVector.XAxis; + text.GlyphDirection = GeoVector.YAxis; + text.TextSize = 1.0; + Assert.IsTrue(text.GetOutline().Count > 0); + } + finally + { + TestFonts.UseTestFonts(null); + Directory.Delete(directory, true); + } + } + } +} diff --git a/tests/CADability.Tests/Files/Fonts/InterCffTest.otf b/tests/CADability.Tests/Files/Fonts/InterCffTest.otf new file mode 100644 index 0000000000000000000000000000000000000000..8afd85e9da68c5f91c90de1948e6422c3816b7bd GIT binary patch literal 11908 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 literal 0 HcmV?d00001 diff --git a/tests/CADability.Tests/Files/Fonts/InterCffTest.reference.json b/tests/CADability.Tests/Files/Fonts/InterCffTest.reference.json new file mode 100644 index 000000000..381571f5c --- /dev/null +++ b/tests/CADability.Tests/Files/Fonts/InterCffTest.reference.json @@ -0,0 +1 @@ +{"glyphs":[{"char":" ","glyph":104,"commands":[]},{"char":"!","glyph":74,"commands":[["moveTo",[204,479]],["lineTo",[383,479]],["lineTo",[396,1490]],["lineTo",[192,1490]],["closePath",[]],["moveTo",[295,-13]],["curveTo",[369,-13,429,47,429,121]],["curveTo",[429,196,369,256,295,256]],["curveTo",[220,256,160,196,160,121]],["curveTo",[160,47,220,-13,295,-13]],["closePath",[]]]},{"char":"\"","glyph":89,"commands":[["moveTo",[232,923]],["lineTo",[382,923]],["lineTo",[404,1490]],["lineTo",[210,1490]],["closePath",[]],["moveTo",[572,923]],["lineTo",[722,923]],["lineTo",[744,1490]],["lineTo",[550,1490]],["closePath",[]]]},{"char":"#","glyph":83,"commands":[["moveTo",[711,0]],["lineTo",[864,0]],["lineTo",[931,411]],["lineTo",[1150,411]],["lineTo",[1175,561]],["lineTo",[956,561]],["lineTo",[1016,927]],["lineTo",[1238,927]],["lineTo",[1261,1079]],["lineTo",[1041,1079]],["lineTo",[1108,1490]],["lineTo",[957,1490]],["lineTo",[889,1079]],["lineTo",[517,1079]],["lineTo",[584,1490]],["lineTo",[433,1490]],["lineTo",[366,1079]],["lineTo",[147,1079]],["lineTo",[122,927]],["lineTo",[341,927]],["lineTo",[281,561]],["lineTo",[59,561]],["lineTo",[34,411]],["lineTo",[256,411]],["lineTo",[189,0]],["lineTo",[340,0]],["lineTo",[407,411]],["lineTo",[779,411]],["closePath",[]],["moveTo",[432,561]],["lineTo",[492,927]],["lineTo",[864,927]],["lineTo",[804,561]],["closePath",[]]]},{"char":"$","glyph":61,"commands":[["moveTo",[597,-218]],["lineTo",[717,-218]],["lineTo",[717,-24]],["curveTo",[1024,-3,1198,177,1198,406]],["curveTo",[1198,684,936,774,776,818]],["lineTo",[717,834]],["lineTo",[717,1339]],["curveTo",[867,1324,977,1244,992,1114]],["lineTo",[1178,1114]],["curveTo",[1169,1327,986,1491,717,1509]],["lineTo",[717,1708]],["lineTo",[597,1708]],["lineTo",[597,1506]],["curveTo",[345,1481,158,1321,158,1091]],["curveTo",[158,893,298,770,538,702]],["lineTo",[597,685]],["lineTo",[597,147]],["curveTo",[448,163,324,241,311,390]],["lineTo",[116,390]],["curveTo",[132,156,309,-5,597,-24]],["closePath",[]],["moveTo",[717,147]],["lineTo",[717,651]],["lineTo",[718,651]],["curveTo",[877,606,1013,553,1013,409]],["curveTo",[1013,266,889,166,717,147]],["closePath",[]],["moveTo",[597,868]],["curveTo",[489,899,343,958,343,1100]],["curveTo",[343,1223,442,1317,597,1338]],["closePath",[]]]},{"char":"%","glyph":102,"commands":[["moveTo",[1490,-29]],["curveTo",[1679,-29,1781,110,1781,274]],["lineTo",[1781,352]],["curveTo",[1781,515,1682,654,1490,654]],["curveTo",[1302,654,1197,516,1197,352]],["lineTo",[1197,274]],["curveTo",[1197,110,1299,-29,1490,-29]],["closePath",[]],["moveTo",[523,831]],["curveTo",[712,831,814,970,814,1134]],["lineTo",[814,1212]],["curveTo",[814,1375,715,1514,523,1514]],["curveTo",[335,1514,230,1376,230,1212]],["lineTo",[230,1134]],["curveTo",[230,970,332,831,523,831]],["closePath",[]],["moveTo",[404,0]],["lineTo",[573,0]],["lineTo",[1597,1490]],["lineTo",[1428,1490]],["closePath",[]],["moveTo",[1490,102]],["curveTo",[1387,102,1350,185,1350,274]],["lineTo",[1350,352]],["curveTo",[1350,440,1390,524,1490,524]],["curveTo",[1594,524,1628,440,1628,352]],["lineTo",[1628,274]],["curveTo",[1628,185,1591,102,1490,102]],["closePath",[]],["moveTo",[523,962]],["curveTo",[420,962,383,1045,383,1134]],["lineTo",[383,1212]],["curveTo",[383,1300,423,1384,523,1384]],["curveTo",[627,1384,661,1300,661,1212]],["lineTo",[661,1134]],["curveTo",[661,1045,624,962,523,962]],["closePath",[]]]},{"char":"&","glyph":73,"commands":[["moveTo",[580,-20]],["curveTo",[716,-20,868,27,967,124]],["lineTo",[1070,0]],["lineTo",[1287,0]],["lineTo",[1079,252]],["curveTo",[1137,326,1199,450,1199,658]],["lineTo",[1026,658]],["curveTo",[1026,556,1003,466,965,391]],["lineTo",[646,778]],["lineTo",[754,858]],["curveTo",[870,944,952,1046,952,1183]],["curveTo",[952,1360,814,1504,590,1504]],["curveTo",[369,1504,225,1364,225,1167]],["curveTo",[225,1031,294,930,398,805]],["curveTo",[227,679,104,578,104,389]],["curveTo",[104,159,285,-20,580,-20]],["closePath",[]],["moveTo",[859,253]],["curveTo",[777,178,674,137,567,137]],["curveTo",[402,137,289,236,289,383]],["curveTo",[289,475,333,546,456,637]],["lineTo",[507,675]],["closePath",[]],["moveTo",[538,908]],["curveTo",[459,1004,406,1078,406,1168]],["curveTo",[406,1275,482,1349,595,1349]],["curveTo",[703,1349,771,1281,771,1186]],["curveTo",[771,1081,689,1020,635,980]],["closePath",[]]]},{"char":"'","glyph":88,"commands":[["moveTo",[232,923]],["lineTo",[382,923]],["lineTo",[404,1490]],["lineTo",[210,1490]],["closePath",[]]]},{"char":"(","glyph":76,"commands":[["moveTo",[218,607]],["curveTo",[218,315,306,10,476,-279]],["lineTo",[651,-279]],["curveTo",[479,76,401,342,401,607]],["curveTo",[401,899,496,1266,651,1581]],["lineTo",[476,1581]],["curveTo",[326,1334,218,932,218,607]],["closePath",[]]]},{"char":")","glyph":77,"commands":[["moveTo",[96,-279]],["lineTo",[271,-279]],["curveTo",[438,5,529,310,529,607]],["curveTo",[529,931,421,1335,271,1581]],["lineTo",[96,1581]],["curveTo",[251,1266,346,899,346,607]],["curveTo",[346,350,273,86,96,-279]],["closePath",[]]]},{"char":"*","glyph":101,"commands":[["moveTo",[441,652]],["lineTo",[585,652]],["lineTo",[573,969]],["lineTo",[840,798]],["lineTo",[912,924]],["lineTo",[630,1070]],["lineTo",[912,1216]],["lineTo",[840,1344]],["lineTo",[573,1173]],["lineTo",[585,1490]],["lineTo",[441,1490]],["lineTo",[454,1173]],["lineTo",[186,1344]],["lineTo",[114,1216]],["lineTo",[396,1070]],["lineTo",[114,924]],["lineTo",[186,798]],["lineTo",[454,969]],["closePath",[]]]},{"char":"+","glyph":97,"commands":[["moveTo",[593,100]],["lineTo",[762,100]],["lineTo",[762,504]],["lineTo",[1160,504]],["lineTo",[1160,663]],["lineTo",[762,663]],["lineTo",[762,1066]],["lineTo",[593,1066]],["lineTo",[593,663]],["lineTo",[195,663]],["lineTo",[195,504]],["lineTo",[593,504]],["closePath",[]]]},{"char":",","glyph":90,"commands":[["moveTo",[128,-359]],["lineTo",[266,-359]],["lineTo",[422,208]],["lineTo",[216,208]],["closePath",[]]]},{"char":"-","glyph":87,"commands":[["moveTo",[798,719]],["lineTo",[144,719]],["lineTo",[144,553]],["lineTo",[798,553]],["closePath",[]]]},{"char":".","glyph":91,"commands":[["moveTo",[295,-13]],["curveTo",[370,-13,430,47,430,122]],["curveTo",[430,197,370,257,295,257]],["curveTo",[220,257,160,197,160,122]],["curveTo",[160,47,220,-13,295,-13]],["closePath",[]]]},{"char":"/","glyph":84,"commands":[["moveTo",[692,1560]],["lineTo",[526,1560]],["lineTo",[46,-224]],["lineTo",[212,-224]],["closePath",[]]]},{"char":"0","glyph":63,"commands":[["moveTo",[646,-20]],["curveTo",[979,-20,1170,259,1170,744]],["curveTo",[1170,1227,976,1510,646,1510]],["curveTo",[316,1510,122,1227,122,744]],["curveTo",[122,258,314,-20,646,-20]],["closePath",[]],["moveTo",[646,146]],["curveTo",[428,146,305,366,305,744]],["curveTo",[305,1123,428,1345,646,1345]],["curveTo",[864,1345,987,1124,987,744]],["curveTo",[987,366,864,146,646,146]],["closePath",[]]]},{"char":"1","glyph":64,"commands":[["moveTo",[653,1490]],["lineTo",[420,1490]],["lineTo",[96,1251]],["lineTo",[96,1047]],["lineTo",[456,1314]],["lineTo",[466,1314]],["lineTo",[466,0]],["lineTo",[653,0]],["closePath",[]]]},{"char":"2","glyph":65,"commands":[["moveTo",[154,0]],["lineTo",[1103,0]],["lineTo",[1103,167]],["lineTo",[416,167]],["lineTo",[416,179]],["lineTo",[742,526]],["curveTo",[1005,806,1078,933,1078,1094]],["curveTo",[1078,1327,889,1510,623,1510]],["curveTo",[358,1510,158,1331,158,1067]],["lineTo",[340,1067]],["curveTo",[340,1235,448,1345,618,1345]],["curveTo",[777,1345,898,1248,898,1092]],["curveTo",[898,955,814,853,649,674]],["lineTo",[154,137]],["closePath",[]]]},{"char":"3","glyph":66,"commands":[["moveTo",[635,-20]],["curveTo",[927,-20,1143,161,1143,405]],["curveTo",[1143,593,1032,731,842,762]],["lineTo",[842,774]],["curveTo",[994,819,1089,942,1089,1110]],["curveTo",[1089,1322,919,1510,641,1510]],["curveTo",[379,1510,165,1349,157,1115]],["lineTo",[340,1115]],["curveTo",[346,1260,485,1345,637,1345]],["curveTo",[799,1345,903,1248,903,1100]],["curveTo",[903,946,783,846,609,846]],["lineTo",[488,846]],["lineTo",[488,681]],["lineTo",[609,681]],["curveTo",[830,681,955,568,955,407]],["curveTo",[955,252,819,147,632,147]],["curveTo",[462,147,328,234,317,376]],["lineTo",[125,376]],["curveTo",[135,140,343,-20,635,-20]],["closePath",[]]]},{"char":"4","glyph":67,"commands":[["moveTo",[120,303]],["lineTo",[821,303]],["lineTo",[821,0]],["lineTo",[1003,0]],["lineTo",[1003,303]],["lineTo",[1205,303]],["lineTo",[1205,470]],["lineTo",[1003,470]],["lineTo",[1003,1490]],["lineTo",[772,1490]],["lineTo",[120,458]],["closePath",[]],["moveTo",[822,470]],["lineTo",[323,470]],["lineTo",[323,482]],["lineTo",[810,1252]],["lineTo",[822,1252]],["closePath",[]]]},{"char":"5","glyph":68,"commands":[["moveTo",[626,-20]],["curveTo",[913,-20,1123,190,1123,476]],["curveTo",[1123,764,922,975,649,975]],["curveTo",[546,975,447,939,386,888]],["lineTo",[378,888]],["lineTo",[429,1323]],["lineTo",[1053,1323]],["lineTo",[1053,1490]],["lineTo",[269,1490]],["lineTo",[181,752]],["lineTo",[359,730]],["curveTo",[418,775,522,808,614,808]],["curveTo",[801,808,937,665,937,473]],["curveTo",[937,284,806,147,626,147]],["curveTo",[476,147,354,243,342,376]],["lineTo",[158,376]],["curveTo",[168,148,364,-20,626,-20]],["closePath",[]]]},{"char":"6","glyph":69,"commands":[["moveTo",[646,-20]],["curveTo",[950,-20,1148,203,1148,481]],["curveTo",[1148,769,936,974,681,974]],["curveTo",[526,974,394,900,314,776]],["lineTo",[302,776]],["curveTo",[302,1134,440,1342,669,1342]],["curveTo",[824,1342,917,1244,950,1115]],["lineTo",[1136,1115]],["curveTo",[1100,1349,919,1510,669,1510]],["curveTo",[331,1510,122,1212,122,688]],["curveTo",[122,139,397,-20,646,-20]],["closePath",[]],["moveTo",[646,147]],["curveTo",[463,147,329,303,329,479]],["curveTo",[329,653,470,809,651,809]],["curveTo",[831,809,964,663,964,481]],["curveTo",[964,295,825,147,646,147]],["closePath",[]]]},{"char":"7","glyph":70,"commands":[["moveTo",[200,0]],["lineTo",[400,0]],["lineTo",[1061,1313]],["lineTo",[1061,1490]],["lineTo",[98,1490]],["lineTo",[98,1323]],["lineTo",[862,1323]],["lineTo",[862,1311]],["closePath",[]]]},{"char":"8","glyph":71,"commands":[["moveTo",[633,-20]],["curveTo",[933,-20,1145,153,1145,396]],["curveTo",[1145,584,1016,744,854,774]],["lineTo",[854,782]],["curveTo",[995,819,1089,956,1089,1114]],["curveTo",[1089,1341,894,1510,633,1510]],["curveTo",[368,1510,176,1341,176,1114]],["curveTo",[176,956,268,819,413,782]],["lineTo",[413,774]],["curveTo",[246,744,122,584,122,396]],["curveTo",[122,153,330,-20,633,-20]],["closePath",[]],["moveTo",[633,145]],["curveTo",[432,145,309,248,309,406]],["curveTo",[309,571,445,689,633,689]],["curveTo",[817,689,955,571,955,406]],["curveTo",[955,248,830,145,633,145]],["closePath",[]],["moveTo",[633,848]],["curveTo",[472,848,360,949,360,1100]],["curveTo",[360,1249,468,1346,633,1346]],["curveTo",[795,1346,905,1249,905,1100]],["curveTo",[905,949,790,848,633,848]],["closePath",[]]]},{"char":"9","glyph":72,"commands":[["moveTo",[603,-20]],["curveTo",[940,-20,1148,277,1148,804]],["curveTo",[1148,1352,873,1506,628,1510]],["curveTo",[322,1515,122,1288,122,1010]],["curveTo",[122,723,334,517,591,517]],["curveTo",[747,517,878,594,958,716]],["lineTo",[970,716]],["curveTo",[970,355,830,148,603,148]],["curveTo",[446,148,353,247,322,379]],["lineTo",[134,379]],["curveTo",[169,141,350,-20,603,-20]],["closePath",[]],["moveTo",[619,683]],["curveTo",[439,683,308,828,308,1011]],["curveTo",[308,1195,445,1343,625,1343]],["curveTo",[808,1343,943,1186,943,1015]],["curveTo",[943,840,800,683,619,683]],["closePath",[]]]},{"char":":","glyph":92,"commands":[["moveTo",[295,-13]],["curveTo",[370,-13,430,47,430,122]],["curveTo",[430,197,370,257,295,257]],["curveTo",[220,257,160,197,160,122]],["curveTo",[160,47,220,-13,295,-13]],["closePath",[]],["moveTo",[295,796]],["curveTo",[370,796,430,856,430,931]],["curveTo",[430,1006,370,1066,295,1066]],["curveTo",[220,1066,160,1006,160,931]],["curveTo",[160,856,220,796,295,796]],["closePath",[]]]},{"char":";","glyph":93,"commands":[["moveTo",[128,-359]],["lineTo",[266,-359]],["lineTo",[422,208]],["lineTo",[216,208]],["closePath",[]],["moveTo",[323,796]],["curveTo",[398,796,458,856,458,931]],["curveTo",[458,1006,398,1066,323,1066]],["curveTo",[248,1066,188,1006,188,931]],["curveTo",[188,856,248,796,323,796]],["closePath",[]]]},{"char":"<","glyph":94,"commands":[["moveTo",[174,520]],["lineTo",[1118,44]],["lineTo",[1118,238]],["lineTo",[406,576]],["lineTo",[406,588]],["lineTo",[1118,926]],["lineTo",[1118,1122]],["lineTo",[174,646]],["closePath",[]]]},{"char":"=","glyph":96,"commands":[["moveTo",[226,283]],["lineTo",[1130,283]],["lineTo",[1130,449]],["lineTo",[226,449]],["closePath",[]],["moveTo",[226,717]],["lineTo",[1130,717]],["lineTo",[1130,883]],["lineTo",[226,883]],["closePath",[]]]},{"char":">","glyph":95,"commands":[["moveTo",[1181,520]],["lineTo",[1181,646]],["lineTo",[237,1122]],["lineTo",[237,926]],["lineTo",[949,588]],["lineTo",[949,576]],["lineTo",[237,238]],["lineTo",[237,44]],["closePath",[]]]},{"char":"?","glyph":75,"commands":[["moveTo",[383,449]],["lineTo",[564,449]],["lineTo",[564,460]],["curveTo",[564,637,613,686,742,765]],["curveTo",[871,844,945,956,945,1117]],["curveTo",[945,1345,777,1510,518,1510]],["curveTo",[282,1510,92,1366,82,1115]],["lineTo",[272,1115]],["curveTo",[283,1281,400,1351,518,1351]],["curveTo",[653,1351,761,1263,761,1124]],["curveTo",[761,1008,693,927,605,874]],["curveTo",[467,791,383,733,383,460]],["closePath",[]],["moveTo",[476,-13]],["curveTo",[550,-13,610,47,610,121]],["curveTo",[610,196,550,256,476,256]],["curveTo",[401,256,341,196,341,121]],["curveTo",[341,47,401,-13,476,-13]],["closePath",[]]]},{"char":"@","glyph":82,"commands":[["moveTo",[1044,-395]],["curveTo",[1195,-395,1384,-352,1476,-313]],["lineTo",[1433,-173]],["curveTo",[1374,-193,1210,-250,1051,-250]],["curveTo",[555,-250,289,28,289,520]],["curveTo",[289,1005,559,1294,1017,1294]],["curveTo",[1479,1294,1697,973,1697,642]],["curveTo",[1697,316,1662,171,1516,171]],["curveTo",[1430,171,1385,241,1385,321]],["lineTo",[1385,980]],["lineTo",[1227,980]],["lineTo",[1227,896]],["lineTo",[1217,896]],["curveTo",[1181,947,1093,1004,965,1004]],["curveTo",[726,1004,552,803,552,535]],["curveTo",[552,253,689,37,955,37]],["curveTo",[1106,37,1193,112,1228,198]],["lineTo",[1236,198]],["curveTo",[1254,74,1408,30,1512,30]],["curveTo",[1820,30,1856,355,1856,640]],["curveTo",[1856,1046,1571,1438,1023,1438]],["curveTo",[458,1438,122,1094,122,525]],["curveTo",[122,-63,449,-395,1044,-395]],["closePath",[]],["moveTo",[962,190]],["curveTo",[776,190,707,358,707,543]],["curveTo",[707,710,803,853,967,853]],["curveTo",[1141,853,1233,782,1233,543]],["curveTo",[1233,306,1155,190,962,190]],["closePath",[]]]},{"char":"A","glyph":1,"commands":[["moveTo",[52,0]],["lineTo",[254,0]],["lineTo",[398,416]],["lineTo",[1012,416]],["lineTo",[1161,0]],["lineTo",[1361,0]],["lineTo",[813,1490]],["lineTo",[593,1490]],["closePath",[]],["moveTo",[456,582]],["lineTo",[561,884]],["curveTo",[593,976,639,1118,702,1338]],["curveTo",[765,1122,809,984,845,884]],["lineTo",[953,582]],["closePath",[]]]},{"char":"B","glyph":3,"commands":[["moveTo",[180,0]],["lineTo",[725,0]],["curveTo",[1076,0,1224,172,1224,397]],["curveTo",[1224,631,1061,759,922,768]],["lineTo",[922,782]],["curveTo",[1052,817,1166,904,1166,1095]],["curveTo",[1166,1313,1019,1490,708,1490]],["lineTo",[180,1490]],["closePath",[]],["moveTo",[370,168]],["lineTo",[370,676]],["lineTo",[730,676]],["curveTo",[921,676,1041,548,1041,399]],["curveTo",[1041,270,952,168,720,168]],["closePath",[]],["moveTo",[370,836]],["lineTo",[370,1322]],["lineTo",[704,1322]],["curveTo",[897,1322,984,1220,984,1091]],["curveTo",[984,937,858,836,698,836]],["closePath",[]]]},{"char":"C","glyph":4,"commands":[["moveTo",[783,-20]],["curveTo",[1088,-20,1332,166,1384,469]],["lineTo",[1194,469]],["curveTo",[1152,266,978,156,783,156]],["curveTo",[516,156,307,363,307,744]],["curveTo",[307,1126,515,1334,783,1334]],["curveTo",[977,1334,1152,1225,1194,1021]],["lineTo",[1384,1021]],["curveTo",[1331,1328,1085,1510,783,1510]],["curveTo",[401,1510,122,1217,122,744]],["curveTo",[122,272,401,-20,783,-20]],["closePath",[]]]},{"char":"D","glyph":5,"commands":[["moveTo",[645,0]],["curveTo",[1099,0,1356,282,1356,748]],["curveTo",[1356,1210,1099,1490,664,1490]],["lineTo",[180,1490]],["lineTo",[180,0]],["closePath",[]],["moveTo",[370,168]],["lineTo",[370,1322]],["lineTo",[651,1322]],["curveTo",[996,1322,1172,1106,1172,748]],["curveTo",[1172,386,996,168,633,168]],["closePath",[]]]},{"char":"E","glyph":6,"commands":[["moveTo",[180,0]],["lineTo",[1097,0]],["lineTo",[1097,168]],["lineTo",[370,168]],["lineTo",[370,668]],["lineTo",[1039,668]],["lineTo",[1039,836]],["lineTo",[370,836]],["lineTo",[370,1322]],["lineTo",[1088,1322]],["lineTo",[1088,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"F","glyph":7,"commands":[["moveTo",[180,0]],["lineTo",[370,0]],["lineTo",[370,644]],["lineTo",[1013,644]],["lineTo",[1013,812]],["lineTo",[370,812]],["lineTo",[370,1322]],["lineTo",[1081,1322]],["lineTo",[1081,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"G","glyph":8,"commands":[["moveTo",[789,-20]],["curveTo",[1144,-20,1400,216,1400,586]],["lineTo",[1400,749]],["lineTo",[828,749]],["lineTo",[828,583]],["lineTo",[1214,583]],["curveTo",[1209,321,1038,156,789,156]],["curveTo",[515,156,307,364,307,744]],["curveTo",[307,1127,516,1334,778,1334]],["curveTo",[989,1334,1132,1214,1193,1023]],["lineTo",[1389,1023]],["curveTo",[1335,1312,1092,1510,777,1510]],["curveTo",[395,1510,122,1217,122,744]],["curveTo",[122,273,391,-20,789,-20]],["closePath",[]]]},{"char":"H","glyph":9,"commands":[["moveTo",[180,0]],["lineTo",[370,0]],["lineTo",[370,678]],["lineTo",[1152,678]],["lineTo",[1152,0]],["lineTo",[1342,0]],["lineTo",[1342,1490]],["lineTo",[1152,1490]],["lineTo",[1152,846]],["lineTo",[370,846]],["lineTo",[370,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"I","glyph":10,"commands":[["moveTo",[370,1490]],["lineTo",[180,1490]],["lineTo",[180,0]],["lineTo",[370,0]],["closePath",[]]]},{"char":"J","glyph":11,"commands":[["moveTo",[545,-20]],["curveTo",[808,-20,989,140,989,426]],["lineTo",[989,1490]],["lineTo",[799,1490]],["lineTo",[799,426]],["curveTo",[799,244,697,147,545,147]],["curveTo",[392,147,290,244,290,426]],["lineTo",[290,506]],["lineTo",[100,506]],["lineTo",[100,426]],["curveTo",[100,140,282,-20,545,-20]],["closePath",[]]]},{"char":"K","glyph":12,"commands":[["moveTo",[180,0]],["lineTo",[370,0]],["lineTo",[370,489]],["lineTo",[585,716]],["lineTo",[1094,0]],["lineTo",[1319,0]],["lineTo",[716,845]],["lineTo",[1318,1490]],["lineTo",[1069,1490]],["lineTo",[624,1009]],["curveTo",[533,911,450,813,367,706]],["lineTo",[370,980]],["lineTo",[370,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"L","glyph":13,"commands":[["moveTo",[180,0]],["lineTo",[1060,0]],["lineTo",[1060,168]],["lineTo",[370,168]],["lineTo",[370,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"M","glyph":14,"commands":[["moveTo",[180,0]],["lineTo",[361,0]],["lineTo",[361,851]],["curveTo",[361,966,357,1142,354,1284]],["curveTo",[413,1109,471,935,504,851]],["lineTo",[842,0]],["lineTo",[1007,0]],["lineTo",[1340,851]],["curveTo",[1374,938,1436,1117,1490,1287]],["curveTo",[1486,1132,1483,961,1483,851]],["lineTo",[1483,0]],["lineTo",[1670,0]],["lineTo",[1670,1490]],["lineTo",[1399,1490]],["lineTo",[1037,550]],["curveTo",[1008,475,962,328,927,211]],["curveTo",[892,332,845,478,817,550]],["lineTo",[450,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"N","glyph":15,"commands":[["moveTo",[180,0]],["lineTo",[371,0]],["lineTo",[371,850]],["curveTo",[371,922,364,1071,356,1262]],["curveTo",[472,1055,529,955,597,848]],["lineTo",[1137,0]],["lineTo",[1363,0]],["lineTo",[1363,1490]],["lineTo",[1173,1490]],["lineTo",[1173,576]],["curveTo",[1173,496,1174,369,1186,225]],["curveTo",[1118,360,1050,474,1010,537]],["lineTo",[404,1490]],["lineTo",[180,1490]],["closePath",[]]]},{"char":"O","glyph":16,"commands":[["moveTo",[784,-20]],["curveTo",[1166,-20,1444,272,1444,744]],["curveTo",[1444,1217,1166,1510,784,1510]],["curveTo",[402,1510,122,1217,122,744]],["curveTo",[122,272,402,-20,784,-20]],["closePath",[]],["moveTo",[784,156]],["curveTo",[516,156,307,364,307,744]],["curveTo",[307,1126,516,1334,784,1334]],["curveTo",[1050,1334,1259,1127,1259,744]],["curveTo",[1259,363,1050,156,784,156]],["closePath",[]]]},{"char":"P","glyph":18,"commands":[["moveTo",[180,0]],["lineTo",[370,0]],["lineTo",[370,539]],["lineTo",[691,539]],["curveTo",[1038,539,1198,750,1198,1016]],["curveTo",[1198,1281,1038,1490,690,1490]],["lineTo",[180,1490]],["closePath",[]],["moveTo",[370,706]],["lineTo",[370,1323]],["lineTo",[680,1323]],["curveTo",[918,1323,1009,1192,1009,1016]],["curveTo",[1009,839,918,706,682,706]],["closePath",[]]]},{"char":"Q","glyph":19,"commands":[["moveTo",[784,-20]],["curveTo",[880,-20,969,-2,1049,33]],["lineTo",[1178,-140]],["lineTo",[1376,-140]],["lineTo",[1184,115]],["curveTo",[1345,245,1444,462,1444,744]],["curveTo",[1444,1217,1166,1510,784,1510]],["curveTo",[402,1510,122,1217,122,744]],["curveTo",[122,272,402,-20,784,-20]],["closePath",[]],["moveTo",[722,466]],["lineTo",[938,181]],["curveTo",[890,165,838,156,784,156]],["curveTo",[516,156,307,364,307,744]],["curveTo",[307,1126,516,1334,784,1334]],["curveTo",[1050,1334,1259,1127,1259,744]],["curveTo",[1259,522,1188,359,1076,261]],["lineTo",[920,466]],["closePath",[]]]},{"char":"R","glyph":20,"commands":[["moveTo",[180,0]],["lineTo",[370,0]],["lineTo",[370,578]],["lineTo",[691,578]],["curveTo",[703,578,714,578,726,579]],["lineTo",[1036,0]],["lineTo",[1256,0]],["lineTo",[922,613]],["curveTo",[1110,679,1198,832,1198,1030]],["curveTo",[1198,1296,1038,1490,690,1490]],["lineTo",[180,1490]],["closePath",[]],["moveTo",[370,747]],["lineTo",[370,1323]],["lineTo",[680,1323]],["curveTo",[917,1323,1009,1207,1009,1030]],["curveTo",[1009,855,917,747,682,747]],["closePath",[]]]},{"char":"S","glyph":21,"commands":[["moveTo",[657,-26]],["curveTo",[1001,-26,1198,162,1198,406]],["curveTo",[1198,684,936,774,776,818]],["lineTo",[627,859]],["curveTo",[518,889,343,944,343,1100]],["curveTo",[343,1240,471,1342,664,1342]],["curveTo",[841,1342,975,1259,992,1114]],["lineTo",[1178,1114]],["curveTo",[1169,1339,964,1510,670,1510]],["curveTo",[382,1510,158,1343,158,1091]],["curveTo",[158,893,298,770,538,702]],["lineTo",[718,651]],["curveTo",[877,606,1013,553,1013,409]],["curveTo",[1013,250,859,144,657,144]],["curveTo",[482,144,326,221,311,390]],["lineTo",[116,390]],["curveTo",[133,140,334,-26,657,-26]],["closePath",[]]]},{"char":"T","glyph":22,"commands":[["moveTo",[98,1322]],["lineTo",[567,1322]],["lineTo",[567,0]],["lineTo",[757,0]],["lineTo",[757,1322]],["lineTo",[1224,1322]],["lineTo",[1224,1490]],["lineTo",[98,1490]],["closePath",[]]]},{"char":"U","glyph":23,"commands":[["moveTo",[763,-24]],["curveTo",[1114,-24,1344,200,1344,507]],["lineTo",[1344,1490]],["lineTo",[1154,1490]],["lineTo",[1154,522]],["curveTo",[1154,308,1006,152,763,152]],["curveTo",[519,152,370,308,370,522]],["lineTo",[370,1490]],["lineTo",[180,1490]],["lineTo",[180,507]],["curveTo",[180,200,410,-24,763,-24]],["closePath",[]]]},{"char":"V","glyph":25,"commands":[["moveTo",[600,0]],["lineTo",[819,0]],["lineTo",[1361,1490]],["lineTo",[1159,1490]],["lineTo",[852,606]],["curveTo",[820,514,773,372,710,154]],["curveTo",[648,367,600,514,567,606]],["lineTo",[252,1490]],["lineTo",[52,1490]],["closePath",[]]]},{"char":"W","glyph":26,"commands":[["moveTo",[458,0]],["lineTo",[680,0]],["lineTo",[935,944]],["curveTo",[964,1052,986,1153,1008,1275]],["curveTo",[1031,1144,1055,1039,1081,944]],["lineTo",[1337,0]],["lineTo",[1558,0]],["lineTo",[1966,1490]],["lineTo",[1770,1490]],["lineTo",[1535,576]],["curveTo",[1503,450,1473,321,1447,183]],["curveTo",[1419,321,1389,450,1356,576]],["lineTo",[1117,1490]],["lineTo",[901,1490]],["lineTo",[660,576]],["curveTo",[626,448,597,317,570,177]],["curveTo",[543,317,514,448,482,576]],["lineTo",[246,1490]],["lineTo",[52,1490]],["closePath",[]]]},{"char":"X","glyph":27,"commands":[["moveTo",[57,0]],["lineTo",[273,0]],["lineTo",[536,378]],["curveTo",[613,488,650,546,699,640]],["curveTo",[749,542,785,487,860,378]],["lineTo",[1121,0]],["lineTo",[1340,0]],["lineTo",[811,770]],["lineTo",[1312,1490]],["lineTo",[1097,1490]],["lineTo",[878,1174]],["curveTo",[790,1047,754,989,701,885]],["curveTo",[647,990,612,1046,525,1174]],["lineTo",[310,1490]],["lineTo",[90,1490]],["lineTo",[591,763]],["closePath",[]]]},{"char":"Y","glyph":28,"commands":[["moveTo",[600,0]],["lineTo",[790,0]],["lineTo",[790,610]],["lineTo",[1338,1490]],["lineTo",[1119,1490]],["lineTo",[825,1009]],["curveTo",[777,930,737,862,697,766]],["curveTo",[657,859,617,927,567,1009]],["lineTo",[272,1490]],["lineTo",[52,1490]],["lineTo",[600,610]],["closePath",[]]]},{"char":"Z","glyph":29,"commands":[["moveTo",[130,0]],["lineTo",[1166,0]],["lineTo",[1166,168]],["lineTo",[640,168]],["curveTo",[541,168,443,167,345,163]],["curveTo",[399,234,453,307,502,379]],["lineTo",[1158,1354]],["lineTo",[1158,1490]],["lineTo",[122,1490]],["lineTo",[122,1322]],["lineTo",[645,1322]],["curveTo",[744,1322,844,1323,943,1327]],["curveTo",[892,1260,841,1192,796,1125]],["lineTo",[130,134]],["closePath",[]]]},{"char":"[","glyph":78,"commands":[["moveTo",[226,-279]],["lineTo",[624,-279]],["lineTo",[624,-126]],["lineTo",[398,-126]],["lineTo",[398,1428]],["lineTo",[624,1428]],["lineTo",[624,1581]],["lineTo",[226,1581]],["closePath",[]]]},{"char":"\\","glyph":86,"commands":[["moveTo",[526,-224]],["lineTo",[692,-224]],["lineTo",[212,1560]],["lineTo",[46,1560]],["closePath",[]]]},{"char":"]","glyph":79,"commands":[["moveTo",[123,-279]],["lineTo",[521,-279]],["lineTo",[521,1581]],["lineTo",[123,1581]],["lineTo",[123,1428]],["lineTo",[349,1428]],["lineTo",[349,-126]],["lineTo",[123,-126]],["closePath",[]]]},{"char":"^","glyph":100,"commands":[["moveTo",[82,814]],["lineTo",[261,814]],["lineTo",[476,1285]],["lineTo",[489,1285]],["lineTo",[706,814]],["lineTo",[883,814]],["lineTo",[582,1443]],["lineTo",[383,1443]],["closePath",[]]]},{"char":"_","glyph":99,"commands":[["moveTo",[934,0]],["lineTo",[0,0]],["lineTo",[0,-166]],["lineTo",[934,-166]],["closePath",[]]]},{"char":"`","glyph":103,"commands":[["moveTo",[343,1264]],["lineTo",[491,1264]],["lineTo",[351,1558]],["lineTo",[150,1558]],["closePath",[]]]},{"char":"a","glyph":30,"commands":[["moveTo",[471,-26]],["curveTo",[660,-26,765,73,803,152]],["lineTo",[815,152]],["lineTo",[815,0]],["lineTo",[992,0]],["lineTo",[992,737]],["curveTo",[992,1086,723,1132,573,1132]],["curveTo",[400,1132,208,1070,122,867]],["lineTo",[295,810]],["curveTo",[330,888,417,974,578,974]],["curveTo",[733,974,812,894,812,755]],["lineTo",[812,749]],["curveTo",[812,666,724,672,525,647]],["curveTo",[318,621,90,574,90,318]],["curveTo",[90,98,259,-26,471,-26]],["closePath",[]],["moveTo",[502,133]],["curveTo",[368,133,271,193,271,309]],["curveTo",[271,436,386,478,517,495]],["curveTo",[586,504,782,524,812,559]],["lineTo",[812,404]],["curveTo",[812,267,701,133,502,133]],["closePath",[]]]},{"char":"b","glyph":32,"commands":[["moveTo",[677,-24]],["curveTo",[956,-24,1150,200,1150,556]],["curveTo",[1150,910,955,1132,676,1132]],["curveTo",[458,1132,389,998,352,939]],["lineTo",[338,939]],["lineTo",[338,1490]],["lineTo",[158,1490]],["lineTo",[158,0]],["lineTo",[332,0]],["lineTo",[332,173]],["lineTo",[352,173]],["curveTo",[389,111,464,-24,677,-24]],["closePath",[]],["moveTo",[650,137]],["curveTo",[447,137,336,309,336,558]],["curveTo",[336,805,444,971,650,971]],["curveTo",[862,971,967,790,967,558]],["curveTo",[967,323,859,137,650,137]],["closePath",[]]]},{"char":"c","glyph":33,"commands":[["moveTo",[613,-24]],["curveTo",[832,-24,1010,95,1070,294]],["lineTo",[898,343]],["curveTo",[865,222,763,137,613,137]],["curveTo",[390,137,287,332,287,552]],["curveTo",[287,775,390,971,613,971]],["curveTo",[760,971,860,889,893,774]],["lineTo",[1066,823]],["curveTo",[1005,1018,829,1132,613,1132]],["curveTo",[309,1132,104,902,104,552]],["curveTo",[104,207,309,-24,613,-24]],["closePath",[]]]},{"char":"d","glyph":34,"commands":[["moveTo",[577,-24]],["curveTo",[790,-24,865,111,902,173]],["lineTo",[922,173]],["lineTo",[922,0]],["lineTo",[1096,0]],["lineTo",[1096,1490]],["lineTo",[916,1490]],["lineTo",[916,939]],["lineTo",[902,939]],["curveTo",[865,998,796,1132,578,1132]],["curveTo",[299,1132,104,910,104,556]],["curveTo",[104,200,298,-24,577,-24]],["closePath",[]],["moveTo",[604,137]],["curveTo",[395,137,287,323,287,558]],["curveTo",[287,790,392,971,604,971]],["curveTo",[810,971,918,805,918,558]],["curveTo",[918,309,807,137,604,137]],["closePath",[]]]},{"char":"e","glyph":35,"commands":[["moveTo",[628,-24]],["curveTo",[857,-24,1024,91,1076,260]],["lineTo",[902,308]],["curveTo",[860,194,761,136,629,136]],["curveTo",[431,136,295,264,286,498]],["lineTo",[1094,498]],["lineTo",[1094,573]],["curveTo",[1094,986,844,1132,607,1132]],["curveTo",[302,1132,104,893,104,550]],["curveTo",[104,206,304,-24,628,-24]],["closePath",[]],["moveTo",[287,650]],["curveTo",[298,821,415,972,607,972]],["curveTo",[791,972,894,840,911,650]],["closePath",[]]]},{"char":"f","glyph":37,"commands":[["moveTo",[678,1118]],["lineTo",[420,1118]],["lineTo",[420,1227]],["curveTo",[420,1335,468,1393,579,1393]],["curveTo",[629,1393,657,1382,676,1376]],["lineTo",[726,1530]],["curveTo",[699,1542,643,1560,558,1560]],["curveTo",[395,1560,240,1461,240,1267]],["lineTo",[240,1118]],["lineTo",[20,1118]],["lineTo",[20,964]],["lineTo",[240,964]],["lineTo",[240,0]],["lineTo",[420,0]],["lineTo",[420,964]],["lineTo",[678,964]],["closePath",[]]]},{"char":"g","glyph":38,"commands":[["moveTo",[611,-442]],["curveTo",[883,-442,1098,-318,1098,-29]],["lineTo",[1098,1118]],["lineTo",[923,1118]],["lineTo",[923,939]],["lineTo",[906,939]],["curveTo",[868,999,796,1132,580,1132]],["curveTo",[301,1132,104,910,104,564]],["curveTo",[104,213,308,18,576,18]],["curveTo",[792,18,863,141,901,205]],["lineTo",[918,205]],["lineTo",[918,-19]],["curveTo",[918,-203,789,-285,611,-285]],["curveTo",[414,-285,344,-185,297,-120]],["lineTo",[151,-214]],["curveTo",[220,-337,363,-442,611,-442]],["closePath",[]],["moveTo",[606,178]],["curveTo",[396,178,287,338,287,568]],["curveTo",[287,791,393,971,606,971]],["curveTo",[812,971,920,805,920,568]],["curveTo",[920,324,809,178,606,178]],["closePath",[]]]},{"char":"h","glyph":39,"commands":[["moveTo",[338,670]],["curveTo",[338,861,459,970,620,970]],["curveTo",[776,970,872,867,872,695]],["lineTo",[872,0]],["lineTo",[1053,0]],["lineTo",[1053,710]],["curveTo",[1053,993,900,1132,674,1132]],["curveTo",[528,1132,411,1073,338,924]],["lineTo",[338,1490]],["lineTo",[158,1490]],["lineTo",[158,0]],["lineTo",[338,0]],["closePath",[]]]},{"char":"i","glyph":40,"commands":[["moveTo",[158,0]],["lineTo",[338,0]],["lineTo",[338,1118]],["lineTo",[158,1118]],["closePath",[]],["moveTo",[249,1301]],["curveTo",[318,1301,375,1354,375,1420]],["curveTo",[375,1486,318,1539,249,1539]],["curveTo",[181,1539,124,1486,124,1420]],["curveTo",[124,1354,181,1301,249,1301]],["closePath",[]]]},{"char":"j","glyph":41,"commands":[["moveTo",[157,1118]],["lineTo",[157,-80]],["curveTo",[157,-191,111,-252,7,-252]],["lineTo",[-26,-252]],["lineTo",[-26,-418]],["lineTo",[10,-418]],["curveTo",[227,-418,339,-293,338,-80]],["lineTo",[338,1118]],["closePath",[]],["moveTo",[248,1301]],["curveTo",[317,1301,374,1354,374,1420]],["curveTo",[374,1486,317,1539,248,1539]],["curveTo",[180,1539,123,1486,123,1420]],["curveTo",[123,1354,180,1301,248,1301]],["closePath",[]]]},{"char":"k","glyph":42,"commands":[["moveTo",[158,0]],["lineTo",[338,0]],["lineTo",[338,412]],["lineTo",[456,523]],["lineTo",[865,0]],["lineTo",[1096,0]],["lineTo",[593,638]],["lineTo",[1061,1118]],["lineTo",[838,1118]],["lineTo",[360,625]],["lineTo",[338,625]],["lineTo",[338,1490]],["lineTo",[158,1490]],["closePath",[]]]},{"char":"l","glyph":43,"commands":[["moveTo",[338,1490]],["lineTo",[158,1490]],["lineTo",[158,0]],["lineTo",[338,0]],["closePath",[]]]},{"char":"m","glyph":44,"commands":[["moveTo",[158,0]],["lineTo",[338,0]],["lineTo",[338,704]],["curveTo",[338,882,457,974,588,974]],["curveTo",[714,974,807,891,807,767]],["lineTo",[807,0]],["lineTo",[987,0]],["lineTo",[987,730]],["curveTo",[987,871,1079,974,1230,974]],["curveTo",[1350,974,1455,910,1455,749]],["lineTo",[1455,0]],["lineTo",[1636,0]],["lineTo",[1636,754]],["curveTo",[1636,1013,1478,1138,1284,1138]],["curveTo",[1122,1138,985,1051,932,910]],["curveTo",[892,1048,794,1138,647,1138]],["curveTo",[515,1138,389,1064,334,913]],["lineTo",[333,1118]],["lineTo",[158,1118]],["closePath",[]]]},{"char":"n","glyph":45,"commands":[["moveTo",[338,670]],["curveTo",[338,861,459,970,620,970]],["curveTo",[776,970,872,867,872,695]],["lineTo",[872,0]],["lineTo",[1052,0]],["lineTo",[1052,710]],["curveTo",[1052,993,900,1132,674,1132]],["curveTo",[524,1132,404,1069,332,911]],["lineTo",[331,1118]],["lineTo",[158,1118]],["lineTo",[158,0]],["lineTo",[338,0]],["closePath",[]]]},{"char":"o","glyph":46,"commands":[["moveTo",[613,-24]],["curveTo",[918,-24,1124,207,1124,552]],["curveTo",[1124,902,918,1132,613,1132]],["curveTo",[309,1132,104,902,104,552]],["curveTo",[104,207,309,-24,613,-24]],["closePath",[]],["moveTo",[613,137]],["curveTo",[390,137,287,332,287,552]],["curveTo",[287,775,390,971,613,971]],["curveTo",[839,971,942,774,942,552]],["curveTo",[942,332,839,137,613,137]],["closePath",[]]]},{"char":"p","glyph":48,"commands":[["moveTo",[158,-418]],["lineTo",[338,-418]],["lineTo",[338,173]],["lineTo",[352,173]],["curveTo",[389,111,464,-24,677,-24]],["curveTo",[956,-24,1150,200,1150,556]],["curveTo",[1150,910,955,1132,676,1132]],["curveTo",[458,1132,389,998,352,939]],["lineTo",[332,939]],["lineTo",[332,1118]],["lineTo",[158,1118]],["closePath",[]],["moveTo",[650,137]],["curveTo",[447,137,336,309,336,558]],["curveTo",[336,805,444,971,650,971]],["curveTo",[862,971,967,790,967,558]],["curveTo",[967,323,859,137,650,137]],["closePath",[]]]},{"char":"q","glyph":49,"commands":[["moveTo",[1096,-418]],["lineTo",[1096,1118]],["lineTo",[922,1118]],["lineTo",[922,939]],["lineTo",[902,939]],["curveTo",[865,998,796,1132,578,1132]],["curveTo",[299,1132,104,910,104,556]],["curveTo",[104,200,298,-24,577,-24]],["curveTo",[790,-24,865,111,902,173]],["lineTo",[916,173]],["lineTo",[916,-418]],["closePath",[]],["moveTo",[604,137]],["curveTo",[395,137,287,323,287,558]],["curveTo",[287,790,392,971,604,971]],["curveTo",[810,971,918,805,918,558]],["curveTo",[918,309,807,137,604,137]],["closePath",[]]]},{"char":"r","glyph":50,"commands":[["moveTo",[158,0]],["lineTo",[338,0]],["lineTo",[338,700]],["curveTo",[338,851,456,960,617,960]],["curveTo",[665,960,714,952,725,950]],["lineTo",[725,1131]],["curveTo",[704,1132,660,1134,634,1134]],["curveTo",[501,1134,385,1059,344,946]],["lineTo",[332,946]],["lineTo",[332,1118]],["lineTo",[158,1118]],["closePath",[]]]},{"char":"s","glyph":51,"commands":[["moveTo",[538,-24]],["curveTo",[800,-24,979,119,979,315]],["curveTo",[979,468,884,569,681,617]],["lineTo",[512,657]],["curveTo",[381,688,324,734,324,817]],["curveTo",[324,910,424,979,557,979]],["curveTo",[701,979,764,901,795,821]],["lineTo",[958,863]],["curveTo",[907,1020,786,1132,556,1132]],["curveTo",[318,1132,142,998,142,806]],["curveTo",[142,649,239,549,442,501]],["lineTo",[628,457]],["curveTo",[738,431,792,380,792,303]],["curveTo",[792,211,692,134,536,134]],["curveTo",[402,134,311,189,279,312]],["lineTo",[108,271]],["curveTo",[148,78,308,-24,538,-24]],["closePath",[]]]},{"char":"t","glyph":53,"commands":[["moveTo",[598,1118]],["lineTo",[368,1118]],["lineTo",[368,1384]],["lineTo",[188,1384]],["lineTo",[188,1118]],["lineTo",[20,1118]],["lineTo",[20,964]],["lineTo",[188,964]],["lineTo",[188,276]],["curveTo",[188,93,302,-14,495,-14]],["curveTo",[541,-14,592,-7,629,6]],["lineTo",[592,158]],["curveTo",[562,152,523,146,500,146]],["curveTo",[405,146,368,190,368,290]],["lineTo",[368,964]],["lineTo",[598,964]],["closePath",[]]]},{"char":"u","glyph":54,"commands":[["moveTo",[537,-14]],["curveTo",[684,-14,806,46,879,209]],["lineTo",[879,0]],["lineTo",[1053,0]],["lineTo",[1053,1118]],["lineTo",[872,1118]],["lineTo",[872,448]],["curveTo",[872,257,751,148,591,148]],["curveTo",[435,148,338,251,338,423]],["lineTo",[338,1118]],["lineTo",[158,1118]],["lineTo",[158,408]],["curveTo",[158,125,311,-14,537,-14]],["closePath",[]]]},{"char":"v","glyph":56,"commands":[["moveTo",[481,0]],["lineTo",[670,0]],["lineTo",[1097,1118]],["lineTo",[900,1118]],["lineTo",[662,455]],["curveTo",[627,358,601,260,576,167]],["curveTo",[550,260,524,358,489,455]],["lineTo",[251,1118]],["lineTo",[54,1118]],["closePath",[]]]},{"char":"w","glyph":57,"commands":[["moveTo",[409,0]],["lineTo",[588,0]],["lineTo",[733,506]],["curveTo",[769,631,804,771,838,913]],["curveTo",[872,770,906,634,943,506]],["lineTo",[1088,0]],["lineTo",[1267,0]],["lineTo",[1606,1118]],["lineTo",[1415,1118]],["lineTo",[1279,630]],["curveTo",[1245,508,1208,367,1171,196]],["curveTo",[1136,369,1100,511,1068,630]],["lineTo",[936,1118]],["lineTo",[744,1118]],["lineTo",[610,630]],["curveTo",[576,506,540,363,504,193]],["curveTo",[467,371,429,513,397,630]],["lineTo",[261,1118]],["lineTo",[70,1118]],["closePath",[]]]},{"char":"x","glyph":58,"commands":[["moveTo",[65,0]],["lineTo",[273,0]],["lineTo",[439,251]],["curveTo",[489,327,523,395,557,458]],["curveTo",[594,395,631,327,680,251]],["lineTo",[844,0]],["lineTo",[1053,0]],["lineTo",[661,564]],["lineTo",[1035,1118]],["lineTo",[829,1118]],["lineTo",[687,901]],["curveTo",[635,822,601,750,565,683]],["curveTo",[526,750,488,822,437,901]],["lineTo",[298,1118]],["lineTo",[88,1118]],["lineTo",[458,574]],["closePath",[]]]},{"char":"y","glyph":59,"commands":[["moveTo",[140,-405]],["curveTo",[159,-414,208,-426,272,-426]],["curveTo",[430,-426,538,-345,606,-167]],["lineTo",[1097,1118]],["lineTo",[901,1118]],["lineTo",[661,455]],["curveTo",[626,358,600,261,575,168]],["curveTo",[549,261,524,357,489,455]],["lineTo",[251,1118]],["lineTo",[54,1118]],["lineTo",[481,-6]],["lineTo",[449,-99]],["curveTo",[394,-259,315,-283,205,-254]],["lineTo",[186,-249]],["closePath",[]]]},{"char":"z","glyph":60,"commands":[["moveTo",[126,0]],["lineTo",[1005,0]],["lineTo",[1005,167]],["lineTo",[376,167]],["lineTo",[376,178]],["lineTo",[985,975]],["lineTo",[985,1118]],["lineTo",[146,1118]],["lineTo",[146,951]],["lineTo",[753,951]],["lineTo",[753,940]],["lineTo",[126,134]],["closePath",[]]]},{"char":"{","glyph":80,"commands":[["moveTo",[750,-279]],["lineTo",[750,-130]],["curveTo",[580,-130,528,-54,528,115]],["lineTo",[528,399]],["curveTo",[528,516,491,606,323,641]],["lineTo",[323,661]],["curveTo",[491,695,528,784,528,901]],["lineTo",[528,1187]],["curveTo",[528,1356,581,1431,750,1430]],["lineTo",[750,1581]],["curveTo",[464,1581,362,1463,362,1190]],["lineTo",[362,960]],["curveTo",[362,814,314,750,178,742]],["lineTo",[144,742]],["lineTo",[144,560]],["lineTo",[177,560]],["curveTo",[314,551,362,487,362,340]],["lineTo",[362,112]],["curveTo",[362,-162,464,-279,750,-279]],["closePath",[]]]},{"char":"|","glyph":85,"commands":[["moveTo",[419,1970]],["lineTo",[262,1970]],["lineTo",[262,-480]],["lineTo",[419,-480]],["closePath",[]]]},{"char":"}","glyph":81,"commands":[["moveTo",[123,-279]],["curveTo",[408,-279,510,-162,510,112]],["lineTo",[510,340]],["curveTo",[510,497,565,560,729,561]],["lineTo",[729,741]],["lineTo",[722,741]],["curveTo",[564,743,510,804,510,960]],["lineTo",[510,1190]],["curveTo",[510,1463,408,1581,123,1581]],["lineTo",[123,1430]],["curveTo",[293,1430,345,1355,345,1187]],["lineTo",[345,901]],["curveTo",[345,784,382,695,549,661]],["lineTo",[549,641]],["curveTo",[382,606,345,515,345,399]],["lineTo",[345,115]],["curveTo",[345,-54,293,-130,123,-130]],["closePath",[]]]},{"char":"~","glyph":98,"commands":[["moveTo",[168,439]],["lineTo",[332,439]],["curveTo",[330,566,387,616,465,616]],["curveTo",[515,616,548,594,620,532]],["curveTo",[723,443,796,400,897,400]],["curveTo",[1044,400,1190,493,1187,740]],["lineTo",[1023,740]],["curveTo",[1023,624,972,563,890,563]],["curveTo",[839,563,805,586,735,647]],["curveTo",[627,741,556,780,458,780]],["curveTo",[309,780,164,687,168,439]],["closePath",[]]]},{"char":"Ä","glyph":2,"commands":[["moveTo",[52,0]],["lineTo",[254,0]],["lineTo",[398,416]],["lineTo",[1012,416]],["lineTo",[1161,0]],["lineTo",[1361,0]],["lineTo",[813,1490]],["lineTo",[593,1490]],["closePath",[]],["moveTo",[456,582]],["lineTo",[561,884]],["curveTo",[593,976,639,1118,702,1338]],["curveTo",[765,1122,809,984,845,884]],["lineTo",[953,582]],["closePath",[]],["moveTo",[496,1673]],["curveTo",[561,1673,614,1727,614,1792]],["curveTo",[614,1857,561,1910,496,1910]],["curveTo",[430,1910,377,1857,377,1792]],["curveTo",[377,1727,430,1673,496,1673]],["closePath",[]],["moveTo",[917,1673]],["curveTo",[982,1673,1035,1727,1035,1792]],["curveTo",[1035,1857,982,1910,917,1910]],["curveTo",[851,1910,798,1857,798,1792]],["curveTo",[798,1727,851,1673,917,1673]],["closePath",[]]]},{"char":"Ö","glyph":17,"commands":[["moveTo",[784,-20]],["curveTo",[1166,-20,1444,272,1444,744]],["curveTo",[1444,1217,1166,1510,784,1510]],["curveTo",[402,1510,122,1217,122,744]],["curveTo",[122,272,402,-20,784,-20]],["closePath",[]],["moveTo",[784,156]],["curveTo",[516,156,307,364,307,744]],["curveTo",[307,1126,516,1334,784,1334]],["curveTo",[1050,1334,1259,1127,1259,744]],["curveTo",[1259,363,1050,156,784,156]],["closePath",[]],["moveTo",[574,1673]],["curveTo",[639,1673,692,1727,692,1792]],["curveTo",[692,1857,639,1910,574,1910]],["curveTo",[508,1910,455,1857,455,1792]],["curveTo",[455,1727,508,1673,574,1673]],["closePath",[]],["moveTo",[995,1673]],["curveTo",[1060,1673,1113,1727,1113,1792]],["curveTo",[1113,1857,1060,1910,995,1910]],["curveTo",[929,1910,876,1857,876,1792]],["curveTo",[876,1727,929,1673,995,1673]],["closePath",[]]]},{"char":"Ü","glyph":24,"commands":[["moveTo",[763,-24]],["curveTo",[1114,-24,1344,200,1344,507]],["lineTo",[1344,1490]],["lineTo",[1154,1490]],["lineTo",[1154,522]],["curveTo",[1154,308,1006,152,763,152]],["curveTo",[519,152,370,308,370,522]],["lineTo",[370,1490]],["lineTo",[180,1490]],["lineTo",[180,507]],["curveTo",[180,200,410,-24,763,-24]],["closePath",[]],["moveTo",[553,1673]],["curveTo",[618,1673,671,1727,671,1792]],["curveTo",[671,1857,618,1910,553,1910]],["curveTo",[487,1910,434,1857,434,1792]],["curveTo",[434,1727,487,1673,553,1673]],["closePath",[]],["moveTo",[974,1673]],["curveTo",[1039,1673,1092,1727,1092,1792]],["curveTo",[1092,1857,1039,1910,974,1910]],["curveTo",[908,1910,855,1857,855,1792]],["curveTo",[855,1727,908,1673,974,1673]],["closePath",[]]]},{"char":"ä","glyph":31,"commands":[["moveTo",[471,-26]],["curveTo",[660,-26,765,73,803,152]],["lineTo",[815,152]],["lineTo",[815,0]],["lineTo",[992,0]],["lineTo",[992,737]],["curveTo",[992,1086,723,1132,573,1132]],["curveTo",[400,1132,208,1070,122,867]],["lineTo",[295,810]],["curveTo",[330,888,417,974,578,974]],["curveTo",[733,974,812,894,812,755]],["lineTo",[812,749]],["curveTo",[812,666,724,672,525,647]],["curveTo",[318,621,90,574,90,318]],["curveTo",[90,98,259,-26,471,-26]],["closePath",[]],["moveTo",[502,133]],["curveTo",[368,133,271,193,271,309]],["curveTo",[271,436,386,478,517,495]],["curveTo",[586,504,782,524,812,559]],["lineTo",[812,404]],["curveTo",[812,267,701,133,502,133]],["closePath",[]],["moveTo",[348,1301]],["curveTo",[413,1301,466,1355,466,1420]],["curveTo",[466,1485,413,1538,348,1538]],["curveTo",[282,1538,229,1485,229,1420]],["curveTo",[229,1355,282,1301,348,1301]],["closePath",[]],["moveTo",[769,1301]],["curveTo",[834,1301,887,1355,887,1420]],["curveTo",[887,1485,834,1538,769,1538]],["curveTo",[703,1538,650,1485,650,1420]],["curveTo",[650,1355,703,1301,769,1301]],["closePath",[]]]},{"char":"ö","glyph":47,"commands":[["moveTo",[613,-24]],["curveTo",[918,-24,1124,207,1124,552]],["curveTo",[1124,902,918,1132,613,1132]],["curveTo",[309,1132,104,902,104,552]],["curveTo",[104,207,309,-24,613,-24]],["closePath",[]],["moveTo",[613,137]],["curveTo",[390,137,287,332,287,552]],["curveTo",[287,775,390,971,613,971]],["curveTo",[839,971,942,774,942,552]],["curveTo",[942,332,839,137,613,137]],["closePath",[]],["moveTo",[403,1301]],["curveTo",[468,1301,521,1355,521,1420]],["curveTo",[521,1485,468,1538,403,1538]],["curveTo",[337,1538,284,1485,284,1420]],["curveTo",[284,1355,337,1301,403,1301]],["closePath",[]],["moveTo",[824,1301]],["curveTo",[889,1301,942,1355,942,1420]],["curveTo",[942,1485,889,1538,824,1538]],["curveTo",[758,1538,705,1485,705,1420]],["curveTo",[705,1355,758,1301,824,1301]],["closePath",[]]]},{"char":"ü","glyph":55,"commands":[["moveTo",[537,-14]],["curveTo",[684,-14,806,46,879,209]],["lineTo",[879,0]],["lineTo",[1053,0]],["lineTo",[1053,1118]],["lineTo",[872,1118]],["lineTo",[872,448]],["curveTo",[872,257,751,148,591,148]],["curveTo",[435,148,338,251,338,423]],["lineTo",[338,1118]],["lineTo",[158,1118]],["lineTo",[158,408]],["curveTo",[158,125,311,-14,537,-14]],["closePath",[]],["moveTo",[395,1301]],["curveTo",[460,1301,513,1355,513,1420]],["curveTo",[513,1485,460,1538,395,1538]],["curveTo",[329,1538,276,1485,276,1420]],["curveTo",[276,1355,329,1301,395,1301]],["closePath",[]],["moveTo",[816,1301]],["curveTo",[881,1301,934,1355,934,1420]],["curveTo",[934,1485,881,1538,816,1538]],["curveTo",[750,1538,697,1485,697,1420]],["curveTo",[697,1355,750,1301,816,1301]],["closePath",[]]]},{"char":"ß","glyph":52,"commands":[["moveTo",[180,0]],["lineTo",[362,0]],["lineTo",[362,1104]],["curveTo",[362,1245,471,1343,621,1343]],["curveTo",[778,1343,875,1234,875,1097]],["curveTo",[875,965,785,841,641,841]],["lineTo",[550,841]],["lineTo",[550,671]],["lineTo",[679,671]],["curveTo",[854,671,964,563,964,412]],["curveTo",[964,269,864,167,709,167]],["lineTo",[544,167]],["lineTo",[544,0]],["lineTo",[709,0]],["curveTo",[973,0,1146,173,1146,410]],["curveTo",[1146,615,1016,756,823,775]],["lineTo",[823,789]],["curveTo",[965,838,1057,959,1057,1125]],["curveTo",[1057,1364,867,1510,616,1510]],["curveTo",[360,1510,180,1357,180,1107]],["closePath",[]]]},{"char":"€","glyph":62,"commands":[["moveTo",[1070,963]],["lineTo",[442,963]],["curveTo",[499,1206,654,1334,847,1334]],["curveTo",[975,1334,1081,1278,1170,1198]],["lineTo",[1243,1357]],["curveTo",[1135,1458,997,1510,847,1510]],["curveTo",[547,1510,320,1306,253,963]],["lineTo",[105,963]],["lineTo",[52,818]],["lineTo",[235,818]],["curveTo",[234,794,233,769,233,744]],["curveTo",[233,720,234,696,235,672]],["lineTo",[105,672]],["lineTo",[52,525]],["lineTo",[253,525]],["curveTo",[320,183,547,-20,847,-20]],["curveTo",[996,-20,1138,29,1239,125]],["lineTo",[1164,282]],["curveTo",[1082,213,976,156,847,156]],["curveTo",[655,156,500,283,443,525]],["lineTo",[876,525]],["lineTo",[943,672]],["lineTo",[421,672]],["curveTo",[420,695,419,719,419,744]],["curveTo",[419,769,420,794,421,818]],["lineTo",[1002,818]],["closePath",[]]]},{"char":"é","glyph":36,"commands":[["moveTo",[628,-24]],["curveTo",[857,-24,1024,91,1076,260]],["lineTo",[902,308]],["curveTo",[860,194,761,136,629,136]],["curveTo",[431,136,295,264,286,498]],["lineTo",[1094,498]],["lineTo",[1094,573]],["curveTo",[1094,986,844,1132,607,1132]],["curveTo",[302,1132,104,893,104,550]],["curveTo",[104,206,304,-24,628,-24]],["closePath",[]],["moveTo",[287,650]],["curveTo",[298,821,415,972,607,972]],["curveTo",[791,972,894,840,911,650]],["closePath",[]],["moveTo",[524,1264]],["lineTo",[671,1264]],["lineTo",[865,1558]],["lineTo",[664,1558]],["closePath",[]]]}]} \ No newline at end of file diff --git a/tests/CADability.Tests/Files/Fonts/InterCidTest.otf b/tests/CADability.Tests/Files/Fonts/InterCidTest.otf new file mode 100644 index 0000000000000000000000000000000000000000..0e4fba29320651ba8ce0b9732dacd99666f9602b GIT binary patch literal 12200 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 JJSckj{{VU8fY|^5 literal 0 HcmV?d00001 diff --git a/tests/CADability.Tests/Files/Fonts/make_cff_test_fonts.py b/tests/CADability.Tests/Files/Fonts/make_cff_test_fonts.py new file mode 100644 index 000000000..f26256056 --- /dev/null +++ b/tests/CADability.Tests/Files/Fonts/make_cff_test_fonts.py @@ -0,0 +1,187 @@ +# Writes the CFF test fonts of TrueTypeFontTests and their reference data with fontTools (pip install fonttools): +# +# InterCffTest.otf name-keyed CFF font +# InterCidTest.otf the same glyphs as a CID-keyed CFF font with two font dicts (FDArray, FDSelect) +# InterCffTest.reference.json the outlines as fontTools draws them, the same for both fonts +# +# Both are made from a subset of Inter Regular (SIL Open Font License 1.1, https://rsms.me/inter), which uses only the +# general charstring operators and no subroutines. To test the whole Type 2 charstring interpreter, the charstrings are +# rewritten without changing the outlines: +# - the specialized operators (hvcurveto, vhcurveto, hhcurveto, vvcurveto, hlineto, vlineto, rcurveline, rlinecurve) +# by fontTools' specializer, +# - flex for two consecutive curves of some glyphs, +# - the body of a third of the glyphs moved into a local subroutine, of another third into a global one. +# +# Run from this directory with the path of Inter-Regular.otf: python make_cff_test_fonts.py /usr/share/fonts/opentype/inter/Inter-Regular.otf +import copy, json, sys +from fontTools.ttLib import TTFont +from fontTools.subset import Subsetter, Options +from fontTools.cffLib import specializer, SubrsIndex, FDArrayIndex, FontDict, FDSelect +from fontTools.misc.psCharStrings import T2CharString +from fontTools.pens.recordingPen import RecordingPen + +UNICODES = list(range(0x20, 0x7F)) + [0xC4, 0xD6, 0xDC, 0xE4, 0xF6, 0xFC, 0xDF, 0x20AC, 0xE9] + + +def bias(count): + return 107 if count < 1240 else 1131 if count < 33900 else 32768 + + +def subset(path): + options = Options() + options.layout_features = [] + font = TTFont(path) + subsetter = Subsetter(options) + subsetter.populate(unicodes=UNICODES) + subsetter.subset(font) + for tag in ('GDEF', 'GPOS', 'GSUB'): # they name glyphs, which the CID font renames + if tag in font: + del font[tag] + return font + + +def rewrite(program, index): + """The program with specialized operators and, for some glyphs, flex instead of two curves.""" + commands = specializer.programToCommands(program) + if index % 4 == 0: + for i in range(len(commands) - 1): + a, b = commands[i], commands[i + 1] + if a[0] == 'rrcurveto' and b[0] == 'rrcurveto' and len(a[1]) == 6 and len(b[1]) == 6: + commands[i:i + 2] = [('flex', a[1] + b[1] + [50])] + break + return specializer.specializeProgram(specializer.commandsToProgram(commands)) + + +def body(program): + assert program[-1] == 'endchar' + return program[:-1] + + +def draw_all(font): + glyphs = font.getGlyphSet() + cmap = font.getBestCmap() + result = [] + for code in UNICODES: + name = cmap[code] + pen = RecordingPen() + glyphs[name].draw(pen) + result.append({'char': chr(code), 'glyph': font.getGlyphID(name), + 'commands': [[op, [c for p in pts for c in p]] for op, pts in pen.value]}) + return result + + +def name_keyed(font): + cff = font['CFF '].cff + top = cff.topDictIndex[0] + private = top.Private + private.Subrs = SubrsIndex() + order = font.getGlyphOrder() + reference = draw_all(font) # before rewriting: the outlines must not change + local, glob = [], [] + for index, name in enumerate(order): + cs = top.CharStrings[name] + cs.decompile() + program = rewrite(cs.program, index) + if index % 3 == 1: + local.append(body(program) + ['return']) + program = [len(local) - 1, 'callsubr', 'endchar'] # bias is added below + elif index % 3 == 2: + glob.append(body(program) + ['return']) + program = [len(glob) - 1, 'callgsubr', 'endchar'] + cs.program = program + for p in local: + private.Subrs.append(T2CharString(program=p, private=private, globalSubrs=cff.GlobalSubrs)) + for p in glob: + cff.GlobalSubrs.append(T2CharString(program=p, private=private, globalSubrs=cff.GlobalSubrs)) + for name in order: # the subroutine numbers are stored without the bias + cs = top.CharStrings[name] + if len(cs.program) == 3 and cs.program[1] in ('callsubr', 'callgsubr'): + count = len(local) if cs.program[1] == 'callsubr' else len(glob) + cs.program[0] -= bias(count) + return reference + + +def to_cid(font): + """Converts the name-keyed font to a CID-keyed one with two font dicts: even glyphs use the first, odd glyphs the second, + each with its own private dict and local subroutines.""" + cmap_tables = font['cmap'].tables # read the tables that name glyphs before the glyphs are renamed + metrics = dict(font['hmtx'].metrics) + cff = font['CFF '].cff + top = cff.topDictIndex[0] + order = font.getGlyphOrder() + new_names = ['.notdef'] + ['cid%05d' % i for i in range(1, len(order))] + rename = dict(zip(order, new_names)) + programs = [] + for name in order: + cs = top.CharStrings[name] + cs.decompile() + programs.append(copy.copy(cs.program)) + old_private = top.Private + old_local = [s.program if s.program is not None else (s.decompile() or s.program) for s in old_private.Subrs] + fd_array = FDArrayIndex() + privates = [] + for k in range(2): + fd = FontDict() + private = copy.copy(old_private) + private.Subrs = SubrsIndex() + fd.Private = private + fd.FontName = 'InterCidTest-%d' % k + fd_array.append(fd) + privates.append(private) + # local subroutines move to the font dict of the glyph that calls them + local_bias = bias(len(old_local)) + for gid, program in enumerate(programs): + fd = gid % 2 + if len(program) == 3 and program[1] == 'callsubr': + subr = old_local[program[0] + local_bias] + privates[fd].Subrs.append(T2CharString(program=list(subr), private=privates[fd], globalSubrs=cff.GlobalSubrs)) + program[0] = len(privates[fd].Subrs) - 1 # bias added below + for gid, program in enumerate(programs): + fd = gid % 2 + if len(program) == 3 and program[1] == 'callsubr': + program[0] -= bias(len(privates[fd].Subrs)) + top.ROS = ('Adobe', 'Identity', 0) + top.CIDCount = len(order) + top.FDArray = fd_array + select = FDSelect() + select.format = 3 + select.gidArray = [gid % 2 for gid in range(len(order))] + top.FDSelect = select + del top.rawDict['Private'] + del top.Private + charstrings = top.CharStrings + new_dict = {} + for gid, name in enumerate(order): + cs = T2CharString(program=programs[gid], private=privates[gid % 2], globalSubrs=cff.GlobalSubrs) + cs.fdSelectIndex = gid % 2 + new_dict[rename[name]] = cs + charstrings.charStrings = new_dict + charstrings.charStringsAreIndexed = False + charstrings.charStringsIndex = None + top.charset = new_names + for table in cmap_tables: + table.cmap = {code: rename[name] for code, name in table.cmap.items()} + font['hmtx'].metrics = {rename[name]: value for name, value in metrics.items()} + font.setGlyphOrder(new_names) + font['post'].formatType = 3.0 + + +def main(path): + font = subset(path) + reference = name_keyed(font) + font.save('InterCffTest.otf') + check = TTFont('InterCffTest.otf') + assert draw_all(check) == reference, 'rewriting the charstrings changed the outlines' + json.dump({'glyphs': reference}, open('InterCffTest.reference.json', 'w', encoding='utf-8'), ensure_ascii=False, separators=(',', ':')) + + font = TTFont('InterCffTest.otf') + to_cid(font) + font.save('InterCidTest.otf') + check = TTFont('InterCidTest.otf') + top = check['CFF '].cff.topDictIndex[0] + assert hasattr(top, 'ROS') and len(top.FDArray) == 2, 'not a CID-keyed font with two font dicts' + assert [g['commands'] for g in draw_all(check)] == [g['commands'] for g in reference], 'the CID font has other outlines' + + +if __name__ == '__main__': + main(sys.argv[1]) diff --git a/tests/CADability.Tests/FontProviderTests.cs b/tests/CADability.Tests/FontProviderTests.cs index 8e2b7be03..c364a0779 100644 --- a/tests/CADability.Tests/FontProviderTests.cs +++ b/tests/CADability.Tests/FontProviderTests.cs @@ -130,7 +130,7 @@ public void UnderlineAndStrikeoutAddBars() public void MissingStyleFallsBackToAnExistingFace() { ManagedFontProvider provider = new ManagedFontProvider(new[] { FontDirectory() }, false); - CollectionAssert.AreEqual(new[] { "Liberation Sans" }, provider.GetFamilyNames()); + CollectionAssert.Contains(provider.GetFamilyNames(), "Liberation Sans"); FontFace bold = provider.GetFace("liberation sans", true, false); Assert.IsNotNull(bold, "the family name is not case sensitive"); Assert.IsFalse(bold.IsBold, "only the regular face is there"); From 8d22a3f0e100c0dc5bd279f8bcd12642999fd813 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 17 Oct 2026 08:50:53 +0000 Subject: [PATCH 4/6] Adapt the font cache test of #497 to the font service (#412) ClearingTheFontCacheDisposesTheGlyphLists reflected on the 'kerning' field of the GDI FontCache, which this branch replaces by the 'faces' of FontProvider.Current, so it failed with a NullReferenceException on Windows. It now checks that Clear empties the faces, and it runs on every platform, because the font cache no longer needs GDI. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 --- tests/CADability.Tests/RendererRecoveryTests.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/tests/CADability.Tests/RendererRecoveryTests.cs b/tests/CADability.Tests/RendererRecoveryTests.cs index 78d85af41..7a6433f43 100644 --- a/tests/CADability.Tests/RendererRecoveryTests.cs +++ b/tests/CADability.Tests/RendererRecoveryTests.cs @@ -1,6 +1,5 @@ using System.Collections; using System.Reflection; -using System.Runtime.InteropServices; using CADability.GeoObject; using CADability.Substitutes; @@ -72,9 +71,9 @@ public void ViewPaintsAgainAfterAnException() [TestMethod] public void ClearingTheFontCacheDisposesTheGlyphLists() { - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) Assert.Inconclusive("the font cache needs GDI, which is only available on Windows"); + // the font cache gets its fonts from FontProvider.Current and no longer needs GDI (issue #412) FontCache fontCache = new FontCache(); - string[] dictionaries = { "cache", "centerLineCache", "fontCharacteristicsCache", "kerning" }; + string[] dictionaries = { "cache", "centerLineCache", "fontCharacteristicsCache", "faces" }; IDictionary cache = Dictionary(fontCache, "cache"); Type dictKey = typeof(FontCache).GetNestedType("DictKey", BindingFlags.NonPublic); Type dictVal = typeof(FontCache).GetNestedType("DictVal", BindingFlags.NonPublic); @@ -86,8 +85,8 @@ public void ClearingTheFontCacheDisposesTheGlyphLists() object withoutList = Activator.CreateInstance(dictVal); // e.g. a space: the list is null dictVal.GetField("listCreated").SetValue(withoutList, true); cache[Activator.CreateInstance(dictKey, "Arial", 0, ' ')] = withoutList; - Type kerningKey = typeof(FontCache).GetNestedType("KerningKey", BindingFlags.NonPublic); - Dictionary(fontCache, "kerning")[Activator.CreateInstance(kerningKey, "Arial", 0)] = new Dictionary<(char Left, char Right), double>(); + Type fontKey = typeof(FontCache).GetNestedType("FontKey", BindingFlags.NonPublic); + Dictionary(fontCache, "faces")[Activator.CreateInstance(fontKey, "Arial", 0)] = null; fontCache.Clear(); From 31637499af46c138784c2568da96787215239ccc Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 06:41:07 +0000 Subject: [PATCH 5/6] Pass Unicode code points instead of chars to FontFace (#412) FontFace.GetAdvanceWidth, GetKerning and GetGlyphPath took a char, a UTF-16 code unit. A character outside the Basic Multilingual Plane, e.g. most emoji, is a surrogate pair of two chars in a string, so it could never reach a font: the text went through the font char by char. They now take a code point (int). FontFace is new public API of this PR, so this is the time to change it; after a release, every custom font provider would break. - Text and FontCache go through the text as code points (Text.CodePoints): a surrogate pair is one character, a lone surrogate stays the char it is. - The managed face passes the code point on to the cmap, which already supports format 12. - The GDI face measures a character beyond U+FFFF with GetTextExtentPoint32, because GetCharABCWidths only takes UTF-16 chars, and reads the kerning pairs as unsigned values. The new FontCodePointTests check with a recording font provider that "a", an emoji and "b" reach the font as three characters, with their widths, kerning and outlines; they fail when the surrogate pair is not combined. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 --- CADability.Forms/GdiFontProvider.cs | 46 +++++-- CADability/FontProvider.cs | 25 ++-- CADability/Text.cs | 61 ++++++---- tests/CADability.Tests/FontCodePointTests.cs | 115 ++++++++++++++++++ .../CADability.Tests/RendererRecoveryTests.cs | 4 +- 5 files changed, 204 insertions(+), 47 deletions(-) create mode 100644 tests/CADability.Tests/FontCodePointTests.cs diff --git a/CADability.Forms/GdiFontProvider.cs b/CADability.Forms/GdiFontProvider.cs index 6773de1c1..846e3d9ef 100644 --- a/CADability.Forms/GdiFontProvider.cs +++ b/CADability.Forms/GdiFontProvider.cs @@ -60,8 +60,8 @@ internal sealed class GdiFontFace : FontFace private readonly FontFamily family; private readonly FontStyle style; // bold and italic only private readonly int em; - private readonly Dictionary widths = new Dictionary(); - private Dictionary<(char, char), double> kerning; + private readonly Dictionary widths = new Dictionary(); + private Dictionary<(int, int), double> kerning; public GdiFontFace(FontFamily family, bool bold, bool italic) { @@ -85,28 +85,37 @@ public GdiFontFace(FontFamily family, bool bold, bool italic) public override int CellDescent => family.GetCellDescent(style); public override int LineSpacing => family.GetLineSpacing(style); - public override double GetAdvanceWidth(char c) + public override double GetAdvanceWidth(int codePoint) { lock (widths) { - if (widths.TryGetValue(c, out double width)) return width; + if (widths.TryGetValue(codePoint, out double width)) return width; WithSelectedFont(hdc => { - ABC[] abc = new ABC[1]; - GetCharABCWidths(hdc, c, c, abc); - width = (abc[0].abcA + abc[0].abcB + abc[0].abcC) / (double)em; + if (codePoint <= 0xFFFF) + { + ABC[] abc = new ABC[1]; + GetCharABCWidths(hdc, (uint)codePoint, (uint)codePoint, abc); + width = (abc[0].abcA + abc[0].abcB + abc[0].abcC) / (double)em; + } + else + { // GetCharABCWidths only takes UTF-16 chars: measure the surrogate pair as a string + string s = char.ConvertFromUtf32(codePoint); + GetTextExtentPoint32(hdc, s, s.Length, out SIZE size); + width = size.cx / (double)em; + } }); - widths[c] = width; + widths[codePoint] = width; return width; } } - public override double GetKerning(char left, char right) + public override double GetKerning(int left, int right) { lock (widths) { if (kerning == null) { - Dictionary<(char, char), double> pairs = new Dictionary<(char, char), double>(); + Dictionary<(int, int), double> pairs = new Dictionary<(int, int), double>(); WithSelectedFont(hdc => { int num = GetKerningPairs(hdc, 0, null); @@ -114,7 +123,7 @@ public override double GetKerning(char left, char right) { KERNINGPAIR[] kp = new KERNINGPAIR[num]; GetKerningPairs(hdc, num, kp); - for (int i = 0; i < kp.Length; ++i) pairs[((char)kp[i].wFirst, (char)kp[i].wSecond)] = kp[i].iKernAmount / (double)em; + for (int i = 0; i < kp.Length; ++i) pairs[((ushort)kp[i].wFirst, (ushort)kp[i].wSecond)] = kp[i].iKernAmount / (double)em; } }); kerning = pairs; @@ -123,7 +132,7 @@ public override double GetKerning(char left, char right) return res; } } - public override void GetGlyphPath(char c, bool underline, bool strikeout, out PointF[] points, out byte[] types) + public override void GetGlyphPath(int codePoint, bool underline, bool strikeout, out PointF[] points, out byte[] types) { FontStyle fs = style | (underline ? FontStyle.Underline : FontStyle.Regular) | (strikeout ? FontStyle.Strikeout : FontStyle.Regular); using (GraphicsPath path = new GraphicsPath()) @@ -131,7 +140,7 @@ public override void GetGlyphPath(char c, bool underline, bool strikeout, out Po { sf.LineAlignment = StringAlignment.Near; sf.Alignment = StringAlignment.Near; - path.AddString(c.ToString(), family, (int)fs, 1.0f, new PointF(0.0f, 0.0f), sf); + path.AddString(AsString(codePoint), family, (int)fs, 1.0f, new PointF(0.0f, 0.0f), sf); if (path.PointCount == 0) { points = new PointF[0]; @@ -145,6 +154,9 @@ public override void GetGlyphPath(char c, bool underline, bool strikeout, out Po } } + // a lone surrogate (not a valid code point) is passed on as the char it is + private static string AsString(int codePoint) => codePoint >= 0xD800 && codePoint <= 0xDFFF ? ((char)codePoint).ToString() : char.ConvertFromUtf32(codePoint); + /// /// Selects this face with the em height as pixel size into a device context for the duration of . /// The device context only lives during the call, so nothing is left to a finalizer. @@ -191,6 +203,14 @@ private struct KERNINGPAIR } [DllImport("gdi32.dll", EntryPoint = "GetCharABCWidthsW")] private static extern bool GetCharABCWidths(IntPtr hdc, uint uFirstChar, uint uLastChar, [Out] ABC[] lpabc); + [StructLayout(LayoutKind.Sequential)] + private struct SIZE + { + public int cx; + public int cy; + } + [DllImport("gdi32.dll", EntryPoint = "GetTextExtentPoint32W", CharSet = CharSet.Unicode)] + private static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int c, out SIZE psizl); [DllImport("gdi32.dll", EntryPoint = "GetKerningPairsW")] private static extern int GetKerningPairs(IntPtr hdc, int cPairs, [Out] KERNINGPAIR[] lpkrnpair); [DllImport("gdi32.dll")] diff --git a/CADability/FontProvider.cs b/CADability/FontProvider.cs index 6dcb925eb..e0c00708c 100644 --- a/CADability/FontProvider.cs +++ b/CADability/FontProvider.cs @@ -98,21 +98,24 @@ public abstract class FontFace /// public abstract int LineSpacing { get; } /// - /// The advance width of the character in units of the em height. + /// The advance width of the character in units of the em height. Characters are Unicode + /// code points, so that characters outside the Basic Multilingual Plane (e.g. most emoji), which take two UTF-16 + /// chars (a surrogate pair) in a string, are one character here. /// - public abstract double GetAdvanceWidth(char c); + public abstract double GetAdvanceWidth(int codePoint); /// - /// The kerning between two characters in units of the em height, usually negative; 0 if there is none. + /// The kerning between two characters (Unicode code points) in units of the em height, usually negative; 0 if there + /// is none. /// - public abstract double GetKerning(char left, char right); + public abstract double GetKerning(int left, int right); /// - /// Returns the outline of the character in the layout of a GDI+ GraphicsPath after + /// Returns the outline of the character (Unicode code point) in the layout of a GDI+ GraphicsPath after /// AddString with an em size of 1 and StringFormat.GenericTypographic: in units of the em height, y /// downwards, the top of the character cell at 0 and the baseline at /. /// The point types are those of GDI+ (0 start, 1 line, 3 cubic Bezier, 0x80 closes the figure). Curves must be returned /// as Bezier segments and not be flattened. Underline and strikeout are added as rectangles when requested. /// - public abstract void GetGlyphPath(char c, bool underline, bool strikeout, out PointF[] points, out byte[] types); + public abstract void GetGlyphPath(int codePoint, bool underline, bool strikeout, out PointF[] points, out byte[] types); } /// @@ -298,20 +301,20 @@ public ManagedFontFace(TrueTypeFont font) public override int CellAscent => font.CellAscent; public override int CellDescent => font.CellDescent; public override int LineSpacing => font.LineSpacing; - public override double GetAdvanceWidth(char c) + public override double GetAdvanceWidth(int codePoint) { - return font.GetAdvanceWidth(font.GetGlyphIndex(c)) / (double)font.UnitsPerEm; + return font.GetAdvanceWidth(font.GetGlyphIndex(codePoint)) / (double)font.UnitsPerEm; } - public override double GetKerning(char left, char right) + public override double GetKerning(int left, int right) { lock (locker) { // the kerning pairs are read on the first call return font.GetKerning(font.GetGlyphIndex(left), font.GetGlyphIndex(right)) / (double)font.UnitsPerEm; } } - public override void GetGlyphPath(char c, bool underline, bool strikeout, out PointF[] points, out byte[] types) + public override void GetGlyphPath(int codePoint, bool underline, bool strikeout, out PointF[] points, out byte[] types) { - font.GetPathData(font.GetGlyphIndex(c), underline, strikeout, out points, out types); + font.GetPathData(font.GetGlyphIndex(codePoint), underline, strikeout, out points, out types); } } } diff --git a/CADability/Text.cs b/CADability/Text.cs index cc09c1e8b..0e171b094 100644 --- a/CADability/Text.cs +++ b/CADability/Text.cs @@ -408,8 +408,8 @@ struct DictKey { public readonly string fontName; public readonly int fontStyle; - public readonly char c; - public DictKey(string fontName, int fontStyle, char c) + public readonly int c; // a Unicode code point + public DictKey(string fontName, int fontStyle, int c) { this.fontName = fontName; this.fontStyle = fontStyle; @@ -417,7 +417,7 @@ public DictKey(string fontName, int fontStyle, char c) } public override int GetHashCode() { - return fontName.GetHashCode() ^ fontStyle ^ (int)c; + return fontName.GetHashCode() ^ fontStyle ^ c; } public override bool Equals(object obj) { @@ -524,7 +524,7 @@ internal FontFace TryGetFace(string fontName, int fontStyle) // The caches are shared by all threads that measure or paint text, and Clear may empty them at any time: every access // goes through lock (this). The lock is reentrant, Get and GetCenterLines call GetOutline2D. The faces are locked // separately and always after this, so the two locks are taken in the same order everywhere. - public Path2D[] GetOutline2D(string fontName, int fontStyle, char c, out double width) + public Path2D[] GetOutline2D(string fontName, int fontStyle, int c, out double width) { lock (this) { @@ -535,7 +535,7 @@ public Path2D[] GetOutline2D(string fontName, int fontStyle, char c, out double return GlyphShapeBuilder.PathDataToPath2D(points, types, FontPrecision); } } - public CompoundShape Get(string font, int fontStyle, char c, out double width) + public CompoundShape Get(string font, int fontStyle, int c, out double width) { // soll verschwinden!! DictVal found; lock (this) @@ -554,14 +554,14 @@ public CompoundShape Get(string font, int fontStyle, char c, out double width) width = found.width; return found.shape; } - public double GetKerning(string font, int fontStyle, char a, char b) + public double GetKerning(string font, int fontStyle, int a, int b) { lock (this) { return GetFace(font, fontStyle).GetKerning(a, b); } } - public IPaintTo3DList Get(string font, int fontStyle, char c, out double width, IPaintTo3D paintTo3D) + public IPaintTo3DList Get(string font, int fontStyle, int c, out double width, IPaintTo3D paintTo3D) { bool useLists = true; if (paintTo3D != null) useLists = !paintTo3D.IsBitmap; @@ -757,14 +757,14 @@ public void Clear() lock (faces) faces.Clear(); } } - public GeoObjectList GetCenterLines(string font, int fontStyle, char c, out double width) + public GeoObjectList GetCenterLines(string font, int fontStyle, int c, out double width) { lock (this) { return ReadCenterLines(font, fontStyle, c, out width); } } - private GeoObjectList ReadCenterLines(string font, int fontStyle, char c, out double width) + private GeoObjectList ReadCenterLines(string font, int fontStyle, int c, out double width) { CenterLineVal found; if (!centerLineCache.TryGetValue(new DictKey(font, fontStyle, c), out found)) @@ -1022,10 +1022,11 @@ private void calcExtent() // the size of the text string with the font size glyphDirection.Length: the advance widths and the kerning // of the characters and the line spacing (this used to be Graphics.MeasureString of GDI+) double width = 0.0; - for (int i = 0; i < textString.Length; ++i) + int[] chars = CodePoints(textString); + for (int i = 0; i < chars.Length; ++i) { - width += ff.GetAdvanceWidth(textString[i]); - if (i > 0) width += ff.GetKerning(textString[i - 1], textString[i]); + width += ff.GetAdvanceWidth(chars[i]); + if (i > 0) width += ff.GetKerning(chars[i - 1], chars[i]); } double size = glyphDirection.Length; sizeExtent = new SizeF((float)(width * size), (float)(size * lsp / em)); @@ -1164,6 +1165,24 @@ private void AddToPath2D(List addto, List points, bool spl } points.RemoveRange(0, points.Count - 1); // den letzten als ersten drinlassen } + /// + /// The characters of as Unicode code points: a surrogate pair (e.g. an emoji) is one character. + /// A lone surrogate is kept as the char it is. + /// + internal static int[] CodePoints(string s) + { + List res = new List(s.Length); + for (int i = 0; i < s.Length; ++i) + { + if (char.IsHighSurrogate(s[i]) && i + 1 < s.Length && char.IsLowSurrogate(s[i + 1])) + { + res.Add(char.ConvertToUtf32(s[i], s[i + 1])); + ++i; + } + else res.Add(s[i]); + } + return res.ToArray(); + } // Returns the string to display: textString, with additional line breaks inserted at word // boundaries when ColumnWidth demands wrapping. Character widths are measured with the same // FontCache the layout loops use, so the wrapped lines fit the column exactly. @@ -1196,9 +1215,10 @@ private string GetDisplayString(int fs) } private void AppendWrappedParagraph(System.Text.StringBuilder result, string paragraph, double normalizedWidth, int fs) { - double MeasureWord(string w) + double MeasureWord(string word) { double total = 0.0; + int[] w = CodePoints(word); for (int i = 0; i < w.Length; i++) { FontCache.GlobalFontCache.Get(fontName, fs, w[i], out double cw, null); @@ -1248,7 +1268,7 @@ public virtual GeoObjectList GetOutline() if (Strikeout) fs |= (int)FontStyle.Strikeout; PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 - string txt = GetDisplayString(fs); + int[] txt = CodePoints(GetDisplayString(fs)); int em = ff.EmHeight; int ls = ff.LineSpacing; int dc = ff.CellDescent; @@ -1395,7 +1415,7 @@ public CompoundShape[] GetShapes() if (Strikeout) fs |= (int)FontStyle.Strikeout; PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 - string txt = GetDisplayString(fs); + int[] txt = CodePoints(GetDisplayString(fs)); int em = ff.EmHeight; int ls = ff.LineSpacing; int dc = ff.CellDescent; @@ -1528,7 +1548,7 @@ public virtual GeoObjectList GetCenterline() if (Strikeout) fs |= (int)FontStyle.Strikeout; PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); FontFace ff = FontCache.GlobalFontCache.GetFace(fontName, fs); // FontProvider.Current, see issue #412 - string txt = GetDisplayString(fs); + int[] txt = CodePoints(GetDisplayString(fs)); int em = ff.EmHeight; int ls = ff.LineSpacing; int dc = ff.CellDescent; @@ -1923,7 +1943,7 @@ private void CalcExtent() } FontFace ff = FontCache.GlobalFontCache.TryGetFace(fontName, fs); // FontProvider.Current, see issue #412 if (ff == null) return; // no font at all: nothing to paint or measure - string txt = GetDisplayString(fs); + int[] txt = CodePoints(GetDisplayString(fs)); int em = ff.EmHeight; int ls = ff.LineSpacing; int dc = ff.CellDescent; @@ -2558,10 +2578,9 @@ public override void PrePaintTo3D(IPaintTo3D paintTo3D) int ls = ff.LineSpacing; int dc = ff.CellDescent; int ac = ff.CellAscent; - for (int i = 0; i < textString.Length; ++i) + foreach (int c in CodePoints(textString)) { - double width; - FontCache.GlobalFontCache.Get(fontName, fs, textString[i], out width, paintTo3D); + FontCache.GlobalFontCache.Get(fontName, fs, c, out double width, paintTo3D); } } else @@ -2614,7 +2633,7 @@ public override void PaintTo3D(IPaintTo3D paintTo3D) PlaneSurface pls = new PlaneSurface(location, lineDirection, glyphDirection, lineDirection ^ glyphDirection); FontFace ff = FontCache.GlobalFontCache.TryGetFace(fontName, fs); // FontProvider.Current, see issue #412 if (ff == null) return; // no font at all: nothing to paint or measure - string txt = GetDisplayString(fs); + int[] txt = CodePoints(GetDisplayString(fs)); int em = ff.EmHeight; int ls = ff.LineSpacing; int dc = ff.CellDescent; diff --git a/tests/CADability.Tests/FontCodePointTests.cs b/tests/CADability.Tests/FontCodePointTests.cs new file mode 100644 index 000000000..045025aa0 --- /dev/null +++ b/tests/CADability.Tests/FontCodePointTests.cs @@ -0,0 +1,115 @@ +using CADability.GeoObject; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Drawing; + +namespace CADability.Tests +{ + /// + /// takes Unicode code points, so that characters outside the Basic Multilingual Plane, e.g. most + /// emoji, which are a surrogate pair of two chars in a string, reach the font as one character (issue #412). + /// + [TestClass] + public class FontCodePointTests + { + private const int Emoji = 0x1F600; // GRINNING FACE, "😀" in a string + + /// + /// A face that records the characters it is asked for. Every character is a square from 0.1 to 0.4 with an advance + /// width of 0.5 (in units of the em height). + /// + private sealed class RecordingFace : FontFace + { + public readonly List Glyphs = new List(), Widths = new List(); + public readonly List<(int, int)> KerningPairs = new List<(int, int)>(); + public override string FamilyName => "Recording"; + public override bool IsBold => false; + public override bool IsItalic => false; + public override int EmHeight => 1000; + public override int CellAscent => 800; + public override int CellDescent => 200; + public override int LineSpacing => 1000; + public override double GetAdvanceWidth(int codePoint) + { + lock (Widths) Widths.Add(codePoint); + return 0.5; + } + public override double GetKerning(int left, int right) + { + lock (KerningPairs) KerningPairs.Add((left, right)); + return 0.0; + } + public override void GetGlyphPath(int codePoint, bool underline, bool strikeout, out PointF[] points, out byte[] types) + { + lock (Glyphs) Glyphs.Add(codePoint); + points = new[] { new PointF(0.1f, 0.2f), new PointF(0.4f, 0.2f), new PointF(0.4f, 0.6f), new PointF(0.1f, 0.6f) }; + types = new byte[] { 0, 1, 1, 0x81 }; + } + } + private sealed class RecordingProvider : FontProvider + { + public readonly RecordingFace Face = new RecordingFace(); + public override string[] GetFamilyNames() => new[] { "Recording" }; + public override FontFace GetFace(string familyName, bool bold, bool italic) => string.Equals(familyName, "Recording", StringComparison.OrdinalIgnoreCase) ? Face : null; + } + + private static bool IsSurrogate(int c) => c >= 0xD800 && c <= 0xDFFF; + + [TestMethod] + public void ASurrogatePairIsOneCodePoint() + { + CollectionAssert.AreEqual(new[] { 'a', Emoji, 'b' }, Text.CodePoints("a😀b")); + // a lone surrogate is no code point, it is kept as the char it is + CollectionAssert.AreEqual(new[] { 0xD83D, 'x' }, Text.CodePoints("\uD83Dx")); + CollectionAssert.AreEqual(new[] { 'x', 0xDE00 }, Text.CodePoints("x\uDE00")); + CollectionAssert.AreEqual(new int[0], Text.CodePoints("")); + } + + [TestMethod] + [DoNotParallelize] + public void AnEmojiReachesTheFontAsOneCharacter() + { + RecordingProvider provider = new RecordingProvider(); + try + { + FontProvider.Current = provider; + Text text = Text.Construct(); + text.Font = "Recording"; + text.TextString = "a😀b"; + text.Location = GeoPoint.Origin; + text.LineDirection = GeoVector.XAxis; + text.GlyphDirection = GeoVector.YAxis; + text.TextSize = 1.0; + + // three characters, not four as with the two halves of the surrogate pair as separate characters: the extent + // is the sum of the advance widths, the outline goes from the first square at 0.1 to the third at 2 * 0.5 + 0.4 + BoundingCube extent = text.GetBoundingCube(); + Assert.AreEqual(1.5, extent.Xmax - extent.Xmin, 1e-6, "the advance widths of three characters"); + BoundingCube outline = text.GetOutline().GetExtent(); + Assert.AreEqual(0.1, outline.Xmin, 1e-6); + Assert.AreEqual(1.4, outline.Xmax, 1e-6, "the outlines of three characters"); + + RecordingFace face = provider.Face; + CollectionAssert.AreEquivalent(new[] { 'a', Emoji, 'b' }, face.Glyphs.Distinct().ToArray(), "the outlines asked for"); + Assert.IsTrue(face.Widths.Contains(Emoji), "the advance width of the emoji"); + Assert.IsTrue(face.KerningPairs.Contains(('a', Emoji)) && face.KerningPairs.Contains((Emoji, 'b')), "the kerning around the emoji"); + Assert.IsFalse(face.Glyphs.Concat(face.Widths).Any(IsSurrogate), "no half of a surrogate pair reaches the font"); + } + finally + { + TestFonts.UseTestFonts(null); + } + } + + [TestMethod] + public void TheManagedReaderMapsCodePointsAboveTheBmp() + { + // the face of the managed reader passes the code point on to the cmap, which supports format 12 (beyond U+FFFF); + // a font without the character gives the missing glyph, but no exception + FontFace face = new ManagedFontProvider(new[] { TestFonts.Directory }, false).GetFace("Liberation Sans", false, false); + Assert.IsNotNull(face); + Assert.AreEqual(face.GetAdvanceWidth(0xFFFF + 1), face.GetAdvanceWidth(0x10FFFF), 1e-12, "both are the missing glyph"); + face.GetGlyphPath(Emoji, false, false, out PointF[] points, out byte[] types); + Assert.AreEqual(points.Length, types.Length); + } + } +} diff --git a/tests/CADability.Tests/RendererRecoveryTests.cs b/tests/CADability.Tests/RendererRecoveryTests.cs index 499b673ac..5e7fdd3af 100644 --- a/tests/CADability.Tests/RendererRecoveryTests.cs +++ b/tests/CADability.Tests/RendererRecoveryTests.cs @@ -83,10 +83,10 @@ public void ClearingTheFontCacheDisposesTheGlyphLists() object withList = Activator.CreateInstance(dictVal); dictVal.GetField("list").SetValue(withList, list); dictVal.GetField("listCreated").SetValue(withList, true); - cache[Activator.CreateInstance(dictKey, "Arial", 0, 'A')] = withList; + cache[Activator.CreateInstance(dictKey, "Arial", 0, (int)'A')] = withList; object withoutList = Activator.CreateInstance(dictVal); // e.g. a space: the list is null dictVal.GetField("listCreated").SetValue(withoutList, true); - cache[Activator.CreateInstance(dictKey, "Arial", 0, ' ')] = withoutList; + cache[Activator.CreateInstance(dictKey, "Arial", 0, (int)' ')] = withoutList; Type fontKey = typeof(FontCache).GetNestedType("FontKey", BindingFlags.NonPublic); Dictionary(fontCache, "faces")[Activator.CreateInstance(fontKey, "Arial", 0)] = null; From ac0ad17df76fff81057806e5ec27f4691b4ee26f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 07:01:23 +0000 Subject: [PATCH 6/6] Lock the shared sets of the parallel STEP import With StepImport.Parallel, CreateEntity runs on several threads, but it added mapped items to the HashSet mappedItems and, for assemblies, products to the HashSet products without a lock (the other place that adds products locks it). Concurrent Add calls can corrupt a HashSet, and the enumeration of mappedItems after the creation then met a null entry: a NullReferenceException in ImportStep.Read. StepCyclicReferenceTest from #498 imports a file with mapped items 20 times in parallel and showed this now and then on the 2-core CI runner. With the test process limited to 2 cores, it failed 3 of 30 times without this change and 0 of 60 times with it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01YXvDW91oXpy7RquGHNxgR5 (cherry picked from commit 85e21f1ae2962e97d1428c173f4a86e3d1e0fddd) --- CADability/ImportStep.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CADability/ImportStep.cs b/CADability/ImportStep.cs index 955480b2b..f3cd3d4f7 100644 --- a/CADability/ImportStep.cs +++ b/CADability/ImportStep.cs @@ -4076,7 +4076,7 @@ private object CreateEntity(Item item) child.parameter["_transformation"] = new Item(Item.ItemType.created, transformation); child.parameter["_referred"] = relatedProduct; relatingProduct.parameter["_children"].lval.Add(child); - products.Add(relatingProduct); + lock (products) products.Add(relatingProduct); // the parallel import adds products on several threads } #if DEBUG //System.Diagnostics.Trace.WriteLine(SubTree(item.definingIndex, 5)); @@ -4590,7 +4590,7 @@ private object CreateEntity(Item item) // System.Diagnostics.Trace.WriteLine(up[i]); //} #endif - mappedItems.Add(item); + lock (mappedItems) mappedItems.Add(item); // the parallel import creates mapped items on several threads string name = item.parameter["name"].sval; object mo = CreateEntity(item.parameter["mapping_source"].parameter["mapping_origin"]); object mr = CreateEntity(item.parameter["mapping_source"].parameter["mapped_representation"]);