├── OpenFont ├── Tables.Others │ ├── Merge.cs │ ├── LinearThreashold.cs │ ├── HorizontalDeviceMetrics.cs │ ├── VerticalDeviceMetrics.cs │ └── VerticalMetrics.cs ├── Tables.Variations │ ├── VVar.cs │ ├── CVar.cs │ ├── Common.ItemVariationStore.cs │ ├── AVar.cs │ └── HVar.cs ├── OpenFont.csproj ├── TrueTypeInterperter │ └── InvalidFontException.cs ├── README.MD ├── Bounds.cs ├── Tables │ ├── TableEntryCollection.cs │ ├── TableHeader.cs │ ├── TableEntry.cs │ ├── HorizontalHeader.cs │ ├── MaxProfile.cs │ ├── HorizontalMetrics.cs │ ├── Utils.cs │ ├── NameEntry.cs │ └── Head.cs ├── Tables.AdvancedLayout │ ├── IGlyphIndexList.cs │ ├── COLR.cs │ ├── ScriptList.cs │ ├── AttachmentListTable.cs │ ├── CPAL.cs │ ├── CoverageTable.cs │ └── FeatureList.cs ├── Tables.BitmapAndSvgFonts │ ├── BitmapFontGlyphSource.cs │ ├── EBSC.cs │ ├── EBDT.cs │ ├── CBDT.cs │ ├── CBLC.cs │ ├── EBLC.cs │ └── SvgTable.cs ├── Tables.TrueType │ ├── Cvt_Programs.cs │ ├── GlyphLocations.cs │ └── Gasp.cs ├── AdditionalInfo │ ├── OS2_IBMFontClassParameters.cs │ ├── MacPostFormat1.cs │ └── Languages.cs ├── Geometry.cs ├── PreviewFontInfo.cs ├── ByteOrderSwappingBinaryReader.cs ├── Tables.CFF │ └── CFFTable.cs └── Typeface_TrimableExtensions.cs ├── samples ├── result.png └── test.in.html ├── Schwabra ├── Result.cs ├── Station.cs ├── ElectionContext.cs ├── Schwabra.csproj ├── Migrations │ ├── InitialCreate.cs │ ├── ElectionContextModelSnapshot.cs │ └── InitialCreate.Designer.cs └── Program.cs ├── Izbirkom21 ├── Izbirkom21.csproj ├── Trash.cs ├── FixedContentMorpher.cs └── FontReplacer.cs ├── LICENSE ├── izbirkom21.sln ├── README.md └── .gitignore /OpenFont/Tables.Others/Merge.cs: -------------------------------------------------------------------------------- 1 | //TODO: implement this -------------------------------------------------------------------------------- /OpenFont/Tables.Others/LinearThreashold.cs: -------------------------------------------------------------------------------- 1 | //TODO: implement this -------------------------------------------------------------------------------- /samples/result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulex/izbirkom21/HEAD/samples/result.png -------------------------------------------------------------------------------- /samples/test.in.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulex/izbirkom21/HEAD/samples/test.in.html -------------------------------------------------------------------------------- /OpenFont/Tables.Variations/VVar.cs: -------------------------------------------------------------------------------- 1 | //TODO: implement this 2 | //https://docs.microsoft.com/en-us/typography/opentype/spec/vvar -------------------------------------------------------------------------------- /OpenFont/OpenFont.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | True 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Schwabra/Result.cs: -------------------------------------------------------------------------------- 1 | namespace Schwabra 2 | { 3 | public class Result 4 | { 5 | public int id { get; set; } 6 | public int num { get; set; } 7 | public string title { get; set; } 8 | public int value { get; set; } 9 | public double? value_percent { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Schwabra/Station.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Schwabra 4 | { 5 | public class Station 6 | { 7 | public int id { get; set; } 8 | public string name { get; set; } 9 | public string filename { get; set; } 10 | public string path { get; set; } 11 | public List rows { get; set; } = new(); 12 | } 13 | } -------------------------------------------------------------------------------- /Izbirkom21/Izbirkom21.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Schwabra/ElectionContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore; 2 | 3 | namespace Schwabra 4 | { 5 | public class ElectionContext : DbContext 6 | { 7 | public DbSet station { get; set; } 8 | public DbSet result { get; set; } 9 | 10 | public string DbPath { get; } 11 | 12 | public ElectionContext() : this("election.sqlite3") 13 | { 14 | } 15 | 16 | public ElectionContext(string path) 17 | { 18 | DbPath = path; 19 | } 20 | protected override void OnConfiguring(DbContextOptionsBuilder options) 21 | => options.UseSqlite($"Data Source={DbPath}"); 22 | } 23 | } -------------------------------------------------------------------------------- /OpenFont/TrueTypeInterperter/InvalidFontException.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2015, Michael Popoloski's SharpFont 2 | 3 | using System; 4 | 5 | namespace Typography.OpenFont 6 | { 7 | 8 | class InvalidFontException : Exception 9 | { 10 | public InvalidFontException() { } 11 | public InvalidFontException(string msg) : base(msg) { } 12 | } 13 | class InvalidTrueTypeFontException : InvalidFontException 14 | { 15 | public InvalidTrueTypeFontException() { } 16 | /// 17 | /// Initializes a new instance of the class. 18 | /// 19 | public InvalidTrueTypeFontException(string msg) : base(msg) { } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /OpenFont/Tables.Variations/CVar.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | //https://docs.microsoft.com/en-us/typography/opentype/spec/cvar 8 | 9 | /// 10 | /// cvar — CVT Variations Table 11 | /// 12 | class CVar : TableEntry 13 | { 14 | public const string _N = "cvar"; 15 | public override string Name => _N; 16 | public CVar() 17 | { 18 | //The control value table (CVT) variations table is used in variable fonts to provide variation data for CVT values. 19 | //For a general overview of OpenType Font Variations 20 | 21 | 22 | } 23 | protected override void ReadContentFrom(BinaryReader reader) 24 | { 25 | 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Schwabra/Schwabra.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net5.0 6 | 7 | 8 | 9 | 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | all 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /OpenFont/README.MD: -------------------------------------------------------------------------------- 1 | Typography.OpenFont 2 | --- 3 | 4 | This module is about reading 5 | 1. Open Font Format (.ttf, .otf, .ttc, .otc) 6 | 2. Web Open Font Format (.woff, .woff2) 7 | 8 | 9 | References: 10 | 11 | ISO/IEC 14496-22:2015, Open Font Format, http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=66391 12 | 13 | Microsoft OpenType Specification, https://www.microsoft.com/en-us/Typography/OpenTypeSpecification.aspx 14 | 15 | The Compact Font Format Specification, Technical Note #5176, Version 1, 4 December 2003, https://typekit.files.wordpress.com/2010/12/5176.cff.pdf 16 | 17 | The Type2 Charstring Format, Technical Note #517716 March 2000, Adobe, https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5177.Type2.pdf 18 | 19 | Type1 Font Format, Adobe, https://www.adobe.com/content/dam/acom/en/devnet/font/pdfs/T1_SPEC.pdf 20 | 21 | WOFF 1.0, https://www.w3.org/TR/2012/REC-WOFF-20121213/ 22 | 23 | WOFF 2.0, https://www.w3.org/TR/WOFF2/ -------------------------------------------------------------------------------- /OpenFont/Bounds.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | namespace Typography.OpenFont 5 | { 6 | /// 7 | /// original glyph bounds 8 | /// 9 | public readonly struct Bounds 10 | { 11 | 12 | //TODO: will be changed to => public readonly struct Bounds 13 | 14 | public static readonly Bounds Zero = new Bounds(0, 0, 0, 0); 15 | public Bounds(short xmin, short ymin, short xmax, short ymax) 16 | { 17 | XMin = xmin; 18 | YMin = ymin; 19 | XMax = xmax; 20 | YMax = ymax; 21 | } 22 | 23 | public short XMin { get; } 24 | public short YMin { get; } 25 | public short XMax { get; } 26 | public short YMax { get; } 27 | #if DEBUG 28 | public override string ToString() 29 | { 30 | return "(" + XMin + "," + YMin + "," + XMax + "," + YMax + ")"; 31 | } 32 | #endif 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /OpenFont/Tables/TableEntryCollection.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System.Collections.Generic; 5 | namespace Typography.OpenFont.Tables 6 | { 7 | class TableEntryCollection 8 | { 9 | readonly Dictionary _tables = new Dictionary(); 10 | public TableEntryCollection() { } 11 | 12 | public void AddEntry(TableEntry en) => _tables.Add(en.Name, en); 13 | 14 | public bool TryGetTable(string tableName, out TableEntry entry) => _tables.TryGetValue(tableName, out entry); 15 | 16 | public void ReplaceTable(TableEntry table) => _tables[table.Name] = table; 17 | 18 | public TableHeader[] CloneTableHeaders() 19 | { 20 | TableHeader[] clones = new TableHeader[_tables.Count]; 21 | int i = 0; 22 | foreach (TableEntry en in _tables.Values) 23 | { 24 | clones[i] = en.Header.Clone(); 25 | i++; 26 | } 27 | return clones; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Alexander Ulitin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/IGlyphIndexList.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | 3 | 4 | namespace Typography.OpenFont.Tables 5 | { 6 | /// 7 | /// replaceable glyph index list 8 | /// 9 | public interface IGlyphIndexList 10 | { 11 | int Count { get; } 12 | ushort this[int index] { get; } 13 | 14 | /// 15 | /// remove:add_new 1:1 16 | /// 17 | /// 18 | /// 19 | void Replace(int index, ushort newGlyphIndex); 20 | /// 21 | /// remove:add_new >=1:1 22 | /// 23 | /// 24 | /// 25 | /// 26 | void Replace(int index, int removeLen, ushort newGlyphIndex); 27 | /// 28 | /// remove: add_new 1:>=1 29 | /// 30 | /// 31 | /// 32 | void Replace(int index, ushort[] newGlyphIndices); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /Izbirkom21/Trash.cs: -------------------------------------------------------------------------------- 1 | using ExCSS; 2 | using HtmlAgilityPack; 3 | 4 | namespace Izbirkom21 5 | { 6 | public static class Trash 7 | { 8 | public static void RemoveTrashNodes(HtmlDocument htmlDocument) 9 | { 10 | // remove trash nodes 11 | foreach (var span in htmlDocument.DocumentNode.SelectNodes("//span")) 12 | { 13 | var style = span.GetAttributeValue("style", ""); 14 | if (string.IsNullOrEmpty(style)) continue; 15 | if (IsTrashStyle("* {" + style + "}")) 16 | { 17 | span.Remove(); 18 | } 19 | } 20 | } 21 | 22 | public static bool IsTrashStyle(string stylesheetStr) 23 | { 24 | var stylesheetParser = new StylesheetParser(); 25 | var stylesheet = stylesheetParser.Parse(stylesheetStr); 26 | foreach (var styleRule in stylesheet.StyleRules) 27 | { 28 | var style = styleRule.Style; 29 | if (IsTrashStyle(style)) return true; 30 | } 31 | 32 | return false; 33 | } 34 | 35 | public static bool IsTrashStyle(StyleDeclaration style) 36 | { 37 | return style.Position == "absolute" || style.Display == "none" || style.FontSize == "0" || 38 | style.Color == "white" || style.Overflow == "hidden"; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /OpenFont/Tables/TableHeader.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | namespace Typography.OpenFont.Tables 5 | { 6 | class TableHeader 7 | { 8 | readonly uint _tag; 9 | 10 | public TableHeader(uint tag, uint checkSum, uint offset, uint len) 11 | { 12 | _tag = tag; 13 | CheckSum = checkSum; 14 | Offset = offset; 15 | Length = len; 16 | Tag = Utils.TagToString(_tag); 17 | } 18 | public TableHeader(string tag, uint checkSum, uint offset, uint len) 19 | { 20 | _tag = 0; 21 | CheckSum = checkSum; 22 | Offset = offset; 23 | Length = len; 24 | Tag = tag; 25 | } 26 | // 27 | public string Tag { get; } 28 | public uint Offset { get; } 29 | public uint CheckSum { get; } 30 | public uint Length { get; } 31 | 32 | public TableHeader Clone() => (_tag != 0) ? new TableHeader(_tag, CheckSum, Offset, Length) : new TableHeader(Tag, CheckSum, Offset, Length); 33 | #if DEBUG 34 | public override string ToString() 35 | { 36 | return "{" + Tag + "}"; 37 | } 38 | #endif 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/BitmapFontGlyphSource.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | 4 | namespace Typography.OpenFont.Tables 5 | { 6 | class BitmapFontGlyphSource 7 | { 8 | readonly CBLC _cblc; //bitmap locator 9 | CBDT _cbdt; 10 | public BitmapFontGlyphSource(CBLC cblc) => _cblc = cblc; 11 | 12 | /// 13 | /// load new bitmap embeded data 14 | /// 15 | /// 16 | public void LoadCBDT(CBDT cbdt) => _cbdt = cbdt; 17 | 18 | /// 19 | /// clear and remove existing bitmap embeded data 20 | /// 21 | public void UnloadCBDT() 22 | { 23 | if (_cbdt != null) 24 | { 25 | _cbdt.RemoveOldMemoryStreamAndReaders(); 26 | _cbdt = null; 27 | } 28 | } 29 | 30 | public void CopyBitmapContent(Glyph glyph, System.IO.Stream outputStream) => _cbdt.CopyBitmapContent(glyph, outputStream); 31 | 32 | public Glyph[] BuildGlyphList() 33 | { 34 | Glyph[] glyphs = _cblc.BuildGlyphList(); 35 | for (int i = 0; i < glyphs.Length; ++i) 36 | { 37 | _cbdt.FillGlyphInfo(glyphs[i]); 38 | } 39 | return glyphs; 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/EBSC.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | 8 | //from 9 | //https://docs.microsoft.com/en-us/typography/opentype/spec/ebsc 10 | 11 | //The EBSC table provides a mechanism for describing embedded bitmaps 12 | //which are created by scaling other embedded bitmaps. 13 | //While this is the sort of thing that outline font technologies were invented to avoid, 14 | //there are cases (small sizes of Kanji, for example) 15 | //where scaling a bitmap produces a more legible font 16 | //than scan-converting an outline. 17 | 18 | //For this reason the EBSC table allows a font to define a bitmap strike 19 | //as a scaled version of another strike. 20 | 21 | //The EBSC table is used together with the EBDT table, 22 | //which provides embedded monochrome or grayscale bitmap data, 23 | //and the EBLC table, which provides embedded bitmap locators. 24 | 25 | 26 | /// 27 | /// EBSC — Embedded Bitmap Scaling Table 28 | /// 29 | class EBSC : TableEntry 30 | { 31 | public const string _N = "EBSC"; 32 | public override string Name => _N; 33 | 34 | protected override void ReadContentFrom(BinaryReader reader) 35 | { 36 | 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /OpenFont/Tables.TrueType/Cvt_Programs.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2015-2016, Michael Popoloski, WinterDev 2 | 3 | using System.IO; 4 | namespace Typography.OpenFont.Tables 5 | { 6 | 7 | class CvtTable : TableEntry 8 | { 9 | public const string _N = "cvt ";//need 4 chars//*** 10 | public override string Name => _N; 11 | 12 | // 13 | 14 | /// 15 | /// control value in font unit 16 | /// 17 | internal int[] _controlValues; 18 | protected override void ReadContentFrom(BinaryReader reader) 19 | { 20 | int nelems = (int)(this.TableLength / sizeof(short)); 21 | var results = new int[nelems]; 22 | for (int i = 0; i < nelems; i++) 23 | { 24 | results[i] = reader.ReadInt16(); 25 | } 26 | _controlValues = results; 27 | } 28 | } 29 | class PrepTable : TableEntry 30 | { 31 | public const string _N = "prep"; 32 | public override string Name => _N; 33 | // 34 | 35 | internal byte[] _programBuffer; 36 | // 37 | protected override void ReadContentFrom(BinaryReader reader) 38 | { 39 | _programBuffer = reader.ReadBytes((int)this.TableLength); 40 | } 41 | } 42 | class FpgmTable : TableEntry 43 | { 44 | public const string _N = "fpgm"; 45 | public override string Name => _N; 46 | // 47 | 48 | internal byte[] _programBuffer; 49 | protected override void ReadContentFrom(BinaryReader reader) 50 | { 51 | _programBuffer = reader.ReadBytes((int)this.TableLength); 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Izbirkom21/FixedContentMorpher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text.RegularExpressions; 5 | using ExCSS; 6 | using HtmlAgilityPack; 7 | 8 | namespace Izbirkom21 9 | { 10 | public class FixedContentMorpher 11 | { 12 | // css class name -> replace text 13 | Dictionary> _classToMorpher = new(); 14 | 15 | public void VisitStyleRule(IStyleRule styleRule) 16 | { 17 | var content = styleRule.Style.Content; 18 | var isTrashStyle = Trash.IsTrashStyle(styleRule.Style); 19 | if (!string.IsNullOrEmpty(content) || isTrashStyle) 20 | { 21 | var toReplace = isTrashStyle ? "" : content.Trim('"'); 22 | foreach (Match match in Regex.Matches(styleRule.SelectorText, "\\.([\\w_]+)")) 23 | { 24 | var className = match.Groups[0].Value.Trim('.'); 25 | if (className.Length <= 7) // 🦄 26 | { 27 | _classToMorpher.Add(className, _ => toReplace); 28 | } 29 | } 30 | } 31 | } 32 | 33 | public void ReplaceFixedContent(HtmlDocument htmlDocument, FontReplacer fonts) 34 | { 35 | // modify content (reverse to process nested first) 36 | foreach (var node in htmlDocument.DocumentNode.SelectNodes("//span|//b|//td").Reverse()) 37 | { 38 | var cls = node.GetAttributeValue("class", ""); 39 | if (_classToMorpher.TryGetValue(cls, out var replaceTo)) 40 | { 41 | node.InnerHtml = replaceTo(node.InnerText); 42 | node.Attributes.Remove("class"); 43 | } 44 | 45 | // custom fonts 46 | fonts.TranslateNode(cls, node); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /OpenFont/AdditionalInfo/OS2_IBMFontClassParameters.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //https://www.microsoft.com/typography/otspec/ibmfc.htm 3 | 4 | namespace Typography.OpenFont 5 | { 6 | 7 | //This section defines the IBM Font Class 8 | //and the IBM Font Subclass parameter values 9 | //to be used in the classification of font designs by the font designer or supplier. 10 | // 11 | //This information is stored in the sFamilyClass field of a font's OS/2 table. 12 | 13 | 14 | [System.Flags] 15 | enum IBMFontClassParametersKind 16 | { 17 | No_Classification = 0 << 8, 18 | // 19 | //class id 1, OldStyle Serifs 20 | // 21 | Class1 = 1 << 8, 22 | OldStyle_Serifs = Class1, 23 | Class1_No_Classification = Class1 | 0, 24 | Class1_IBM_Rounded_Legibility = Class1 | 1, 25 | Class1_Garalde = Class1 | 2, 26 | Class1_Venetian = Class1 | 3, 27 | Class1_Modified_Venetian = Class1 | 4, 28 | Class1_Dutch_Modern = Class1 | 5, 29 | Class1_Dutch_Traditional = Class1 | 6, 30 | Class1_Comtemporary = Class1 | 7, 31 | Class1_Calligraphic = Class1 | 8, 32 | //subclass 9-14 -> (reserved for future use) 33 | Class1_Miscellaneous = Class1 | 15, 34 | // 35 | //class id 2, Transitional Serifs 36 | // 37 | Class2 = 2 << 8, 38 | Class2_No_Classification = Class2 | 0, 39 | Class2_Direct_Line = Class2 | 1, 40 | Class2_Script = Class2 | 2, 41 | //subclass 3-14 -> (reserved for future use) 42 | Class2_Miscellaneous = Class2 | 15, 43 | // 44 | //class id 3, Modern Serifs 45 | // 46 | //TODO... add more... 47 | 48 | 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /OpenFont/Tables/TableEntry.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System; 5 | using System.IO; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | /// 9 | /// this is base class of all 'top' font table 10 | /// 11 | public abstract class TableEntry 12 | { 13 | public TableEntry() 14 | { 15 | } 16 | internal TableHeader Header { get; set; } 17 | protected abstract void ReadContentFrom(BinaryReader reader); 18 | public abstract string Name { get; } 19 | internal void LoadDataFrom(BinaryReader reader) 20 | { 21 | //ensure that we always start at the correct offset*** 22 | reader.BaseStream.Seek(this.Header.Offset, SeekOrigin.Begin); 23 | ReadContentFrom(reader); 24 | } 25 | public uint TableLength => this.Header.Length; 26 | 27 | } 28 | class UnreadTableEntry : TableEntry 29 | { 30 | public UnreadTableEntry(TableHeader header) 31 | { 32 | this.Header = header; 33 | } 34 | public override string Name => this.Header.Tag; 35 | // 36 | protected sealed override void ReadContentFrom(BinaryReader reader) 37 | { 38 | //intend *** 39 | throw new NotImplementedException(); 40 | } 41 | 42 | public bool HasCustomContentReader { get; protected set; } 43 | public virtual T CreateTableEntry(BinaryReader reader, T expectedResult) 44 | where T : TableEntry 45 | { 46 | throw new NotImplementedException(); 47 | } 48 | #if DEBUG 49 | public override string ToString() 50 | { 51 | return this.Name; 52 | } 53 | #endif 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /izbirkom21.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31710.8 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Izbirkom21", "Izbirkom21\Izbirkom21.csproj", "{406F57B4-06F9-49EB-B836-9596641E3220}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenFont", "OpenFont\OpenFont.csproj", "{042710D0-13D9-4C49-89F3-6B6D59F107D6}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schwabra", "Schwabra\Schwabra.csproj", "{94E4C9B4-71FD-4135-B333-B9949E5B0B7D}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {406F57B4-06F9-49EB-B836-9596641E3220}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {406F57B4-06F9-49EB-B836-9596641E3220}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {406F57B4-06F9-49EB-B836-9596641E3220}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {406F57B4-06F9-49EB-B836-9596641E3220}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {042710D0-13D9-4C49-89F3-6B6D59F107D6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {042710D0-13D9-4C49-89F3-6B6D59F107D6}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {042710D0-13D9-4C49-89F3-6B6D59F107D6}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {042710D0-13D9-4C49-89F3-6B6D59F107D6}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {94E4C9B4-71FD-4135-B333-B9949E5B0B7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {94E4C9B4-71FD-4135-B333-B9949E5B0B7D}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {94E4C9B4-71FD-4135-B333-B9949E5B0B7D}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {94E4C9B4-71FD-4135-B333-B9949E5B0B7D}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(ExtensibilityGlobals) = postSolution 35 | SolutionGuid = {95430CAB-205F-4820-9138-1A9593AD0D55} 36 | EndGlobalSection 37 | EndGlobal 38 | -------------------------------------------------------------------------------- /OpenFont/Geometry.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2015, Michael Popoloski's SharpFont, 2 | //MIT, 2016-present, WinterDev 3 | 4 | 5 | using System.Numerics; 6 | namespace Typography.OpenFont 7 | { 8 | [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 9 | public struct GlyphPointF 10 | { 11 | //from https://docs.microsoft.com/en-us/typography/opentype/spec/glyf 12 | //'point' of the glyph contour. 13 | //eg. ... In the glyf table, the position of a point ... 14 | // ... the point is on the curve; otherwise, it is off the curve.... 15 | 16 | internal Vector2 P; 17 | internal bool onCurve; 18 | 19 | public GlyphPointF(float x, float y, bool onCurve) 20 | { 21 | P = new Vector2(x, y); 22 | this.onCurve = onCurve; 23 | } 24 | public GlyphPointF(Vector2 position, bool onCurve) 25 | { 26 | P = position; 27 | this.onCurve = onCurve; 28 | } 29 | public float X => this.P.X; 30 | public float Y => this.P.Y; 31 | 32 | public static GlyphPointF operator *(GlyphPointF p, float n) 33 | { 34 | return new GlyphPointF(p.P * n, p.onCurve); 35 | } 36 | 37 | //----------------------------------------- 38 | 39 | internal GlyphPointF Offset(short dx, short dy) { return new GlyphPointF(new Vector2(P.X + dx, P.Y + dy), onCurve); } 40 | 41 | internal void ApplyScale(float scale) 42 | { 43 | P *= scale; 44 | } 45 | internal void ApplyScaleOnlyOnXAxis(float scale) 46 | { 47 | P = new Vector2(P.X * scale, P.Y); 48 | } 49 | 50 | internal void UpdateX(float x) 51 | { 52 | this.P.X = x; 53 | } 54 | internal void UpdateY(float y) 55 | { 56 | this.P.Y = y; 57 | } 58 | internal void OffsetY(float dy) 59 | { 60 | this.P.Y += dy; 61 | } 62 | internal void OffsetX(float dx) 63 | { 64 | this.P.X += dx; 65 | } 66 | #if DEBUG 67 | internal bool dbugIsEqualsWith(GlyphPointF another) 68 | { 69 | return this.P == another.P && this.onCurve == another.onCurve; 70 | } 71 | public override string ToString() { return P.ToString() + " " + onCurve.ToString(); } 72 | #endif 73 | } 74 | 75 | 76 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Деобфускатор результатов выборов 2021 2 | ============== 3 | 4 | На вход требуется сохранённая html страница с сайта izbirkom.ru. 5 | В выходной файл запишется версия страницы без интерактивных элементов и стилей в удобном для дальнейшего анализа виде. 6 | 7 | Обфускатор может работать в полностью оффлайн режиме, если у него есть доступ к шрифтам, которые имеются в количестве 100 штук на сайте избиркома. Они кешируются в папку `fonts_cache`. Если шрифт с необходимым именем уже есть, то загружаться снова он не будет. 8 | 9 | 10 | Для запуска требуется [.net 5.0 SDK](https://dotnet.microsoft.com/download), должно работать на Windows/Linux/Mac. 11 | 12 | ![Пример результата](https://raw.githubusercontent.com/ulex/izbirkom21/master/samples/result.png) 13 | 14 | 15 | [Исходная страница](https://raw.githubusercontent.com/ulex/izbirkom21/master/samples/test.in.html) была получена с помощью команды 16 | ``` 17 | curl -A 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0' 'http://www.crimea.vybory.izbirkom.ru/region/izbirkom?action=show&root=1000050&tvd=100100225883448&vrn=100100225883172&prver=0&pronetvd=null®ion=19&sub_region=19&type=463&report_mode=null' > test.in.html 18 | ``` 19 | [Пример деобфусцированного результата](https://raw.githubusercontent.com/ulex/izbirkom21/master/samples/test.out.html), который был получен с помощью команды 20 | ``` 21 | dotnet run --project izbirkom21 samples/test.in.html samples/test.out.html 22 | ``` 23 | 24 | Работа в пакетном режиме 25 | ---- 26 | 27 | Теперь можно запускать приложение на директориях. В этом случае, будут пытаться деобфусцироваться все файлы, которые есть в параметре 1 и записываться в директорию, указанную в параметре 2. Этот режим существенно быстрее обработки файлов по одному и на моём оборудовании обрабатывает примерно 40 файлов в секунду. 28 | ``` 29 | dotnet run --configuration Release --project izbirkom21 samples outDirectory 30 | ``` 31 | 32 | Сырые данные 33 | ---- 34 | 35 | Пользователь @illusionofchaos предлагает уже загруженные данные и шрифты с ЦИК по федеральному избирательному округу: 36 | https://github.com/illusionofchaos/IzbirkomRipper/releases/tag/0.0.1 37 | 38 | Достоверность данных я не гарантирую 39 | 40 | 41 | Используемые зависимости 42 | ---- 43 | 44 | https://github.com/LayoutFarm/Typography 45 | MIT 46 | 47 | https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE 48 | MIT 49 | 50 | https://github.com/TylerBrinks/ExCSS 51 | MIT 52 | 53 | -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/COLR.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present Sam Hocevar , WinterDev 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | 7 | namespace Typography.OpenFont.Tables 8 | { 9 | public class COLR : TableEntry 10 | { 11 | public const string _N = "COLR"; 12 | public override string Name => _N; 13 | 14 | // Read the COLR table 15 | // https://docs.microsoft.com/en-us/typography/opentype/spec/colr 16 | protected override void ReadContentFrom(BinaryReader reader) 17 | { 18 | long offset = reader.BaseStream.Position; 19 | 20 | //Type Name Description 21 | //uint16 version Table version number(starts at 0). 22 | //uint16 numBaseGlyphRecords Number of Base Glyph Records. 23 | //Offset32 baseGlyphRecordsOffset Offset(from beginning of COLR table) to Base Glyph records. 24 | //Offset32 layerRecordsOffset Offset(from beginning of COLR table) to Layer Records. 25 | //uint16 numLayerRecords Number of Layer Records. 26 | 27 | 28 | ushort version = reader.ReadUInt16(); 29 | ushort numBaseGlyphRecords = reader.ReadUInt16(); 30 | uint baseGlyphRecordsOffset = reader.ReadUInt32(); 31 | uint layerRecordsOffset = reader.ReadUInt32(); 32 | ushort numLayerRecords = reader.ReadUInt16(); 33 | 34 | GlyphLayers = new ushort[numLayerRecords]; 35 | GlyphPalettes = new ushort[numLayerRecords]; 36 | 37 | reader.BaseStream.Seek(offset + baseGlyphRecordsOffset, SeekOrigin.Begin); 38 | for (int i = 0; i < numBaseGlyphRecords; ++i) 39 | { 40 | ushort gid = reader.ReadUInt16(); 41 | LayerIndices[gid] = reader.ReadUInt16(); 42 | LayerCounts[gid] = reader.ReadUInt16(); 43 | } 44 | 45 | reader.BaseStream.Seek(offset + layerRecordsOffset, SeekOrigin.Begin); 46 | for (int i = 0; i < GlyphLayers.Length; ++i) 47 | { 48 | GlyphLayers[i] = reader.ReadUInt16(); 49 | GlyphPalettes[i] = reader.ReadUInt16(); 50 | } 51 | } 52 | 53 | public ushort[] GlyphLayers { get; private set; } 54 | public ushort[] GlyphPalettes { get; private set; } 55 | public readonly Dictionary LayerIndices = new Dictionary(); 56 | public readonly Dictionary LayerCounts = new Dictionary(); 57 | } 58 | } 59 | 60 | -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/ScriptList.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace Typography.OpenFont.Tables 7 | { 8 | public class ScriptList : Dictionary 9 | { 10 | //https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2 11 | // The ScriptList identifies the scripts in a font, 12 | // each of which is represented by a Script table that contains script and language-system data. 13 | // Language system tables reference features, which are defined in the FeatureList. 14 | // Each feature table references the lookup data defined in the LookupList that describes how, when, and where to implement the feature. 15 | private ScriptList() { } 16 | public new ScriptTable this[uint tagName] => TryGetValue(tagName, out ScriptTable ret) ? ret : null; 17 | 18 | 19 | public static ScriptList CreateFrom(BinaryReader reader, long beginAt) 20 | { 21 | 22 | // ScriptList table 23 | // Type Name Description 24 | // uint16 scriptCount Number of ScriptRecords 25 | // ScriptRecord scriptRecords[scriptCount] Array of ScriptRecords,listed alphabetically by ScriptTag 26 | // 27 | // ScriptRecord 28 | // Type Name Description 29 | // Tag scriptTag 4-byte ScriptTag identifier 30 | // Offset16 scriptOffset Offset to Script table-from beginning of ScriptList 31 | 32 | reader.BaseStream.Seek(beginAt, SeekOrigin.Begin); 33 | 34 | ushort scriptCount = reader.ReadUInt16(); 35 | ScriptList scriptList = new ScriptList(); 36 | 37 | // Read records (tags and table offsets) 38 | uint[] scriptTags = new uint[scriptCount]; 39 | ushort[] scriptOffsets = new ushort[scriptCount]; 40 | for (int i = 0; i < scriptCount; ++i) 41 | { 42 | scriptTags[i] = reader.ReadUInt32(); 43 | scriptOffsets[i] = reader.ReadUInt16(); 44 | } 45 | 46 | // Read each table and add it to the dictionary 47 | for (int i = 0; i < scriptCount; ++i) 48 | { 49 | ScriptTable scriptTable = ScriptTable.CreateFrom(reader, beginAt + scriptOffsets[i]); 50 | scriptTable.scriptTag = scriptTags[i]; 51 | scriptList.Add(scriptTags[i], scriptTable); 52 | } 53 | 54 | return scriptList; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Schwabra/Migrations/InitialCreate.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.EntityFrameworkCore.Migrations; 2 | 3 | namespace Schwabra.Migrations 4 | { 5 | public partial class InitialCreate : Migration 6 | { 7 | protected override void Up(MigrationBuilder migrationBuilder) 8 | { 9 | migrationBuilder.CreateTable( 10 | name: "station", 11 | columns: table => new 12 | { 13 | id = table.Column(type: "INTEGER", nullable: false) 14 | .Annotation("Sqlite:Autoincrement", true), 15 | name = table.Column(type: "TEXT", nullable: true), 16 | filename = table.Column(type: "TEXT", nullable: true), 17 | path = table.Column(type: "TEXT", nullable: true) 18 | }, 19 | constraints: table => 20 | { 21 | table.PrimaryKey("PK_station", x => x.id); 22 | }); 23 | 24 | migrationBuilder.CreateTable( 25 | name: "result", 26 | columns: table => new 27 | { 28 | id = table.Column(type: "INTEGER", nullable: false) 29 | .Annotation("Sqlite:Autoincrement", true), 30 | num = table.Column(type: "INTEGER", nullable: false), 31 | title = table.Column(type: "TEXT", nullable: true), 32 | value = table.Column(type: "INTEGER", nullable: false), 33 | value_percent = table.Column(type: "REAL", nullable: true), 34 | Stationid = table.Column(type: "INTEGER", nullable: true) 35 | }, 36 | constraints: table => 37 | { 38 | table.PrimaryKey("PK_result", x => x.id); 39 | table.ForeignKey( 40 | name: "FK_result_station_Stationid", 41 | column: x => x.Stationid, 42 | principalTable: "station", 43 | principalColumn: "id", 44 | onDelete: ReferentialAction.Restrict); 45 | }); 46 | 47 | migrationBuilder.CreateIndex( 48 | name: "IX_result_Stationid", 49 | table: "result", 50 | column: "Stationid"); 51 | } 52 | 53 | protected override void Down(MigrationBuilder migrationBuilder) 54 | { 55 | migrationBuilder.DropTable( 56 | name: "result"); 57 | 58 | migrationBuilder.DropTable( 59 | name: "station"); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Schwabra/Migrations/ElectionContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 6 | using Schwabra; 7 | 8 | namespace Schwabra.Migrations 9 | { 10 | [DbContext(typeof(ElectionContext))] 11 | partial class ElectionContextModelSnapshot : ModelSnapshot 12 | { 13 | protected override void BuildModel(ModelBuilder modelBuilder) 14 | { 15 | #pragma warning disable 612, 618 16 | modelBuilder 17 | .HasAnnotation("ProductVersion", "5.0.10"); 18 | 19 | modelBuilder.Entity("Schwabra.Result", b => 20 | { 21 | b.Property("id") 22 | .ValueGeneratedOnAdd() 23 | .HasColumnType("INTEGER"); 24 | 25 | b.Property("Stationid") 26 | .HasColumnType("INTEGER"); 27 | 28 | b.Property("num") 29 | .HasColumnType("INTEGER"); 30 | 31 | b.Property("title") 32 | .HasColumnType("TEXT"); 33 | 34 | b.Property("value") 35 | .HasColumnType("INTEGER"); 36 | 37 | b.Property("value_percent") 38 | .HasColumnType("REAL"); 39 | 40 | b.HasKey("id"); 41 | 42 | b.HasIndex("Stationid"); 43 | 44 | b.ToTable("result"); 45 | }); 46 | 47 | modelBuilder.Entity("Schwabra.Station", b => 48 | { 49 | b.Property("id") 50 | .ValueGeneratedOnAdd() 51 | .HasColumnType("INTEGER"); 52 | 53 | b.Property("filename") 54 | .HasColumnType("TEXT"); 55 | 56 | b.Property("name") 57 | .HasColumnType("TEXT"); 58 | 59 | b.Property("path") 60 | .HasColumnType("TEXT"); 61 | 62 | b.HasKey("id"); 63 | 64 | b.ToTable("station"); 65 | }); 66 | 67 | modelBuilder.Entity("Schwabra.Result", b => 68 | { 69 | b.HasOne("Schwabra.Station", null) 70 | .WithMany("rows") 71 | .HasForeignKey("Stationid"); 72 | }); 73 | 74 | modelBuilder.Entity("Schwabra.Station", b => 75 | { 76 | b.Navigation("rows"); 77 | }); 78 | #pragma warning restore 612, 618 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Schwabra/Migrations/InitialCreate.Designer.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using Microsoft.EntityFrameworkCore; 4 | using Microsoft.EntityFrameworkCore.Infrastructure; 5 | using Microsoft.EntityFrameworkCore.Migrations; 6 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 7 | using Schwabra; 8 | 9 | namespace Schwabra.Migrations 10 | { 11 | [DbContext(typeof(ElectionContext))] 12 | [Migration("20210922220720_InitialCreate")] 13 | partial class InitialCreate 14 | { 15 | protected override void BuildTargetModel(ModelBuilder modelBuilder) 16 | { 17 | #pragma warning disable 612, 618 18 | modelBuilder 19 | .HasAnnotation("ProductVersion", "5.0.10"); 20 | 21 | modelBuilder.Entity("Schwabra.Result", b => 22 | { 23 | b.Property("id") 24 | .ValueGeneratedOnAdd() 25 | .HasColumnType("INTEGER"); 26 | 27 | b.Property("Stationid") 28 | .HasColumnType("INTEGER"); 29 | 30 | b.Property("num") 31 | .HasColumnType("INTEGER"); 32 | 33 | b.Property("title") 34 | .HasColumnType("TEXT"); 35 | 36 | b.Property("value") 37 | .HasColumnType("INTEGER"); 38 | 39 | b.Property("value_percent") 40 | .HasColumnType("REAL"); 41 | 42 | b.HasKey("id"); 43 | 44 | b.HasIndex("Stationid"); 45 | 46 | b.ToTable("result"); 47 | }); 48 | 49 | modelBuilder.Entity("Schwabra.Station", b => 50 | { 51 | b.Property("id") 52 | .ValueGeneratedOnAdd() 53 | .HasColumnType("INTEGER"); 54 | 55 | b.Property("filename") 56 | .HasColumnType("TEXT"); 57 | 58 | b.Property("name") 59 | .HasColumnType("TEXT"); 60 | 61 | b.Property("path") 62 | .HasColumnType("TEXT"); 63 | 64 | b.HasKey("id"); 65 | 66 | b.ToTable("station"); 67 | }); 68 | 69 | modelBuilder.Entity("Schwabra.Result", b => 70 | { 71 | b.HasOne("Schwabra.Station", null) 72 | .WithMany("rows") 73 | .HasForeignKey("Stationid"); 74 | }); 75 | 76 | modelBuilder.Entity("Schwabra.Station", b => 77 | { 78 | b.Navigation("rows"); 79 | }); 80 | #pragma warning restore 612, 618 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/EBDT.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | //from https://docs.microsoft.com/en-us/typography/opentype/spec/ebdt 8 | 9 | //The EBDT table is used to embed monochrome or grayscale bitmap glyph data. 10 | //It is used together with the EBLC table, 11 | //which provides embedded bitmap locators, 12 | //and the EBSC table, which provides embedded bitmap scaling information. 13 | 14 | //OpenType embedded bitmaps are also called “sbits” (for “scaler bitmaps”). 15 | //A set of bitmaps for a face at a given size is called a strike. 16 | 17 | //The EBLC table identifies the sizes and glyph ranges of the sbits, 18 | //and keeps offsets to glyph bitmap data in indexSubTables. 19 | 20 | //The EBDT table then stores the glyph bitmap data, 21 | //in a number of different possible formats. 22 | //Glyph metrics information may be stored in either the EBLC or EBDT table, 23 | //depending upon the indexSubTable and glyph bitmap data formats. 24 | 25 | //The EBSC table identifies sizes that will be handled by scaling up or scaling down other sbit sizes. 26 | 27 | 28 | //The EBDT table is a super set of Apple’s Apple Advanced Typography (AAT) 'bdat' table. 29 | 30 | 31 | /// 32 | /// Embedded Bitmap Data Table 33 | /// 34 | class EBDT : TableEntry 35 | { 36 | public const string _N = "EBDT"; 37 | public override string Name => _N; 38 | 39 | protected override void ReadContentFrom(BinaryReader reader) 40 | { 41 | ushort majorVersion = reader.ReadUInt16(); 42 | ushort minorVersion = reader.ReadUInt16(); 43 | 44 | //The rest of the EBDT table is a collection of bitmap data. 45 | //The data can be in a number of possible formats, 46 | //indicated by information in the EBLC table. 47 | 48 | //Some of the formats contain metric information plus image data, 49 | //and other formats contain only the image data. 50 | //Long word alignment is not required for these sub tables; 51 | //byte alignment is sufficient. 52 | 53 | //There are also two different formats for glyph metrics: 54 | //big glyph metrics and small glyph metrics. 55 | //Big glyph metrics define metrics information 56 | //for both horizontal and vertical layouts. 57 | //This is important in fonts(such as Kanji) where both types of layout may be used. 58 | //Small glyph metrics define metrics information for one layout direction only. 59 | //Which direction applies, horizontal or vertical, is determined by the flags field in the BitmapSize 60 | //tables within the EBLC table. 61 | 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /OpenFont/PreviewFontInfo.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | 5 | using Typography.OpenFont.Tables; 6 | 7 | namespace Typography.OpenFont 8 | { 9 | public class PreviewFontInfo 10 | { 11 | public readonly string Name; 12 | public readonly string SubFamilyName; 13 | public readonly Extensions.TranslatedOS2FontStyle OS2TranslatedStyle; 14 | public readonly Extensions.OS2FsSelection OS2FsSelection; 15 | 16 | readonly PreviewFontInfo[] _ttcfMembers; 17 | 18 | public Languages Languages { get; } 19 | public NameEntry NameEntry { get; } 20 | public OS2Table OS2Table { get; } 21 | 22 | internal PreviewFontInfo( 23 | NameEntry nameEntry, 24 | OS2Table os2Table, 25 | Languages langs) 26 | { 27 | NameEntry = nameEntry; 28 | OS2Table = os2Table; 29 | Languages = langs; 30 | 31 | Name = nameEntry.FontName; 32 | SubFamilyName = nameEntry.FontSubFamily; 33 | OS2TranslatedStyle = Extensions.TypefaceExtensions.TranslateOS2FontStyle(os2Table); 34 | OS2FsSelection = Extensions.TypefaceExtensions.TranslateOS2FsSelection(os2Table); 35 | } 36 | internal PreviewFontInfo(string fontName, PreviewFontInfo[] ttcfMembers) 37 | { 38 | Name = fontName; 39 | SubFamilyName = ""; 40 | _ttcfMembers = ttcfMembers; 41 | Languages = new Languages(); 42 | } 43 | 44 | public string TypographicFamilyName => (NameEntry?.TypographicFamilyName) ?? string.Empty; 45 | public string TypographicSubFamilyName => (NameEntry?.TypographyicSubfamilyName) ?? string.Empty; 46 | public string PostScriptName => (NameEntry?.PostScriptName) ?? string.Empty; 47 | public string UniqueFontIden => (NameEntry?.UniqueFontIden) ?? string.Empty; 48 | public string VersionString => (NameEntry?.VersionString) ?? string.Empty; 49 | public ushort WeightClass => (OS2Table != null) ? OS2Table.usWeightClass : ushort.MinValue; 50 | public ushort WidthClass => (OS2Table != null) ? OS2Table.usWidthClass : ushort.MinValue; 51 | 52 | 53 | public int ActualStreamOffset { get; internal set; } 54 | public bool IsWebFont { get; internal set; } 55 | public bool IsFontCollection => _ttcfMembers != null; 56 | /// 57 | /// get font collection's member count 58 | /// 59 | public int MemberCount => _ttcfMembers.Length; 60 | /// 61 | /// get font collection's member 62 | /// 63 | /// 64 | /// 65 | public PreviewFontInfo GetMember(int index) => _ttcfMembers[index]; 66 | #if DEBUG 67 | public override string ToString() 68 | { 69 | return (IsFontCollection) ? Name : Name + ", " + SubFamilyName + ", " + OS2TranslatedStyle; 70 | } 71 | #endif 72 | } 73 | 74 | 75 | } -------------------------------------------------------------------------------- /OpenFont/Tables.Others/HorizontalDeviceMetrics.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | 8 | class HorizontalDeviceMetrics : TableEntry 9 | { 10 | public const string _N = "hdmx"; 11 | public override string Name => _N; 12 | // 13 | //https://www.microsoft.com/typography/otspec/hdmx.htm 14 | //The hdmx table relates to OpenType™ fonts with TrueType outlines. 15 | //The Horizontal Device Metrics table stores integer advance widths scaled to particular pixel sizes. 16 | //This allows the font manager to build integer width tables without calling the scaler for each glyph. 17 | //Typically this table contains only selected screen sizes. 18 | //This table is sorted by pixel size. 19 | //The checksum for this table applies to both subtables listed. 20 | 21 | //Note that for non-square pixel grids, 22 | //the character width (in pixels) will be used to determine which device record to use. 23 | //For example, a 12 point character on a device with a resolution of 72x96 would be 12 pixels high and 16 pixels wide. 24 | //The hdmx device record for 16 pixel characters would be used. 25 | 26 | //If bit 4 of the flag field in the 'head' table is not set, 27 | //then it is assumed that the font scales linearly; in this case an 'hdmx' table is not necessary and should not be built. 28 | //If bit 4 of the flag field is set, then one or more glyphs in the font are assumed to scale nonlinearly. 29 | //In this case, performance can be improved by including the 'hdmx' table with one or more important DeviceRecord's for important sizes. 30 | //Please see the chapter “Recommendations for OpenType Fonts” for more detail. 31 | 32 | //The table begins as follows: 33 | //hdmx Header 34 | //Type Name Description 35 | //USHORT version Table version number (0) 36 | //SHORT numRecords Number of device records. 37 | //LONG sizeDeviceRecord Size of a device record, long aligned. 38 | //DeviceRecord records[numRecords] Array of device records. 39 | 40 | //Each DeviceRecord for format 0 looks like this. 41 | //Device Record 42 | //Type Name Description 43 | //BYTE pixelSize Pixel size for following widths (as ppem). 44 | //BYTE maxWidth Maximum width. 45 | //BYTE widths[numGlyphs] Array of widths (numGlyphs is from the 'maxp' table). 46 | 47 | //Each DeviceRecord is padded with 0's to make it long word aligned. 48 | 49 | //Each Width value is the width of the particular glyph, in pixels, 50 | //at the pixels per em (ppem) size listed at the start of the DeviceRecord. 51 | 52 | //The ppem sizes are measured along the y axis. 53 | 54 | protected override void ReadContentFrom(BinaryReader reader) 55 | { 56 | 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /OpenFont/ByteOrderSwappingBinaryReader.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 2 | 3 | using System; 4 | using System.IO; 5 | 6 | namespace Typography.OpenFont 7 | { 8 | class ByteOrderSwappingBinaryReader : BinaryReader 9 | { 10 | //All OpenType fonts use Motorola-style byte ordering (Big Endian) 11 | // 12 | public ByteOrderSwappingBinaryReader(Stream input) 13 | : base(input) 14 | { 15 | } 16 | protected override void Dispose(bool disposing) 17 | { 18 | GC.SuppressFinalize(this); 19 | base.Dispose(disposing); 20 | } 21 | // 22 | //as original 23 | // 24 | //public override byte ReadByte() { return base.ReadByte(); } 25 | // 26 | //we override the 4 methods here 27 | // 28 | public override short ReadInt16() => BitConverter.ToInt16(RR(2), 8 - 2); 29 | public override ushort ReadUInt16() => BitConverter.ToUInt16(RR(2), 8 - 2); 30 | public override uint ReadUInt32() => BitConverter.ToUInt32(RR(4), 8 - 4); 31 | public override ulong ReadUInt64() => BitConverter.ToUInt64(RR(8), 8 - 8); 32 | 33 | 34 | //used in CFF font 35 | public override double ReadDouble() => BitConverter.ToDouble(RR(8), 8 - 8); 36 | //used in CFF font 37 | public override int ReadInt32() => BitConverter.ToInt32(RR(4), 8 - 4); 38 | 39 | // 40 | readonly byte[] _reusable_buffer = new byte[8]; //fix buffer size to 8 bytes 41 | /// 42 | /// read and reverse 43 | /// 44 | /// 45 | /// 46 | private byte[] RR(int count) 47 | { 48 | base.Read(_reusable_buffer, 0, count); 49 | Array.Reverse(_reusable_buffer); 50 | return _reusable_buffer; 51 | } 52 | 53 | //we don't use these methods in our OpenFont, so => throw the exception 54 | public override int PeekChar() { throw new NotImplementedException(); } 55 | public override int Read() { throw new NotImplementedException(); } 56 | public override int Read(byte[] buffer, int index, int count) => base.Read(buffer, index, count); 57 | public override int Read(char[] buffer, int index, int count) { throw new NotImplementedException(); } 58 | public override bool ReadBoolean() { throw new NotImplementedException(); } 59 | public override char ReadChar() { throw new NotImplementedException(); } 60 | public override char[] ReadChars(int count) { throw new NotImplementedException(); } 61 | public override decimal ReadDecimal() { throw new NotImplementedException(); } 62 | 63 | public override long ReadInt64() { throw new NotImplementedException(); } 64 | public override sbyte ReadSByte() { throw new NotImplementedException(); } 65 | public override float ReadSingle() { throw new NotImplementedException(); } 66 | public override string ReadString() { throw new NotImplementedException(); } 67 | // 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/AttachmentListTable.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | //https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2 8 | //Attachment List Table 9 | 10 | //The Attachment List table (AttachList) may be used to cache attachment point coordinates along with glyph bitmaps. 11 | 12 | //The table consists of an offset to a Coverage table (Coverage) listing all glyphs that define attachment points in the GPOS table, 13 | //a count of the glyphs with attachment points (GlyphCount), and an array of offsets to AttachPoint tables (AttachPoint). 14 | //The array lists the AttachPoint tables, one for each glyph in the Coverage table, in the same order as the Coverage Index. 15 | //AttachList table 16 | //Type Name Description 17 | //Offset16 Coverage Offset to Coverage table - from beginning of AttachList table 18 | //unint16 GlyphCount Number of glyphs with attachment points 19 | //Offset16 AttachPoint[GlyphCount] Array of offsets to AttachPoint tables-from beginning of AttachList table-in Coverage Index order 20 | 21 | //An AttachPoint table consists of a count of the attachment points on a single glyph (PointCount) and 22 | //an array of contour indices of those points (PointIndex), listed in increasing numerical order. 23 | 24 | //Example 3 at the end of the chapter demonstrates an AttachList table that defines attachment points for two glyphs. 25 | //AttachPoint table 26 | //Type Name Description 27 | //uint16 PointCount Number of attachment points on this glyph 28 | //uint16 PointIndex[PointCount] Array of contour point indices -in increasing numerical order 29 | 30 | class AttachmentListTable 31 | { 32 | AttachPoint[] _attachPoints; 33 | public CoverageTable CoverageTable { get; private set; } 34 | public static AttachmentListTable CreateFrom(BinaryReader reader, long beginAt) 35 | { 36 | AttachmentListTable attachmentListTable = new AttachmentListTable(); 37 | reader.BaseStream.Seek(beginAt, SeekOrigin.Begin); 38 | // 39 | ushort coverageOffset = reader.ReadUInt16(); 40 | ushort glyphCount = reader.ReadUInt16(); 41 | ushort[] attachPointOffsets = Utils.ReadUInt16Array(reader, glyphCount); 42 | //----------------------- 43 | attachmentListTable.CoverageTable = CoverageTable.CreateFrom(reader, beginAt + coverageOffset); 44 | attachmentListTable._attachPoints = new AttachPoint[glyphCount]; 45 | for (int i = 0; i < glyphCount; ++i) 46 | { 47 | reader.BaseStream.Seek(beginAt + attachPointOffsets[i], SeekOrigin.Begin); 48 | ushort pointCount = reader.ReadUInt16(); 49 | attachmentListTable._attachPoints[i] = new AttachPoint() 50 | { 51 | pointIndices = Utils.ReadUInt16Array(reader, pointCount) 52 | }; 53 | } 54 | 55 | return attachmentListTable; 56 | } 57 | struct AttachPoint 58 | { 59 | public ushort[] pointIndices; 60 | } 61 | } 62 | 63 | } -------------------------------------------------------------------------------- /OpenFont/Tables.Others/VerticalDeviceMetrics.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | class VerticalDeviceMetrics : TableEntry 8 | { 9 | public const string _N = "VDMX"; 10 | public override string Name => _N; 11 | // 12 | //https://docs.microsoft.com/en-us/typography/opentype/spec/vdmx 13 | //VDMX - Vertical Device Metrics 14 | //The VDMX table relates to OpenType™ fonts with TrueType outlines. 15 | //Under Windows, the usWinAscent and usWinDescent values from the 'OS/2' table 16 | //will be used to determine the maximum black height for a font at any given size. 17 | //Windows calls this distance the Font Height. 18 | //Because TrueType instructions can lead to Font Heights that differ from the actual scaled and rounded values, 19 | //basing the Font Height strictly on the yMax and yMin can result in “lost pixels.” 20 | //Windows will clip any pixels that extend above the yMax or below the yMin. 21 | //In order to avoid grid fitting the entire font to determine the correct height, the VDMX table has been defined. 22 | 23 | //The VDMX table consists of a header followed by groupings of VDMX records: 24 | Ratio[] _ratios; 25 | protected override void ReadContentFrom(BinaryReader reader) 26 | { 27 | //uint16 version Version number (0 or 1). 28 | //uint16 numRecs Number of VDMX groups present 29 | //uint16 numRatios Number of aspect ratio groupings 30 | //RatioRange ratRange[numRatios] Ratio ranges (see below for more info) 31 | //Offset16 offset[numRatios] Offset from start of this table to the VDMX group for this ratio range. 32 | //--- 33 | //RatioRange Record: 34 | //Type Name Description 35 | //uint8 bCharSet Character set (see below). 36 | //uint8 xRatio Value to use for x-Ratio 37 | //uint8 yStartRatio Starting y-Ratio value. 38 | //uint8 yEndRatio Ending y-Ratio value. 39 | ushort version = reader.ReadUInt16(); 40 | ushort numRecs = reader.ReadUInt16(); 41 | ushort numRatios = reader.ReadUInt16(); 42 | _ratios = new Ratio[numRatios]; 43 | for (int i = 0; i < numRatios; ++i) 44 | { 45 | _ratios[i] = new Ratio( 46 | reader.ReadByte(), 47 | reader.ReadByte(), 48 | reader.ReadByte(), 49 | reader.ReadByte()); 50 | } 51 | ushort[] offsets = Utils.ReadUInt16Array(reader, numRatios); 52 | //------ 53 | //actual vdmx group 54 | //TODO: implement this 55 | } 56 | readonly struct Ratio 57 | { 58 | public readonly byte charset; 59 | public readonly byte xRatio; 60 | public readonly byte yStartRatio; 61 | public readonly byte yEndRatio; 62 | public Ratio(byte charset, byte xRatio, byte yStartRatio, byte yEndRatio) 63 | { 64 | this.charset = charset; 65 | this.xRatio = xRatio; 66 | this.yStartRatio = yStartRatio; 67 | this.yEndRatio = yEndRatio; 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /OpenFont/Tables.TrueType/GlyphLocations.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2018-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | //https://www.microsoft.com/typography/otspec/loca.htm 4 | 5 | using System.IO; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | class GlyphLocations : TableEntry 9 | { 10 | public const string _N = "loca"; 11 | public override string Name => _N; 12 | 13 | 14 | // loca - Index to Location 15 | 16 | //The indexToLoc table stores the offsets to the locations of the glyphs in the font, 17 | //relative to the beginning of the glyphData table. 18 | //In order to compute the length of the last glyph element, 19 | //there is an extra entry after the last valid index. 20 | 21 | //By definition, 22 | //index zero points to the “missing character,” 23 | //which is the character that appears if a character is not found in the font. 24 | //The missing character is commonly represented by a blank box or a space. 25 | //If the font does not contain an outline for the missing character, 26 | //then the first and second offsets should have the same value. 27 | //This also applies to any other characters without an outline, such as the space character. 28 | //If a glyph has no outline, then loca[n] = loca [n+1]. 29 | //In the particular case of the last glyph(s), loca[n] will be equal the length of the glyph data ('glyf') table. 30 | //The offsets must be in ascending order with loca[n] <= loca[n+1]. 31 | 32 | //Most routines will look at the 'maxp' table to determine the number of glyphs in the font, but the value in the 'loca' table must agree. 33 | 34 | //There are two versions of this table, the short and the long. The version is specified in the indexToLocFormat entry in the 'head' table. 35 | 36 | uint[] _offsets; 37 | public GlyphLocations(int glyphCount, bool isLongVersion) 38 | { 39 | _offsets = new uint[glyphCount + 1]; 40 | this.IsLongVersion = isLongVersion; 41 | } 42 | public bool IsLongVersion { get; private set; } 43 | public uint[] Offsets => _offsets; 44 | public int GlyphCount => _offsets.Length - 1; 45 | 46 | protected override void ReadContentFrom(BinaryReader reader) 47 | { 48 | //Short version 49 | //Type Name Description 50 | //USHORT offsets[n] The actual local offset divided by 2 is stored. 51 | //The value of n is numGlyphs + 1. 52 | //The value for numGlyphs is found in the 'maxp' table. 53 | //------------------------- 54 | //Long version 55 | //Type Name Description 56 | //ULONG offsets[n] The actual local offset is stored. 57 | //The value of n is numGlyphs + 1. The value for numGlyphs is found in the 'maxp' table. 58 | 59 | //Note that the local offsets should be long-aligned, i.e., multiples of 4. Offsets which are not long-aligned may seriously degrade performance of some processors. 60 | 61 | int glyphCount = GlyphCount; 62 | int lim = glyphCount + 1; 63 | _offsets = new uint[lim]; 64 | if (IsLongVersion) 65 | { 66 | //long version 67 | for (int i = 0; i < lim; i++) 68 | { 69 | _offsets[i] = reader.ReadUInt32(); 70 | } 71 | } 72 | else 73 | { 74 | //short version 75 | for (int i = 0; i < lim; i++) 76 | { 77 | _offsets[i] = (uint)(reader.ReadUInt16() << 1); // =*2 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /OpenFont/Tables/HorizontalHeader.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System; 5 | using System.IO; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | /// 9 | /// hhea 10 | /// 11 | class HorizontalHeader : TableEntry 12 | { 13 | public const string _N = "hhea"; 14 | public override string Name => _N; 15 | 16 | //https://docs.microsoft.com/en-us/typography/opentype/spec/hhea 17 | //hhea — Horizontal Header Table 18 | //----- 19 | // Type Name Description 20 | //uint16 majorVersion Major version number of the horizontal header table — set to 1. 21 | //uint16 minorVersion Minor version number of the horizontal header table — set to 0. 22 | //FWORD Ascender Typographic ascent(Distance from baseline of highest ascender). 23 | //FWORD Descender Typographic descent(Distance from baseline of lowest descender). 24 | //FWORD LineGap Typographic line gap. 25 | // Negative LineGap values are treated as zero in Windows 3.1, and in Mac OS System 6 and System 7. 26 | //UFWORD advanceWidthMax Maximum advance width value in 'hmtx' table. 27 | //FWORD minLeftSideBearing Minimum left sidebearing value in 'hmtx' table. 28 | //FWORD minRightSideBearing Minimum right sidebearing value; calculated as Min(aw - lsb - (xMax - xMin)). 29 | //FWORD xMaxExtent Max(lsb + (xMax - xMin)). 30 | //int16 caretSlopeRise Used to calculate the slope of the cursor(rise/run); 1 for vertical. 31 | //int16 caretSlopeRun 0 for vertical. 32 | //int16 caretOffset The amount by which a slanted highlight on a glyph needs to be shifted to produce the best appearance.Set to 0 for non-slanted fonts 33 | //int16 (reserved) set to 0 34 | //int16 (reserved) set to 0 35 | //int16 (reserved) set to 0 36 | //int16 (reserved) set to 0 37 | //int16 metricDataFormat 0 for current format. 38 | //uint16 numberOfHMetrics Number of hMetric entries in 'hmtx' table 39 | 40 | public HorizontalHeader() 41 | { 42 | } 43 | protected override void ReadContentFrom(BinaryReader input) 44 | { 45 | Version = input.ReadUInt32(); //major + minor 46 | Ascent = input.ReadInt16(); 47 | Descent = input.ReadInt16(); 48 | LineGap = input.ReadInt16(); 49 | 50 | AdvancedWidthMax = input.ReadUInt16(); 51 | MinLeftSideBearing = input.ReadInt16(); 52 | MinRightSideBearing = input.ReadInt16(); 53 | MaxXExtent = input.ReadInt16(); 54 | 55 | CaretSlopRise = input.ReadInt16(); 56 | CaretSlopRun = input.ReadInt16(); 57 | CaretOffset = input.ReadInt16(); 58 | 59 | //reserve 4 int16 fields, int16 x 4 fields 60 | input.BaseStream.Seek(2 * 4, SeekOrigin.Current); 61 | 62 | MetricDataFormat = input.ReadInt16(); // 0 63 | NumberOfHMetrics = input.ReadUInt16(); 64 | } 65 | public uint Version { get; private set; } 66 | public short Ascent { get; private set; } 67 | public short Descent { get; private set; } 68 | public short LineGap { get; private set; } 69 | public ushort AdvancedWidthMax { get; private set; } 70 | public short MinLeftSideBearing { get; private set; } 71 | public short MinRightSideBearing { get; private set; } 72 | public short MaxXExtent { get; private set; } 73 | public short CaretSlopRise { get; private set; } 74 | public short CaretSlopRun { get; private set; } 75 | public short CaretOffset { get; private set; } 76 | public short MetricDataFormat { get; private set; } 77 | public ushort NumberOfHMetrics { get; private set; } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Schwabra/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using HtmlAgilityPack; 9 | 10 | namespace Schwabra 11 | { 12 | public class Program 13 | { 14 | static int Main(string[] args) 15 | { 16 | if (args.Length != 1) 17 | { 18 | Console.WriteLine("use:"); 19 | Console.WriteLine(" dotnet run "); 20 | Console.WriteLine(" dotnet run "); 21 | 22 | return -1; 23 | } 24 | 25 | var inputArg = args[0]; 26 | using var dbContext = new ElectionContext(); 27 | var processedStations = new ConcurrentBag(); 28 | if (Directory.Exists(inputArg)) 29 | { 30 | // batch processing 31 | var directory = Directory.GetFiles(inputArg); 32 | Parallel.For(0, directory.Length, i => 33 | { 34 | var file = directory[i]; 35 | try 36 | { 37 | Console.WriteLine($"Processing {file} ({i} from {directory.Length})"); 38 | processedStations.Add(Extract(file)); 39 | } 40 | catch (Exception) 41 | { 42 | Console.Error.WriteLine($"Error processing file: {file}"); 43 | throw; 44 | } 45 | }); 46 | dbContext.station.AddRange(processedStations); 47 | } 48 | else 49 | { 50 | dbContext.Add(Extract(inputArg)); 51 | } 52 | 53 | Console.WriteLine("Saving changes to sqlite..."); 54 | dbContext.SaveChanges(); 55 | 56 | 57 | return 0; 58 | } 59 | 60 | private static Station Extract(string inputPath) 61 | { 62 | var inputText = File.ReadAllText(inputPath); 63 | var doc = new HtmlDocument(); 64 | doc.LoadHtml(inputText); 65 | 66 | string name = ExtractName(doc); 67 | string path = ExtractPath(doc); 68 | var rows = ExtractRows(doc).ToArray(); 69 | var station = new Station() 70 | { 71 | filename = inputPath, 72 | name = name, 73 | path = path 74 | }; 75 | station.rows.AddRange(rows); 76 | 77 | return station; 78 | } 79 | 80 | public static string ExtractName(HtmlDocument doc) 81 | { 82 | foreach (var td in doc.DocumentNode.SelectNodes("//td") ?? Enumerable.Empty()) 83 | { 84 | if (td.InnerText.Contains("Наименование избирательной комиссии")) 85 | { 86 | return td.ParentNode.Elements("td").Last().InnerText; 87 | } 88 | } 89 | 90 | return null; 91 | } 92 | 93 | public static string ExtractPath(HtmlDocument doc) 94 | { 95 | var result = new List(); 96 | foreach (var node in doc.DocumentNode.SelectSingleNode("//ul[@class='breadcrumb']")?.SelectNodes("li") ?? Enumerable.Empty()) 97 | { 98 | var val = node.InnerText.Trim(); 99 | if (val != "menu") 100 | result.Add(val); 101 | } 102 | 103 | return string.Join(";", result); 104 | } 105 | 106 | public static IEnumerable ExtractRows(HtmlDocument doc) 107 | { 108 | var mainTable = doc.DocumentNode.SelectSingleNode("//table[contains(@class, 'table-sm')]"); 109 | foreach (var tr in mainTable?.SelectNodes("tr") ?? Enumerable.Empty()) 110 | { 111 | if (tr.InnerText.Trim() == "") continue; 112 | var tds = tr.Elements("td").ToArray(); 113 | var numNode = tds[0]; 114 | var titleNode = tds[1]; 115 | var valNode = tds[2]; 116 | 117 | var num = int.Parse(numNode.InnerText); 118 | var title = titleNode.InnerText; 119 | double? percent = null; 120 | if (!int.TryParse(valNode.InnerText, out var value)) 121 | { 122 | var nums = valNode.InnerText.Split(new []{' ', '\r', '\n'}, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).ToArray(); 123 | value = int.Parse(nums[0]); 124 | percent = double.Parse(nums[1].TrimEnd('%'), CultureInfo.InvariantCulture); 125 | } 126 | 127 | yield return new Result(){ num = num, title = title, value = value, value_percent = percent}; 128 | } 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /OpenFont/Tables/MaxProfile.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | 5 | using System.IO; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | //https://docs.microsoft.com/en-us/typography/opentype/spec/maxp 9 | class MaxProfile : TableEntry 10 | { 11 | public const string _N = "maxp"; 12 | public override string Name => _N; 13 | 14 | //This table establishes the memory requirements for this font. 15 | //Fonts with CFF data must use Version 0.5 of this table, 16 | //specifying only the numGlyphs field. 17 | 18 | //Fonts with TrueType outlines must use Version 1.0 of this table, where all data is required. 19 | 20 | //Version 0.5 21 | //Type Name Description 22 | //Fixed version 0x00005000 for version 0.5 23 | // (Note the difference in the representation of a non-zero fractional part, in Fixed numbers.) 24 | //uint16 numGlyphs The number of glyphs in the font. 25 | 26 | //Version 1.0 27 | //Type Name Description 28 | //Fixed version 0x00010000 for version 1.0. 29 | //uint16 numGlyphs The number of glyphs in the font. 30 | //uint16 maxPoints Maximum points in a non-composite glyph. 31 | //uint16 maxContours Maximum contours in a non-composite glyph. 32 | //uint16 maxCompositePoints Maximum points in a composite glyph. 33 | //uint16 maxCompositeContours Maximum contours in a composite glyph. 34 | //uint16 maxZones 1 if instructions do not use the twilight zone (Z0), or 2 if instructions do use Z0; should be set to 2 in most cases. 35 | //uint16 maxTwilightPoints Maximum points used in Z0. 36 | //uint16 maxStorage Number of Storage Area locations. 37 | //uint16 maxFunctionDefs Number of FDEFs, equal to the highest function number + 1. 38 | //uint16 maxInstructionDefs Number of IDEFs. 39 | //uint16 maxStackElements Maximum stack depth across Font Program ('fpgm' table), CVT Program('prep' table) and all glyph instructions(in the 'glyf' table). 40 | //uint16 maxSizeOfInstructions Maximum byte count for glyph instructions. 41 | //uint16 maxComponentElements Maximum number of components referenced at “top level” for any composite glyph. 42 | //uint16 maxComponentDepth Maximum levels of recursion; 1 for simple components. 43 | 44 | public uint Version { get; private set; } 45 | public ushort GlyphCount { get; private set; } 46 | public ushort MaxPointsPerGlyph { get; private set; } 47 | public ushort MaxContoursPerGlyph { get; private set; } 48 | public ushort MaxPointsPerCompositeGlyph { get; private set; } 49 | public ushort MaxContoursPerCompositeGlyph { get; private set; } 50 | public ushort MaxZones { get; private set; } 51 | public ushort MaxTwilightPoints { get; private set; } 52 | public ushort MaxStorage { get; private set; } 53 | public ushort MaxFunctionDefs { get; private set; } 54 | public ushort MaxInstructionDefs { get; private set; } 55 | public ushort MaxStackElements { get; private set; } 56 | public ushort MaxSizeOfInstructions { get; private set; } 57 | public ushort MaxComponentElements { get; private set; } 58 | public ushort MaxComponentDepth { get; private set; } 59 | 60 | protected override void ReadContentFrom(BinaryReader input) 61 | { 62 | Version = input.ReadUInt32(); // 0x00010000 == 1.0 63 | GlyphCount = input.ReadUInt16(); 64 | MaxPointsPerGlyph = input.ReadUInt16(); 65 | MaxContoursPerGlyph = input.ReadUInt16(); 66 | MaxPointsPerCompositeGlyph = input.ReadUInt16(); 67 | MaxContoursPerCompositeGlyph = input.ReadUInt16(); 68 | MaxZones = input.ReadUInt16(); 69 | MaxTwilightPoints = input.ReadUInt16(); 70 | MaxStorage = input.ReadUInt16(); 71 | MaxFunctionDefs = input.ReadUInt16(); 72 | MaxInstructionDefs = input.ReadUInt16(); 73 | MaxStackElements = input.ReadUInt16(); 74 | MaxSizeOfInstructions = input.ReadUInt16(); 75 | MaxComponentElements = input.ReadUInt16(); 76 | MaxComponentDepth = input.ReadUInt16(); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /OpenFont/Tables/HorizontalMetrics.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | namespace Typography.OpenFont.Tables 8 | { 9 | /// 10 | /// hmtx 11 | /// 12 | class HorizontalMetrics : TableEntry 13 | { 14 | public const string _N = "hmtx"; 15 | public override string Name => _N; 16 | // 17 | //https://docs.microsoft.com/en-us/typography/opentype/spec/hmtx 18 | // A font rendering engine must use the advanceWidths in the hmtx table for the advances of a CFF OFF font, 19 | //even though the CFF table specifies its own glyph widths.//*** 20 | 21 | //Note that fonts in a Font Collection which share a CFF table may specify different advanceWidths in their hmtx table for a particular glyph index. 22 | //For any glyph, xmax and xmin are given in 'glyf' table, lsb and aw are given in 'hmtx' table. rsb is calculated as follows: 23 | // rsb = aw - (lsb + xmax - xmin) 24 | //If pp1 and pp2 are phantom points used to control lsb and rsb, their initial position in x is calculated as follows: 25 | // pp1 = xmin - lsb 26 | // pp2 = pp1 + aw 27 | 28 | 29 | //NOTE: 30 | //lsb=> left-side bearing 31 | //rsb=> right-side bearing 32 | //aw=> advance width 33 | 34 | readonly ushort[] _advanceWidths; //in font design unit 35 | readonly short[] _leftSideBearings;//lsb, in font design unit 36 | readonly int _numOfHMetrics; 37 | readonly int _numGlyphs; 38 | public HorizontalMetrics(ushort numOfHMetrics, ushort numGlyphs) 39 | { 40 | //The value numOfHMetrics comes from the 'hhea' table** 41 | _advanceWidths = new ushort[numGlyphs]; 42 | _leftSideBearings = new short[numGlyphs]; 43 | _numOfHMetrics = numOfHMetrics; 44 | _numGlyphs = numGlyphs; 45 | #if DEBUG 46 | if (numGlyphs < numOfHMetrics) 47 | { 48 | throw new NotSupportedException(); 49 | } 50 | #endif 51 | } 52 | 53 | public ushort GetAdvanceWidth(ushort glyphIndex) => _advanceWidths[glyphIndex]; 54 | 55 | public short GetLeftSideBearing(ushort glyphIndex) => _leftSideBearings[glyphIndex]; 56 | 57 | public void GetHMetric(ushort glyphIndex, out ushort advWidth, out short lsb) 58 | { 59 | advWidth = _advanceWidths[glyphIndex]; 60 | lsb = _leftSideBearings[glyphIndex]; 61 | //TODO: calculate other value? 62 | } 63 | protected override void ReadContentFrom(BinaryReader input) 64 | { 65 | //=============================================================================== 66 | //1. hMetrics : have both advance width and leftSideBearing(lsb) 67 | //Paired advance width and left side bearing values for each glyph. 68 | //The value numOfHMetrics comes from the 'hhea' table** 69 | //If the font is monospaced, only one entry need be in the array, 70 | //but that entry is required. The last entry applies to all subsequent glyphs 71 | 72 | int gid = 0; //gid=> glyphIndex 73 | 74 | int numOfHMetrics = _numOfHMetrics; 75 | for (int i = 0; i < numOfHMetrics; i++) 76 | { 77 | _advanceWidths[gid] = input.ReadUInt16(); 78 | _leftSideBearings[gid] = input.ReadInt16(); 79 | 80 | gid++;//*** 81 | } 82 | 83 | //=============================================================================== 84 | //2. (only) LeftSideBearing: (same advanced width (eg. monospace font), vary only left side bearing) 85 | //Here the advanceWidth is assumed to be the same as the advanceWidth for the last entry above. 86 | //The number of entries in this array is derived from numGlyphs (from 'maxp' table) minus numberOfHMetrics. 87 | 88 | int nEntries = _numGlyphs - numOfHMetrics; 89 | ushort advanceWidth = _advanceWidths[numOfHMetrics - 1]; 90 | 91 | for (int i = 0; i < nEntries; i++) 92 | { 93 | _advanceWidths[gid] = advanceWidth; 94 | _leftSideBearings[gid] = input.ReadInt16(); 95 | 96 | gid++;//*** 97 | } 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/CBDT.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | using Typography.OpenFont.Tables.BitmapFonts; 5 | 6 | namespace Typography.OpenFont.Tables 7 | { 8 | //test font=> NotoColorEmoji.ttf 9 | 10 | //from https://docs.microsoft.com/en-us/typography/opentype/spec/cbdt 11 | 12 | //Table structure 13 | 14 | //The CBDT table is used to embed color bitmap glyph data. It is used together with the CBLC table, 15 | //which provides embedded bitmap locators. 16 | //The formats of these two tables are backward compatible with the EBDT and EBLC tables 17 | //used for embedded monochrome and grayscale bitmaps. 18 | 19 | //The CBDT table begins with a header containing simply the table version number. 20 | //Type Name Description 21 | //uint16 majorVersion Major version of the CBDT table, = 3. 22 | //uint16 minorVersion Minor version of the CBDT table, = 0. 23 | 24 | //Note that the first version of the CBDT table is 3.0. 25 | 26 | //The rest of the CBDT table is a collection of bitmap data. 27 | //The data can be presented in three possible formats, 28 | //indicated by information in the CBLC table. 29 | //Some of the formats contain metric information plus image data, 30 | //and other formats contain only the image data. Long word alignment is not required for these subtables; 31 | //byte alignment is sufficient. 32 | 33 | class CBDT : TableEntry, IDisposable 34 | { 35 | public const string _N = "CBDT"; 36 | public override string Name => _N; 37 | 38 | readonly GlyphBitmapDataFmt17 _format17 = new GlyphBitmapDataFmt17(); 39 | readonly GlyphBitmapDataFmt18 _format18 = new GlyphBitmapDataFmt18(); 40 | readonly GlyphBitmapDataFmt19 _format19 = new GlyphBitmapDataFmt19(); 41 | 42 | 43 | System.IO.MemoryStream _ms; //sub-stream contains image data 44 | Typography.OpenFont.ByteOrderSwappingBinaryReader _binReader; 45 | 46 | public void Dispose() 47 | { 48 | RemoveOldMemoryStreamAndReaders(); 49 | } 50 | 51 | public void RemoveOldMemoryStreamAndReaders() 52 | { 53 | try 54 | { 55 | if (_binReader != null) 56 | { 57 | ((System.IDisposable)_binReader).Dispose(); 58 | _binReader = null; 59 | } 60 | if (_ms != null) 61 | { 62 | _ms.Dispose(); 63 | _ms = null; 64 | } 65 | } 66 | catch (Exception ex) 67 | { 68 | // 69 | } 70 | } 71 | protected override void ReadContentFrom(BinaryReader reader) 72 | { 73 | 74 | //we copy data from the input mem stream 75 | //and store inside this table for later use. 76 | RemoveOldMemoryStreamAndReaders(); 77 | 78 | //------------------- 79 | byte[] data = reader.ReadBytes((int)this.Header.Length);//*** 80 | _ms = new MemoryStream(data); 81 | _binReader = new ByteOrderSwappingBinaryReader(_ms); 82 | } 83 | public void FillGlyphInfo(Glyph glyph) 84 | { 85 | //int srcOffset, int srcLen, int srcFormat, 86 | _binReader.BaseStream.Position = glyph.BitmapStreamOffset; 87 | switch (glyph.BitmapFormat) 88 | { 89 | case 17: _format17.FillGlyphInfo(_binReader, glyph); break; 90 | case 18: _format18.FillGlyphInfo(_binReader, glyph); break; 91 | case 19: _format19.FillGlyphInfo(_binReader, glyph); break; 92 | default: 93 | throw new NotSupportedException(); 94 | } 95 | } 96 | public void CopyBitmapContent(Glyph glyph, System.IO.Stream outputStream) 97 | { 98 | //1 99 | _binReader.BaseStream.Position = glyph.BitmapStreamOffset; 100 | switch (glyph.BitmapFormat) 101 | { 102 | case 17: _format17.ReadRawBitmap(_binReader, glyph, outputStream); break; 103 | case 18: _format18.ReadRawBitmap(_binReader, glyph, outputStream); break; 104 | case 19: _format19.ReadRawBitmap(_binReader, glyph, outputStream); break; 105 | default: 106 | throw new NotSupportedException(); 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /OpenFont/Tables/Utils.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System; 5 | using System.Text; 6 | using System.IO; 7 | 8 | namespace Typography.OpenFont 9 | { 10 | static class Utils 11 | { 12 | /// 13 | /// read float, 2.14 format 14 | /// 15 | /// 16 | /// 17 | public static float ReadF2Dot14(this BinaryReader reader) 18 | { 19 | return ((float)reader.ReadInt16()) / (1 << 14); /* Format 2.14 */ 20 | } 21 | 22 | public static Bounds ReadBounds(BinaryReader input) 23 | { 24 | return new Bounds( 25 | input.ReadInt16(),//xmin 26 | input.ReadInt16(), //ymin 27 | input.ReadInt16(), //xmax 28 | input.ReadInt16());//ymax 29 | } 30 | 31 | public static string TagToString(uint tag) 32 | { 33 | byte[] bytes = BitConverter.GetBytes(tag); 34 | Array.Reverse(bytes); 35 | return Encoding.UTF8.GetString(bytes, 0, bytes.Length); 36 | } 37 | 38 | public static int ReadUInt24(this BinaryReader reader) 39 | { 40 | byte highByte = reader.ReadByte(); 41 | return (highByte << 16) | reader.ReadUInt16(); 42 | } 43 | /// 44 | /// 16.16 float format 45 | /// 46 | /// 47 | /// 48 | public static float ReadFixed(this BinaryReader reader) 49 | { 50 | //16.16 format 51 | return (float)reader.ReadUInt32() / (1 << 16); 52 | } 53 | 54 | public static ushort[] ReadUInt16Array(this BinaryReader reader, int nRecords) 55 | { 56 | ushort[] arr = new ushort[nRecords]; 57 | for (int i = 0; i < arr.Length; ++i) 58 | { 59 | arr[i] = reader.ReadUInt16(); 60 | } 61 | return arr; 62 | } 63 | public static uint[] ReadUInt16ArrayAsUInt32Array(this BinaryReader reader, int nRecords) 64 | { 65 | uint[] arr = new uint[nRecords]; 66 | for (int i = 0; i < arr.Length; ++i) 67 | { 68 | arr[i] = reader.ReadUInt16(); 69 | } 70 | return arr; 71 | } 72 | public static uint[] ReadUInt32Array(this BinaryReader reader, int nRecords) 73 | { 74 | uint[] arr = new uint[nRecords]; 75 | for (int i = 0; i < arr.Length; ++i) 76 | { 77 | arr[i] = reader.ReadUInt32(); 78 | } 79 | return arr; 80 | } 81 | 82 | public static T[] CloneArray(T[] original, int newArrLenExtend = 0) 83 | { 84 | int orgLen = original.Length; 85 | T[] newClone = new T[orgLen + newArrLenExtend]; 86 | Array.Copy(original, newClone, orgLen); 87 | return newClone; 88 | } 89 | 90 | public static T[] ConcatArray(T[] arr1, T[] arr2) 91 | { 92 | T[] newArr = new T[arr1.Length + arr2.Length]; 93 | Array.Copy(arr1, 0, newArr, 0, arr1.Length); 94 | Array.Copy(arr2, 0, newArr, arr1.Length, arr2.Length); 95 | return newArr; 96 | } 97 | 98 | public static void WarnUnimplemented(string format, params object[] args) 99 | { 100 | #if DEBUG 101 | System.Diagnostics.Debug.WriteLine("!STUB! " + string.Format(format, args)); 102 | #endif 103 | } 104 | 105 | internal static void WarnUnimplementedCollectAssocGlyphs(string msg) 106 | { 107 | #if DEBUG 108 | System.Diagnostics.Debug.WriteLine("!STUB! UnimplementedCollectAssocGlyph :" + msg); 109 | #endif 110 | } 111 | #if DEBUG 112 | public static bool dbugIsDiff(GlyphPointF[] set1, GlyphPointF[] set2) 113 | { 114 | int j = set1.Length; 115 | if (j != set2.Length) 116 | { 117 | //yes, diff 118 | return true; 119 | } 120 | for (int i = j - 1; i >= 0; --i) 121 | { 122 | if (!set1[i].dbugIsEqualsWith(set2[i])) 123 | { 124 | //yes, diff 125 | return true; 126 | } 127 | } 128 | //no, both are the same 129 | return false; 130 | } 131 | #endif 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /OpenFont/Tables.CFF/CFFTable.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2018, Apache/PDFBox Authors ( https://github.com/apache/pdfbox) 2 | // 3 | // 4 | //Apache PDFBox 5 | //Copyright 2014 The Apache Software Foundation 6 | 7 | //This product includes software developed at 8 | //The Apache Software Foundation(http://www.apache.org/). 9 | 10 | //Based on source code originally developed in the PDFBox and 11 | //FontBox projects. 12 | 13 | //Copyright (c) 2002-2007, www.pdfbox.org 14 | 15 | //Based on source code originally developed in the PaDaF project. 16 | //Copyright (c) 2010 Atos Worldline SAS 17 | 18 | //Includes the Adobe Glyph List 19 | //Copyright 1997, 1998, 2002, 2007, 2010 Adobe Systems Incorporated. 20 | 21 | //Includes the Zapf Dingbats Glyph List 22 | //Copyright 2002, 2010 Adobe Systems Incorporated. 23 | 24 | //Includes OSXAdapter 25 | //Copyright (C) 2003-2007 Apple, Inc., All Rights Reserved 26 | 27 | //---------------- 28 | //ccf1 spec from http://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf 29 | 30 | //------------------------------------------------------------------ 31 | //Apache2, 2018-present, WinterDev 32 | 33 | 34 | using System; 35 | using System.Collections.Generic; 36 | using System.IO; 37 | using System.Text; 38 | 39 | using Typography.OpenFont.CFF; 40 | 41 | namespace Typography.OpenFont.Tables 42 | { 43 | 44 | //from https://docs.microsoft.com/en-us/typography/opentype/spec/cff 45 | //This table contains a Compact Font Format font representation (also known as a PostScript Type 1, or CIDFont) 46 | //and is structured according to 47 | //Adobe Technical Note #5176: “The Compact Font Format Specification,” (https://wwwimages2.adobe.com/content/dam/acom/en/devnet/font/pdfs/5176.CFF.pdf) 48 | //and 49 | //Adobe Technical Note #5177: “Type 2 Charstring Format.” (https://wwwimages2.adobe.com/content/dam/acom/en/devnet/font/pdfs/5177.Type2.pdf) 50 | 51 | 52 | //... 53 | // ... glyph data is accessed through the CharStrings INDEX of the CFF table. 54 | 55 | //... 56 | //The CFF Top DICT must specify a CharstringType value of 2. 57 | //The numGlyphs field in the 'maxp' table must be the same as the number of entries in the CFF's CharStrings INDEX. 58 | //The OpenType font glyph index is the same as the CFF glyph index for all glyphs in the font. 59 | 60 | //Note that, in an OpenType font collection file, a single 'CFF ' table can be shared across multiple fonts; 61 | //names used by applications must be those provided in the 'name' table, not the Name INDEX entry. 62 | //The CFF Top DICT must specify a CharstringType value of 2. 63 | //The numGlyphs field in the 'maxp' table must be the same as the number of entries in the CFF’s CharStrings INDEX. 64 | //The OpenType font glyph index is the same as the CFF glyph index for all glyphs in the font. 65 | 66 | class CFFTable : TableEntry 67 | { 68 | public const string _N = "CFF ";//4 chars, left 1 blank whitespace 69 | public override string Name => _N; 70 | // 71 | 72 | Cff1FontSet _cff1FontSet; 73 | // 74 | internal Cff1FontSet Cff1FontSet => _cff1FontSet; 75 | protected override void ReadContentFrom(BinaryReader reader) 76 | { 77 | long startAt = reader.BaseStream.Position; 78 | // 79 | // 80 | //Table 8 Header Format 81 | //Type Name Description 82 | //Card8 major Format major version(starting at 1) 83 | //Card8 minor Format minor version(starting at 0) 84 | //Card8 hdrSize Header size(bytes) 85 | //OffSize offSize Absolute offset(0) size 86 | byte[] header = reader.ReadBytes(4); 87 | byte major = header[0]; 88 | byte minor = header[1]; 89 | byte hdrSize = header[2]; 90 | byte offSize = header[3]; 91 | ////--------- 92 | //name index 93 | 94 | switch (major) 95 | { 96 | default: throw new NotSupportedException(); 97 | case 1: 98 | { 99 | Cff1Parser cff1 = new Cff1Parser(); 100 | cff1.ParseAfterHeader(startAt, reader); 101 | _cff1FontSet = cff1.ResultCff1FontSet; 102 | } 103 | break; 104 | case 2: 105 | { 106 | Cff2Parser cff2 = new Cff2Parser(); 107 | cff2.ParseAfterHeader(reader); 108 | } 109 | break; 110 | } 111 | } 112 | 113 | } 114 | 115 | 116 | } -------------------------------------------------------------------------------- /OpenFont/Tables.Others/VerticalMetrics.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | 3 | using System; 4 | using System.IO; 5 | namespace Typography.OpenFont.Tables 6 | { 7 | /// 8 | /// vertical metrics table 9 | /// 10 | class VerticalMetrics : TableEntry 11 | { 12 | public const string _N = "vmtx"; 13 | public override string Name => _N; 14 | 15 | // https://docs.microsoft.com/en-us/typography/opentype/spec/vmtx 16 | // vmtx - Vertical Metrics Table 17 | 18 | //The vertical metrics table allows you to specify the vertical spacing for each glyph in a vertical font. 19 | //This table consists of either one or two arrays that contain metric information(the advance heights and top sidebearings) 20 | //for the vertical layout of each of the glyphs in the font. 21 | //The vertical metrics coordinate system is shown below. 22 | 23 | 24 | //Vertical Metrics Table Format 25 | 26 | //The overall structure of the vertical metrics table consists of two arrays shown below: 27 | //the vMetrics array followed by an array of top side bearings. 28 | // 29 | //The top side bearing is measured relative to the top of the origin of glyphs, 30 | //for vertical composition of ideographic glyphs. 31 | // 32 | //This table does not have a header, 33 | //but does require that the number of glyphs included in the two arrays equals the total number of glyphs in the font. 34 | // 35 | //The number of entries in the vMetrics array is determined by the value of the numOfLongVerMetrics field of the vertical header table. 36 | // 37 | //The vMetrics array contains two values for each entry. 38 | //These are the advance height and the top sidebearing for each glyph included in the array. 39 | // 40 | //In monospaced fonts, such as Courier or Kanji, all glyphs have the same advance height. 41 | //If the font is monospaced, only one entry need be in the first array, but that one entry is required. 42 | //The format of an entry in the vertical metrics array is given below. 43 | 44 | // 45 | //Type Name Description 46 | //uint16 advanceHeight The advance height of the glyph. Unsigned integer in FUnits 47 | //int16 topSideBearing The top sidebearing of the glyph. Signed integer in FUnits. 48 | 49 | //The second array is optional and generally is used for a run of monospaced glyphs in the font. 50 | //Only one such run is allowed per font, and it must be located at the end of the font. 51 | //This array contains the top sidebearings of glyphs not represented in the first array, 52 | //and all the glyphs in this array must have the same advance height as the last entry in the vMetrics array. 53 | //All entries in this array are therefore monospaced. 54 | // 55 | //The number of entries in this array is calculated by subtracting the value of numOfLongVerMetrics from the number of glyphs in the font. 56 | //The sum of glyphs represented in the first array plus the glyphs represented in the second array therefore equals the number of glyphs in the font. 57 | //The format of the top sidebearing array is given below. 58 | //Type Name Description 59 | // int16 topSideBearing[] The top sidebearing of the glyph. Signed integer in FUnits. 60 | 61 | ushort _numOfLongVerMetrics; 62 | AdvanceHeightAndTopSideBearing[] _advHeightAndTopSideBearings; 63 | public VerticalMetrics(ushort numOfLongVerMetrics) 64 | { 65 | _numOfLongVerMetrics = numOfLongVerMetrics; 66 | } 67 | protected override void ReadContentFrom(BinaryReader reader) 68 | { 69 | _advHeightAndTopSideBearings = new AdvanceHeightAndTopSideBearing[_numOfLongVerMetrics]; 70 | int m = 0; 71 | for (int i = _numOfLongVerMetrics - 1; i >= 0; --i) 72 | { 73 | _advHeightAndTopSideBearings[m] = new AdvanceHeightAndTopSideBearing( 74 | reader.ReadUInt16(), 75 | reader.ReadInt16() 76 | ); 77 | } 78 | } 79 | 80 | public readonly struct AdvanceHeightAndTopSideBearing 81 | { 82 | public readonly ushort advanceHeight; 83 | public readonly short topSideBearing; 84 | public AdvanceHeightAndTopSideBearing(ushort advanceHeight, short topSideBearing) 85 | { 86 | this.advanceHeight = advanceHeight; 87 | this.topSideBearing = topSideBearing; 88 | } 89 | #if DEBUG 90 | public override string ToString() 91 | { 92 | return advanceHeight + "," + topSideBearing; 93 | } 94 | #endif 95 | } 96 | 97 | } 98 | } -------------------------------------------------------------------------------- /OpenFont/Tables.TrueType/Gasp.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | using System; 3 | using System.IO; 4 | namespace Typography.OpenFont.Tables 5 | { 6 | /// 7 | /// Grid-fitting And Scan-conversion Procedure Table 8 | /// 9 | class Gasp : TableEntry 10 | { 11 | public const string _N = "gasp"; 12 | public override string Name => _N; 13 | // 14 | //https://docs.microsoft.com/en-us/typography/opentype/spec/gasp 15 | 16 | 17 | // This table contains information which describes the preferred rasterization techniques 18 | //for the typeface when it is rendered on grayscale-capable devices. 19 | //This table also has some use for monochrome devices, 20 | //which may use the table to turn off hinting at very large or small sizes, to improve performance. 21 | 22 | //At very small sizes, 23 | //the best appearance on grayscale devices can usually be achieved by rendering the glyphs 24 | //in grayscale without using hints. 25 | // 26 | //At intermediate sizes, hinting and monochrome rendering will usually produce the best appearance. 27 | // 28 | //At large sizes, the combination of hinting and grayscale rendering will 29 | //typically produce the best appearance. 30 | 31 | //If the 'gasp' table is not present in a typeface, 32 | //the rasterizer may apply default rules to decide how to render the glyphs on grayscale devices. 33 | 34 | //The 'gasp' table consists of a header followed by groupings of 'gasp' records: 35 | GaspRangeRecord[] _rangeRecords; 36 | protected override void ReadContentFrom(BinaryReader reader) 37 | { 38 | 39 | //Type Name Description 40 | //USHORT version Version number (set to 1) 41 | //USHORT numRanges Number of records to follow 42 | //GASPRANGE gaspRange[numRanges] Sorted by ppem 43 | 44 | //Each GASPRANGE record looks like this: 45 | //Type Name Description 46 | //USHORT rangeMaxPPEM Upper limit of range, in PPEM 47 | //USHORT rangeGaspBehavior Flags describing desired rasterizer behavior. 48 | ushort version = reader.ReadUInt16(); 49 | ushort numRanges = reader.ReadUInt16(); 50 | _rangeRecords = new GaspRangeRecord[numRanges]; 51 | for (int i = 0; i < numRanges; ++i) 52 | { 53 | _rangeRecords[i] = new GaspRangeRecord( 54 | reader.ReadUInt16(), 55 | (GaspRangeBehavior)reader.ReadUInt16()); 56 | } 57 | } 58 | 59 | [Flags] 60 | enum GaspRangeBehavior : ushort 61 | { 62 | Neither = 0, 63 | GASP_DOGRAY = 0x0002, 64 | GASP_GRIDFIT = 0x0001, 65 | GASP_DOGRAY_GASP_GRIDFIT = 0x0003, 66 | GASP_SYMMETRIC_GRIDFIT = 0x0004, 67 | GASP_SYMMETRIC_SMOOTHING = 0x0008, 68 | GASP_SYMMETRIC_SMOOTHING_GASP_SYMMETRIC_GRIDFIT = 0x000C 69 | } 70 | readonly struct GaspRangeRecord 71 | { 72 | public readonly ushort rangeMaxPPEM; 73 | public readonly GaspRangeBehavior rangeGaspBehavior; 74 | public GaspRangeRecord(ushort rangeMaxPPEM, GaspRangeBehavior rangeGaspBehavior) 75 | { 76 | this.rangeMaxPPEM = rangeMaxPPEM; 77 | this.rangeGaspBehavior = rangeGaspBehavior; 78 | } 79 | 80 | // There are four flags for the rangeGaspBehavior flags: 81 | //Flag Meaning 82 | //GASP_DOGRAY Use grayscale rendering 83 | //GASP_GRIDFIT Use gridfitting 84 | //GASP_SYMMETRIC_SMOOTHING Use smoothing along multiple axes with ClearType® 85 | //Only supported in version 1 gasp 86 | //GASP_SYMMETRIC_GRIDFIT Use gridfitting with ClearType symmetric smoothing 87 | //Only supported in version 1 gasp 88 | 89 | //The set of bit flags may be extended in the future. 90 | //The first two bit flags operate independently of the following two bit flags. 91 | //If font smoothing is enabled, then the first two bit flags are used. 92 | //If ClearType is enabled, then the following two bit flags are used. The seven currently defined values of rangeGaspBehavior would have the following uses: 93 | //Flag Value Meaning 94 | 95 | //GASP_DOGRAY 0x0002 small sizes, typically ppem<9 96 | //GASP_GRIDFIT 0x0001 medium sizes, typically 9<=ppem<=16 97 | //GASP_DOGRAY|GASP_GRIDFIT 0x0003 large sizes, typically ppem>16 98 | //(neither) 0x0000 optional for very large sizes, typically ppem>2048 99 | //GASP_SYMMETRIC_GRIDFIT 0x0004 typically always enabled 100 | //GASP_SYMMETRIC_SMOOTHING 0x0008 larger screen sizes, typically ppem>15, most commonly used with the gridfit flag. 101 | //GASP_SYMMETRIC_SMOOTHING| GASP_SYMMETRIC_GRIDFIT 0x000C larger screen sizes, typically ppem>15 102 | //neither 0x0000 optional for very large sizes, typically ppem>2048 103 | } 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/CPAL.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present Sam Hocevar , WinterDev 2 | 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | public class CPAL : TableEntry 8 | { 9 | public const string _N = "CPAL"; 10 | public override string Name => _N; 11 | // 12 | 13 | byte[] _colorBGRABuffer; 14 | 15 | // Palette Table Header 16 | // Read the CPAL table 17 | // https://docs.microsoft.com/en-us/typography/opentype/spec/cpal 18 | protected override void ReadContentFrom(BinaryReader reader) 19 | { 20 | long beginAt = reader.BaseStream.Position; 21 | 22 | //The CPAL table begins with a header that starts with a version number. 23 | //Currently, only versions 0 and 1 are defined. 24 | 25 | //CPAL version 0 26 | 27 | //The CPAL header version 0 is organized as follows: 28 | //CPAL version 0 29 | //Type Name Description 30 | //uint16 version Table version number (=0). 31 | //uint16 numPaletteEntries Number of palette entries in each palette. 32 | //uint16 numPalettes Number of palettes in the table. 33 | //uint16 numColorRecords Total number of color records, combined for all palettes. 34 | //Offset32 offsetFirstColorRecord Offset from the beginning of CPAL table to the first ColorRecord. 35 | //uint16 colorRecordIndices[numPalettes] Index of each palette’s first color record in the combined color record array. 36 | 37 | //CPAL version 1 38 | 39 | //The CPAL header version 1 adds three additional fields to the end of the table header and is organized as follows: 40 | //CPAL version 1 41 | //Type Name Description 42 | //uint16 version Table version number (=1). 43 | //uint16 numPaletteEntries Number of palette entries in each palette. 44 | //uint16 numPalettes Number of palettes in the table. 45 | //uint16 numColorRecords Total number of color records, combined for all palettes. 46 | //Offset32 offsetFirstColorRecord Offset from the beginning of CPAL table to the first ColorRecord. 47 | //uint16 colorRecordIndices[numPalettes] Index of each palette’s first color record in the combined color record array. 48 | //Offset32 offsetPaletteTypeArray Offset from the beginning of CPAL table to the Palette Type Array. Set to 0 if no array is provided. 49 | //Offset32 offsetPaletteLabelArray Offset from the beginning of CPAL table to the Palette Labels Array. Set to 0 if no array is provided. 50 | //Offset32 offsetPaletteEntryLabelArray Offset from the beginning of CPAL table to the Palette Entry Label Array. Set to 0 if no array is provided. 51 | 52 | ushort version = reader.ReadUInt16(); 53 | ushort numPaletteEntries = reader.ReadUInt16(); // XXX: unused? 54 | ushort numPalettes = reader.ReadUInt16(); 55 | ColorCount = reader.ReadUInt16(); //numColorRecords 56 | uint offsetFirstColorRecord = reader.ReadUInt32(); //Offset from the beginning of CPAL table to the first ColorRecord. 57 | Palettes = Utils.ReadUInt16Array(reader, numPalettes); //colorRecordIndices, Index of each palette’s first color record in the combined color record array. 58 | 59 | #if DEBUG 60 | if (version == 1) 61 | { 62 | //Offset32 offsetPaletteTypeArray Offset from the beginning of CPAL table to the Palette Type Array. Set to 0 if no array is provided. 63 | //Offset32 offsetPaletteLabelArray Offset from the beginning of CPAL table to the Palette Labels Array. Set to 0 if no array is provided. 64 | //Offset32 offsetPaletteEntryLabelArray Offset from the beginning of CPAL table to the Palette Entry Label Array. Set to 0 if no array is provided. 65 | } 66 | #endif 67 | 68 | //move to color records 69 | reader.BaseStream.Seek(beginAt + offsetFirstColorRecord, SeekOrigin.Begin); 70 | _colorBGRABuffer = reader.ReadBytes(4 * ColorCount); 71 | } 72 | 73 | public ushort[] Palettes { get; private set; } 74 | public ushort ColorCount { get; private set; } 75 | public void GetColor(int colorIndex, out byte r, out byte g, out byte b, out byte a) 76 | { 77 | //Each color record has BGRA values. The color space for these values is sRGB. 78 | //Type Name Description 79 | //uint8 blue Blue value(B0). 80 | //uint8 green Green value(B1). 81 | //uint8 red Red value(B2). 82 | //uint8 alpha Alpha value(B3). 83 | 84 | byte[] colorBGRABuffer = _colorBGRABuffer; 85 | int startAt = colorIndex * 4;//bgra 86 | b = colorBGRABuffer[startAt]; 87 | g = colorBGRABuffer[startAt + 1]; 88 | r = colorBGRABuffer[startAt + 2]; 89 | a = colorBGRABuffer[startAt + 3]; 90 | } 91 | } 92 | } 93 | 94 | -------------------------------------------------------------------------------- /Izbirkom21/FontReplacer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text.RegularExpressions; 8 | using System.Threading.Tasks; 9 | using ExCSS; 10 | using HtmlAgilityPack; 11 | using Typography.OpenFont; 12 | 13 | namespace Izbirkom21 14 | { 15 | public interface IFontProvider 16 | { 17 | ConcurrentDictionary Cache { get; } 18 | 19 | Task GetFont(string name, string directory); 20 | } 21 | 22 | public class CacheFontProvider : IFontProvider 23 | { 24 | public string FontsDirectory = Environment.GetEnvironmentVariable("IZ_CACHE") ?? Path.Combine(Directory.GetCurrentDirectory(), "fonts_cache"); 25 | private readonly IFontProvider _delegateTo; 26 | 27 | public CacheFontProvider(IFontProvider delegateTo) 28 | { 29 | _delegateTo = delegateTo; 30 | } 31 | 32 | public ConcurrentDictionary Cache { get; } = new ConcurrentDictionary(); 33 | 34 | public async Task GetFont(string name, string directory) 35 | { 36 | if (!Directory.Exists(FontsDirectory)) 37 | Directory.CreateDirectory(FontsDirectory); 38 | 39 | var path = Path.Combine(FontsDirectory, name); 40 | if (File.Exists(path)) 41 | return await File.ReadAllBytesAsync(path); 42 | 43 | var bytes = await _delegateTo.GetFont(name, directory); 44 | await File.WriteAllBytesAsync(path, bytes); 45 | 46 | return bytes; 47 | } 48 | } 49 | 50 | public class DownloadFontProvider : IFontProvider 51 | { 52 | public ConcurrentDictionary Cache { get; } = new ConcurrentDictionary(); 53 | 54 | public async Task GetFont(string name, string directory) 55 | { 56 | var requestUri = $"http://izbirkom.ru/{directory}/{name}"; 57 | Console.Write($"Downloading font from {requestUri}"); 58 | var client = new HttpClient(); 59 | client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:92.0) Gecko/20100101 Firefox/92.0"); 60 | var fontContent = await client.GetByteArrayAsync(requestUri); 61 | Console.WriteLine("done"); 62 | return fontContent; 63 | } 64 | } 65 | 66 | public class FontReplacer 67 | { 68 | /// css class name -> font name (guid.ttf) 69 | private Dictionary _classToFont = new(); 70 | 71 | /// font-family name -> font name (guid.ttf) 72 | private Dictionary _fonts = new(); 73 | 74 | /// font name (guid.ttf) -> Converter 75 | private Dictionary> _fontToConverter = new(); 76 | 77 | private IFontProvider _fontProvider; 78 | 79 | public FontReplacer(IFontProvider fontProvider) 80 | { 81 | _fontProvider = fontProvider; 82 | } 83 | 84 | public void VisitStyleRule(IStyleRule styleRule) 85 | { 86 | var ff = styleRule.Style.FontFamily; 87 | if (!string.IsNullOrEmpty(ff) && _fonts.TryGetValue(ff, out var fontName)) 88 | { 89 | foreach (Match match in Regex.Matches(styleRule.SelectorText, "\\.([\\w_]+)")) 90 | _classToFont[match.Groups[0].Value.Trim('.')] = fontName; 91 | } 92 | } 93 | 94 | public void VisitFontFaces(Stylesheet sheet) 95 | { 96 | foreach (var fontFace in sheet.FontfaceSetRules) 97 | { 98 | var fontName = Regex.Match(fontFace.Source, "[^/]*.ttf"); 99 | _fonts[fontFace.Family] = fontName.Value; 100 | _fontToConverter[fontName.Value] = ReadFont(fontName.Value); 101 | } 102 | } 103 | 104 | private static object CacheKey = new(); 105 | private Func ReadFont(string fontValue) 106 | { 107 | var converter = (ConcurrentDictionary>) 108 | _fontProvider.Cache.GetOrAdd(CacheKey, x => new ConcurrentDictionary>()); 109 | 110 | if (converter.TryGetValue(fontValue, out var translator)) 111 | { 112 | return translator; 113 | } 114 | 115 | var izb = new OpenFontReader().Read(new MemoryStream(_fontProvider.GetFont(fontValue, "fonts1").Result)); 116 | var ptsans = new OpenFontReader().Read(new MemoryStream(_fontProvider.GetFont("pt-sans-v12-latin_cyrillic-regular.ttf", "fonts").Result)); 117 | 118 | List alphabet = new(); 119 | for (char l = 'A'; l <= 'Z'; l++) alphabet.Add(l); 120 | for (char l = 'z'; l <= 'z'; l++) alphabet.Add(l); 121 | for (char l = '0'; l <= '9'; l++) alphabet.Add(l); 122 | for (char l = 'а'; l <= 'я'; l++) alphabet.Add(l); 123 | for (char l = 'А'; l <= 'Я'; l++) alphabet.Add(l); 124 | 125 | var glyphIndexToLetter = new char[ptsans.GlyphCount]; 126 | foreach (var c in alphabet) 127 | glyphIndexToLetter[ptsans.GetGlyphIndex(c)] = c; 128 | 129 | translator = c => glyphIndexToLetter[izb.GetGlyphIndex(c)]; 130 | converter[fontValue] = translator; 131 | return translator; 132 | } 133 | 134 | public void TranslateNode(string cls, HtmlNode node) 135 | { 136 | if (_classToFont.TryGetValue(cls, out var font1)) 137 | { 138 | var converter = _fontToConverter[font1]; 139 | var convertedText = new string(node.InnerText.Select(converter).ToArray()); 140 | node.InnerHtml = convertedText; 141 | node.Attributes.Remove("class"); 142 | } 143 | } 144 | } 145 | } -------------------------------------------------------------------------------- /OpenFont/Tables.Variations/Common.ItemVariationStore.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | //https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats 8 | 9 | //Item variation stores are used for most variation data other than that used for TrueType glyph outlines, 10 | //including the variation data in MVAR, HVAR, VVAR, BASE and GDEF tables. 11 | 12 | //Note: For CFF2 glyph outlines, delta values are interleaved directly within the glyph outline description in the CFF2 table. 13 | //The sets of regions which are associated with the delta sets are defined in an item variation store, 14 | //contained as a subtable within the CFF2 table. 15 | //See the CFF2 chapter for additional details. 16 | 17 | 18 | //... 19 | //The item variation store includes a variation region list and an array of item variation data subtables 20 | 21 | class ItemVariationStoreTable 22 | { 23 | 24 | public VariationRegion[] variationRegions; 25 | public void ReadContentFrom(BinaryReader reader) 26 | { 27 | 28 | 29 | //VariationRegionList: 30 | //Type Name Description 31 | //uint16 axisCount The number of variation axes for this font.This must be the same number as axisCount in the 'fvar' table. 32 | //uint16 regionCount The number of variation region tables in the variation region list. 33 | //VariationRegion variationRegions[regionCount] Array of variation regions. 34 | 35 | //The regions can be in any order. 36 | //The regions are defined using an array of RegionAxisCoordinates records, one for each axis defined in the 'fvar' table: 37 | 38 | ushort axisCount = reader.ReadUInt16(); 39 | ushort regionCount = reader.ReadUInt16(); 40 | variationRegions = new VariationRegion[regionCount]; 41 | for (int i = 0; i < regionCount; ++i) 42 | { 43 | var variationRegion = new VariationRegion(); 44 | variationRegion.ReadContent(reader, axisCount); 45 | variationRegions[i] = variationRegion; 46 | } 47 | } 48 | } 49 | class VariationRegion 50 | { 51 | //VariationRegion record: 52 | //Type Name Description 53 | //RegionAxisCoordinates regionAxes[axisCount] Array of region axis coordinates records, in the order of axes given in the 'fvar' table. 54 | //Each RegionAxisCoordinates record provides coordinate values for a region along a single axis: 55 | 56 | public RegionAxisCoordinate[] regionAxes; 57 | public void ReadContent(BinaryReader reader, int axisCount) 58 | { 59 | regionAxes = new RegionAxisCoordinate[axisCount]; 60 | for (int i = 0; i < axisCount; ++i) 61 | { 62 | regionAxes[i] = new RegionAxisCoordinate( 63 | reader.ReadF2Dot14(), //start 64 | reader.ReadF2Dot14(), //peak 65 | reader.ReadF2Dot14() //end 66 | ); 67 | } 68 | } 69 | } 70 | readonly struct RegionAxisCoordinate 71 | { 72 | //RegionAxisCoordinates record: 73 | //Type Name Description 74 | //F2DOT14 startCoord The region start coordinate value for the current axis. 75 | //F2DOT14 peakCoord The region peak coordinate value for the current axis. 76 | //F2DOT14 endCoord The region end coordinate value for the current axis. 77 | public readonly float startCoord; 78 | public readonly float peakCoord; 79 | public readonly float endCoord; 80 | 81 | public RegionAxisCoordinate(float startCoord, float peakCoord, float endCoord) 82 | { 83 | this.startCoord = startCoord; 84 | this.peakCoord = peakCoord; 85 | this.endCoord = endCoord; 86 | 87 | 88 | //The three values must all be within the range - 1.0 to + 1.0. 89 | //startCoord must be less than or equal to peakCoord, 90 | //and peakCoord must be less than or equal to endCoord. 91 | //The three values must be either all non-positive or all non-negative with one possible exception: 92 | //if peakCoord is zero, then startCoord can be negative or 0 while endCoord can be positive or zero. 93 | 94 | //... 95 | //Note: The following guidelines are used for setting the three values in different scenarios: 96 | 97 | //In the case of a non-intermediate region for which the given axis should factor into the scalar calculation for the region, 98 | //either startCoord and peakCoord are set to a negative value(typically, -1.0) 99 | //and endCoord is set to zero, or startCoord is set to zero and peakCoord and endCoord are set to a positive value(typically + 1.0). 100 | 101 | //In the case of an intermediate region for which the given axis should factor into the scalar calculation for the region, 102 | //startCoord, peakCoord and endCoord are all set to non - positive values or are all set to non - negative values. 103 | 104 | //If the given axis should not factor into the scalar calculation for a region, 105 | //then this is achieved by setting peakCoord to zero. 106 | //In this case, startCoord can be any non - positive value, and endCoord can be any non - negative value. 107 | //It is recommended either that all three be set to zero, or that startCoord be set to - 1.0 and endCoord be set to + 1.0. 108 | 109 | 110 | } 111 | #if DEBUG 112 | public override string ToString() 113 | { 114 | return "start:" + startCoord + ",peak:" + peakCoord + ",end:" + endCoord; 115 | } 116 | #endif 117 | } 118 | 119 | } -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/CBLC.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | using Typography.OpenFont.Tables.BitmapFonts; 7 | 8 | namespace Typography.OpenFont.Tables 9 | { 10 | //test font=> NotoColorEmoji.ttf 11 | 12 | //from https://docs.microsoft.com/en-us/typography/opentype/spec/cblc 13 | 14 | //Table structure 15 | 16 | //The CBLC table provides embedded bitmap locators. 17 | //It is used together with the CBDT table, which provides embedded, 18 | //color bitmap glyph data. 19 | //The formats of these two tables are backward compatible with the EBDT and EBLC tables 20 | //used for embedded monochrome and grayscale bitmaps. 21 | 22 | //The CBLC table begins with a header containing the table version and number of strikes. 23 | //CblcHeader 24 | //Type Name Description 25 | //uint16 majorVersion Major version of the CBLC table, = 3. 26 | //uint16 minorVersion Minor version of the CBLC table, = 0. 27 | //uint32 numSizes Number of BitmapSize tables 28 | 29 | //Note that the first version of the CBLC table is 3.0. 30 | 31 | //The CblcHeader is followed immediately by the BitmapSize table array(s). 32 | //The numSizes in the CblcHeader indicates the number of BitmapSize tables in the array. 33 | //Each strike is defined by one BitmapSize table. 34 | 35 | /// 36 | /// embeded bitmap locator 37 | /// 38 | class CBLC : TableEntry 39 | { 40 | BitmapSizeTable[] _bmpSizeTables; 41 | 42 | public const string _N = "CBLC"; 43 | public override string Name => _N; 44 | 45 | protected override void ReadContentFrom(BinaryReader reader) 46 | { 47 | long cblcBeginPos = reader.BaseStream.Position; 48 | ushort majorVersion = reader.ReadUInt16(); //3 49 | ushort minorVersion = reader.ReadUInt16(); //0 50 | uint numSizes = reader.ReadUInt32(); 51 | 52 | //The CblcHeader is followed immediately by the BitmapSize table array(s). 53 | //The numSizes in the CblcHeader indicates the number of BitmapSize tables in the array. 54 | //Each strike is defined by one BitmapSize table. 55 | BitmapSizeTable[] bmpSizeTables = new BitmapSizeTable[numSizes]; 56 | for (int i = 0; i < numSizes; ++i) 57 | { 58 | bmpSizeTables[i] = BitmapSizeTable.ReadBitmapSizeTable(reader); 59 | } 60 | _bmpSizeTables = bmpSizeTables; 61 | 62 | // 63 | //------- 64 | //IndexSubTableArray 65 | //Type Name Description 66 | //uint16 firstGlyphIndex First glyph ID of this range. 67 | //uint16 lastGlyphIndex Last glyph ID of this range(inclusive). 68 | //Offset32 additionalOffsetToIndexSubtable Add to indexSubTableArrayOffset to get offset from beginning of EBLC. 69 | 70 | //After determining the strike, 71 | //the rasterizer searches this array for the range containing the given glyph ID. 72 | //When the range is found, the additionalOffsetToIndexSubtable is added to the indexSubTableArrayOffset 73 | //to get the offset of the IndexSubTable in the EBLC. 74 | 75 | //The first indexSubTableArray is located after the last bitmapSizeSubTable entry. 76 | //Then the IndexSubTables for the strike follow. 77 | //Another IndexSubTableArray(if more than one strike) and 78 | //its IndexSubTableArray are next. 79 | 80 | //The EBLC continues with an array and IndexSubTables for each strike. 81 | //We now have the offset to the IndexSubTable. 82 | //All IndexSubTable formats begin with an IndexSubHeader which identifies the IndexSubTable format, 83 | //the format of the EBDT image data, 84 | //and the offset from the beginning of the EBDT table to the beginning of the image data for this range. 85 | 86 | for (int n = 0; n < numSizes; ++n) 87 | { 88 | BitmapSizeTable bmpSizeTable = bmpSizeTables[n]; 89 | uint numberofIndexSubTables = bmpSizeTable.numberOfIndexSubTables; 90 | 91 | // 92 | IndexSubTableArray[] indexSubTableArrs = new IndexSubTableArray[numberofIndexSubTables]; 93 | for (uint i = 0; i < numberofIndexSubTables; ++i) 94 | { 95 | indexSubTableArrs[i] = new IndexSubTableArray( 96 | reader.ReadUInt16(), //First glyph ID of this range. 97 | reader.ReadUInt16(), //Last glyph ID of this range (inclusive). 98 | reader.ReadUInt32());//Add to indexSubTableArrayOffset to get offset from beginning of EBLC. 99 | } 100 | 101 | //--- 102 | IndexSubTableBase[] subTables = new IndexSubTableBase[numberofIndexSubTables]; 103 | bmpSizeTable.indexSubTables = subTables; 104 | for (uint i = 0; i < numberofIndexSubTables; ++i) 105 | { 106 | IndexSubTableArray indexSubTableArr = indexSubTableArrs[i]; 107 | reader.BaseStream.Position = cblcBeginPos + bmpSizeTable.indexSubTableArrayOffset + indexSubTableArr.additionalOffsetToIndexSubtable; 108 | 109 | IndexSubTableBase result = subTables[i] = IndexSubTableBase.CreateFrom(bmpSizeTable, reader); 110 | result.firstGlyphIndex = indexSubTableArr.firstGlyphIndex; 111 | result.lastGlyphIndex = indexSubTableArr.lastGlyphIndex; 112 | } 113 | } 114 | } 115 | public Glyph[] BuildGlyphList() 116 | { 117 | List glyphs = new List(); 118 | int numSizes = _bmpSizeTables.Length; 119 | for (int n = 0; n < numSizes; ++n) 120 | { 121 | BitmapSizeTable bmpSizeTable = _bmpSizeTables[n]; 122 | uint numberofIndexSubTables = bmpSizeTable.numberOfIndexSubTables; 123 | for (uint i = 0; i < numberofIndexSubTables; ++i) 124 | { 125 | bmpSizeTable.indexSubTables[i].BuildGlyphList(glyphs); 126 | } 127 | } 128 | return glyphs.ToArray(); 129 | } 130 | } 131 | 132 | 133 | } -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/EBLC.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2017-present, WinterDev 2 | //MIT, 2015, Michael Popoloski, WinterDev 3 | 4 | using System; 5 | using System.IO; 6 | using Typography.OpenFont.Tables.BitmapFonts; 7 | 8 | namespace Typography.OpenFont.Tables 9 | { 10 | /// 11 | /// EBLC : Embedded bitmap location data 12 | /// 13 | class EBLC : TableEntry 14 | { 15 | public const string _N = "EBLC"; 16 | public override string Name => _N; 17 | // 18 | //from https://docs.microsoft.com/en-us/typography/opentype/spec/eblc 19 | //EBLC - Embedded Bitmap Location Table 20 | //---------------------------------------------- 21 | //The EBLC provides embedded bitmap locators.It is used together with the EDBTtable, which provides embedded, monochrome or grayscale bitmap glyph data, and the EBSC table, which provided embedded bitmap scaling information. 22 | //OpenType embedded bitmaps are called 'sbits' (for “scaler bitmaps”). A set of bitmaps for a face at a given size is called a strike. 23 | //The 'EBLC' table identifies the sizes and glyph ranges of the sbits, and keeps offsets to glyph bitmap data in indexSubTables.The 'EBDT' table then stores the glyph bitmap data, also in a number of different possible formats.Glyph metrics information may be stored in either the 'EBLC' or 'EBDT' table, depending upon the indexSubTable and glyph bitmap formats. The 'EBSC' table identifies sizes that will be handled by scaling up or scaling down other sbit sizes. 24 | //The 'EBLC' table uses the same format as the Apple Apple Advanced Typography (AAT) 'bloc' table. 25 | //The 'EBLC' table begins with a header containing the table version and number of strikes.An OpenType font may have one or more strikes embedded in the 'EBDT' table. 26 | //---------------------------------------------- 27 | //eblcHeader 28 | //---------------------------------------------- 29 | //Type Name Description 30 | //uint16 majorVersion Major version of the EBLC table, = 2. 31 | //uint16 minorVersion Minor version of the EBLC table, = 0. 32 | //uint32 numSizes Number of bitmapSizeTables 33 | //---------------------------------------------- 34 | //Note that the first version of the EBLC table is 2.0. 35 | //The eblcHeader is followed immediately by the bitmapSizeTable array(s). 36 | //The numSizes in the eblcHeader indicates the number of bitmapSizeTables in the array. 37 | //Each strike is defined by one bitmapSizeTable. 38 | 39 | BitmapSizeTable[] _bmpSizeTables; 40 | protected override void ReadContentFrom(BinaryReader reader) 41 | { 42 | // load each strike table 43 | long eblcBeginPos = reader.BaseStream.Position; 44 | // 45 | ushort versionMajor = reader.ReadUInt16(); 46 | ushort versionMinor = reader.ReadUInt16(); 47 | uint numSizes = reader.ReadUInt32(); 48 | 49 | if (numSizes > MAX_BITMAP_STRIKES) 50 | throw new Exception("Too many bitmap strikes in font."); 51 | 52 | //---------------- 53 | var bmpSizeTables = new BitmapSizeTable[numSizes]; 54 | for (int i = 0; i < numSizes; i++) 55 | { 56 | bmpSizeTables[i] = BitmapSizeTable.ReadBitmapSizeTable(reader); 57 | } 58 | _bmpSizeTables = bmpSizeTables; 59 | 60 | // 61 | //------- 62 | //IndexSubTableArray 63 | //Type Name Description 64 | //uint16 firstGlyphIndex First glyph ID of this range. 65 | //uint16 lastGlyphIndex Last glyph ID of this range(inclusive). 66 | //Offset32 additionalOffsetToIndexSubtable Add to indexSubTableArrayOffset to get offset from beginning of EBLC. 67 | 68 | //After determining the strike, 69 | //the rasterizer searches this array for the range containing the given glyph ID. 70 | //When the range is found, the additionalOffsetToIndexSubtable is added to the indexSubTableArrayOffset 71 | //to get the offset of the IndexSubTable in the EBLC. 72 | 73 | //The first indexSubTableArray is located after the last bitmapSizeSubTable entry. 74 | //Then the IndexSubTables for the strike follow. 75 | //Another IndexSubTableArray(if more than one strike) and 76 | //its IndexSubTableArray are next. 77 | 78 | //The EBLC continues with an array and IndexSubTables for each strike. 79 | //We now have the offset to the IndexSubTable. 80 | //All IndexSubTable formats begin with an IndexSubHeader which identifies the IndexSubTable format, 81 | //the format of the EBDT image data, 82 | //and the offset from the beginning of the EBDT table to the beginning of the image data for this range. 83 | 84 | for (int n = 0; n < numSizes; ++n) 85 | { 86 | BitmapSizeTable bmpSizeTable = bmpSizeTables[n]; 87 | uint numberofIndexSubTables = bmpSizeTable.numberOfIndexSubTables; 88 | 89 | // 90 | IndexSubTableArray[] indexSubTableArrs = new IndexSubTableArray[numberofIndexSubTables]; 91 | for (uint i = 0; i < numberofIndexSubTables; ++i) 92 | { 93 | indexSubTableArrs[i] = new IndexSubTableArray( 94 | reader.ReadUInt16(), //First glyph ID of this range. 95 | reader.ReadUInt16(), //Last glyph ID of this range (inclusive). 96 | reader.ReadUInt32());//Add to indexSubTableArrayOffset to get offset from beginning of EBLC. 97 | } 98 | 99 | //--- 100 | IndexSubTableBase[] subTables = new IndexSubTableBase[numberofIndexSubTables]; 101 | bmpSizeTable.indexSubTables = subTables; 102 | for (uint i = 0; i < numberofIndexSubTables; ++i) 103 | { 104 | IndexSubTableArray indexSubTableArr = indexSubTableArrs[i]; 105 | reader.BaseStream.Position = eblcBeginPos + bmpSizeTable.indexSubTableArrayOffset + indexSubTableArr.additionalOffsetToIndexSubtable; 106 | 107 | subTables[i] = IndexSubTableBase.CreateFrom(bmpSizeTable, reader); 108 | } 109 | } 110 | } 111 | 112 | 113 | 114 | const int MAX_BITMAP_STRIKES = 1024; 115 | } 116 | } -------------------------------------------------------------------------------- /OpenFont/Tables.Variations/AVar.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | //https://docs.microsoft.com/en-us/typography/opentype/spec/avar 8 | 9 | /// 10 | /// avar — Axis Variations Table 11 | /// 12 | class AVar : TableEntry 13 | { 14 | public const string _N = "avar"; 15 | public override string Name => _N; 16 | 17 | //The axis variations table('avar') is an optional table 18 | //used in variable fonts that use OpenType Font Variations mechanisms. 19 | //It can be used to modify aspects of how a design varies for different instances along a particular design-variation axis. 20 | //Specifically, it allows modification of the coordinate normalization that is used when processing variation data for a particular variation instance. 21 | 22 | //... 23 | 24 | //The 'avar' table must be used in combination with a font variations('fvar') table and 25 | //other required or optional tables used in variable fonts. 26 | 27 | SegmentMapRecord[] _axisSegmentMaps; 28 | protected override void ReadContentFrom(BinaryReader reader) 29 | { 30 | 31 | //The 'avar' table is comprised of a small header plus segment maps for each axis. 32 | 33 | //Axis variation table: 34 | //Type Name Description 35 | //uint16 majorVersion Major version number of the axis variations table — set to 1. 36 | //uint16 minorVersion Minor version number of the axis variations table — set to 0. 37 | //uint16 Permanently reserved; set to zero. 38 | //uint16 axisCount The number of variation axes for this font. 39 | // This must be the same number as axisCount in the 'fvar' table. 40 | //SegmentMaps axisSegmentMaps[axisCount] The segment maps array—one segment map for each axis, 41 | // in the order of axes specified in the 'fvar' table. 42 | //-------------- 43 | 44 | //There must be one segment map for each axis defined in the 'fvar' table, 45 | //and the segment maps for the different axes must be given in the order of axes specified in the 'fvar' table. 46 | //The segment map for each axis is comprised of a list of axis - value mapping records. 47 | 48 | ushort majorVersion = reader.ReadUInt16(); 49 | ushort minorVersion = reader.ReadUInt16(); 50 | ushort reserved = reader.ReadUInt16(); 51 | ushort axisCount = reader.ReadUInt16(); 52 | 53 | //Each axis value map record provides a single axis-value mapping correspondence. 54 | _axisSegmentMaps = new SegmentMapRecord[axisCount]; 55 | for (int i = 0; i < axisCount; ++i) 56 | { 57 | SegmentMapRecord segmentMap = new SegmentMapRecord(); 58 | segmentMap.ReadContent(reader); 59 | _axisSegmentMaps[i] = segmentMap; 60 | } 61 | 62 | 63 | 64 | } 65 | public class SegmentMapRecord 66 | { 67 | //SegmentMaps record: 68 | //Type Name Description 69 | //uint16 positionMapCount The number of correspondence pairs for this axis. 70 | //AxisValueMap axisValueMaps[positionMapCount] The array of axis value map records for this axis. 71 | public AxisValueMap[] axisValueMaps; 72 | public void ReadContent(BinaryReader reader) 73 | { 74 | ushort positionMapCount = reader.ReadUInt16(); 75 | axisValueMaps = new AxisValueMap[positionMapCount]; 76 | for (int i = 0; i < positionMapCount; ++i) 77 | { 78 | axisValueMaps[i] = new AxisValueMap( 79 | reader.ReadF2Dot14(), 80 | reader.ReadF2Dot14() 81 | ); 82 | } 83 | } 84 | } 85 | public readonly struct AxisValueMap 86 | { 87 | //AxisValueMap record: 88 | //Type Name Description 89 | //F2DOT14 fromCoordinate A normalized coordinate value obtained using default normalization. 90 | //F2DOT14 toCoordinate The modified, normalized coordinate value. 91 | 92 | 93 | //Axis value maps can be provided for any axis, 94 | //but are required only if the normalization mapping for an axis is being modified. 95 | //If the segment map for a given axis has any value maps, 96 | //then it must include at least three value maps: -1 to - 1, 0 to 0, and 1 to 1. 97 | //These value mappings are essential to the design of the variation mechanisms and 98 | //are required even if no additional maps are specified for a given axis. 99 | //If any of these is missing, then no modification to axis coordinate values will be made for that axis. 100 | 101 | 102 | //All of the axis value map records for a given axis must have different fromCoordinate values, 103 | //and axis value map records must be arranged in increasing order of the fromCoordinate value. 104 | //If the fromCoordinate value of a record is less than or equal to the fromCoordinate value of a previous record in the array, 105 | //then the given record may be ignored. 106 | 107 | //Also, for any given record except the first, 108 | //the toCoordinate value must be greater than or equal to the toCoordinate value of the preceding record. 109 | //This requirement ensures that there are no retrograde behaviors as the user-scale value range is traversed. 110 | //If a toCoordinate value of a record is less than that of the previous record, then the given record may be ignored. 111 | 112 | public readonly float fromCoordinate; 113 | public readonly float toCoordinate; 114 | public AxisValueMap(float fromCoordinate, float toCoordinate) 115 | { 116 | this.fromCoordinate = fromCoordinate; 117 | this.toCoordinate = toCoordinate; 118 | } 119 | #if DEBUG 120 | public override string ToString() 121 | { 122 | return "from:" + fromCoordinate + " to:" + toCoordinate; 123 | } 124 | #endif 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/CoverageTable.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace Typography.OpenFont.Tables 7 | { 8 | //https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2 9 | 10 | public abstract class CoverageTable 11 | { 12 | public abstract int FindPosition(ushort glyphIndex); 13 | public abstract IEnumerable GetExpandedValueIter(); 14 | 15 | #if DEBUG 16 | 17 | #endif 18 | 19 | public static CoverageTable CreateFrom(BinaryReader reader, long beginAt) 20 | { 21 | reader.BaseStream.Seek(beginAt, SeekOrigin.Begin); 22 | ushort format = reader.ReadUInt16(); 23 | switch (format) 24 | { 25 | default: throw new NotSupportedException(); 26 | case 1: return CoverageFmt1.CreateFrom(reader); 27 | case 2: return CoverageFmt2.CreateFrom(reader); 28 | } 29 | } 30 | 31 | public static CoverageTable[] CreateMultipleCoverageTables(long initPos, ushort[] offsets, BinaryReader reader) 32 | { 33 | CoverageTable[] results = new CoverageTable[offsets.Length]; 34 | for (int i = 0; i < results.Length; ++i) 35 | { 36 | results[i] = CoverageTable.CreateFrom(reader, initPos + offsets[i]); 37 | } 38 | return results; 39 | } 40 | } 41 | 42 | public class CoverageFmt1 : CoverageTable 43 | { 44 | public static CoverageFmt1 CreateFrom(BinaryReader reader) 45 | { 46 | // CoverageFormat1 table: Individual glyph indices 47 | // Type Name Description 48 | // uint16 CoverageFormat Format identifier-format = 1 49 | // uint16 GlyphCount Number of glyphs in the GlyphArray 50 | // uint16 GlyphArray[GlyphCount] Array of glyph IDs — in numerical order 51 | 52 | ushort glyphCount = reader.ReadUInt16(); 53 | ushort[] glyphs = Utils.ReadUInt16Array(reader, glyphCount); 54 | return new CoverageFmt1() { _orderedGlyphIdList = glyphs }; 55 | } 56 | 57 | public override int FindPosition(ushort glyphIndex) 58 | { 59 | // "The glyph indices must be in numerical order for binary searching of the list" 60 | // (https://www.microsoft.com/typography/otspec/chapter2.htm#coverageFormat1) 61 | int n = Array.BinarySearch(_orderedGlyphIdList, glyphIndex); 62 | return n < 0 ? -1 : n; 63 | } 64 | public override IEnumerable GetExpandedValueIter() { return _orderedGlyphIdList; } 65 | 66 | #if DEBUG 67 | 68 | public override string ToString() 69 | { 70 | List stringList = new List(); 71 | foreach (ushort g in _orderedGlyphIdList) 72 | { 73 | stringList.Add(g.ToString()); 74 | } 75 | return "CoverageFmt1: " + string.Join(",", stringList.ToArray()); 76 | } 77 | #endif 78 | 79 | internal ushort[] _orderedGlyphIdList; 80 | } 81 | 82 | public class CoverageFmt2 : CoverageTable 83 | { 84 | public override int FindPosition(ushort glyphIndex) 85 | { 86 | // Ranges must be in glyph ID order, and they must be distinct, with no overlapping. 87 | // [...] quick calculation of the Coverage Index for any glyph in any range using the 88 | // formula: Coverage Index (glyphID) = startCoverageIndex + glyphID - startGlyphID. 89 | // (https://www.microsoft.com/typography/otspec/chapter2.htm#coverageFormat2) 90 | int n = Array.BinarySearch(_endIndices, glyphIndex); 91 | n = n < 0 ? ~n : n; 92 | if (n >= RangeCount || glyphIndex < _startIndices[n]) 93 | { 94 | return -1; 95 | } 96 | return _coverageIndices[n] + glyphIndex - _startIndices[n]; 97 | } 98 | 99 | public override IEnumerable GetExpandedValueIter() 100 | { 101 | for (int i = 0; i < RangeCount; ++i) 102 | { 103 | for (ushort n = _startIndices[i]; n <= _endIndices[i]; ++n) 104 | { 105 | yield return n; 106 | } 107 | } 108 | } 109 | public static CoverageFmt2 CreateFrom(BinaryReader reader) 110 | { 111 | // CoverageFormat2 table: Range of glyphs 112 | // Type Name Description 113 | // uint16 CoverageFormat Format identifier-format = 2 114 | // uint16 RangeCount Number of RangeRecords 115 | // struct RangeRecord[RangeCount] Array of glyph ranges — ordered by StartGlyphID. 116 | // 117 | // RangeRecord 118 | // Type Name Description 119 | // uint16 StartGlyphID First glyph ID in the range 120 | // uint16 EndGlyphID Last glyph ID in the range 121 | // uint16 StartCoverageIndex Coverage Index of first glyph ID in range 122 | 123 | ushort rangeCount = reader.ReadUInt16(); 124 | ushort[] startIndices = new ushort[rangeCount]; 125 | ushort[] endIndices = new ushort[rangeCount]; 126 | ushort[] coverageIndices = new ushort[rangeCount]; 127 | for (int i = 0; i < rangeCount; ++i) 128 | { 129 | startIndices[i] = reader.ReadUInt16(); 130 | endIndices[i] = reader.ReadUInt16(); 131 | coverageIndices[i] = reader.ReadUInt16(); 132 | } 133 | 134 | return new CoverageFmt2() 135 | { 136 | _startIndices = startIndices, 137 | _endIndices = endIndices, 138 | _coverageIndices = coverageIndices 139 | }; 140 | } 141 | 142 | #if DEBUG 143 | 144 | public override string ToString() 145 | { 146 | List stringList = new List(); 147 | for (int i = 0; i < RangeCount; ++i) 148 | { 149 | stringList.Add(string.Format("{0}-{1}", _startIndices[i], _endIndices[i])); 150 | } 151 | return "CoverageFmt2: " + string.Join(",", stringList.ToArray()); 152 | } 153 | #endif 154 | 155 | internal ushort[] _startIndices; 156 | internal ushort[] _endIndices; 157 | internal ushort[] _coverageIndices; 158 | 159 | private int RangeCount => _startIndices.Length; 160 | } 161 | 162 | } 163 | -------------------------------------------------------------------------------- /OpenFont/AdditionalInfo/MacPostFormat1.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | 3 | 4 | using System.IO; 5 | namespace Typography.OpenFont 6 | { 7 | static class MacPostFormat1 8 | { 9 | 10 | 11 | static string[] s_stdMacGlyphNames; 12 | 13 | public static string[] GetStdMacGlyphNames() 14 | { 15 | if (s_stdMacGlyphNames == null) 16 | { 17 | s_stdMacGlyphNames = new string[260]; 18 | using (StringReader strReader = new StringReader(orgGlyphNames)) 19 | { 20 | string[] seps = new string[] { " " }; 21 | 22 | string line = strReader.ReadLine(); 23 | 24 | while (line != null) 25 | { 26 | line = line.Trim(); 27 | if (line != "") 28 | { 29 | 30 | string[] key_value = line.Split(seps, System.StringSplitOptions.RemoveEmptyEntries); 31 | if (key_value.Length != 2) 32 | { 33 | throw new System.NotSupportedException(); 34 | } 35 | if (int.TryParse(key_value[0], System.Globalization.NumberStyles.None, System.Globalization.CultureInfo.InvariantCulture, out int index)) 36 | { 37 | #if DEBUG 38 | if (index < 0 || index > 258) 39 | { 40 | 41 | } 42 | #endif 43 | s_stdMacGlyphNames[index] = key_value[1].Trim(); 44 | } 45 | else 46 | { 47 | throw new System.NotSupportedException(); 48 | } 49 | 50 | } 51 | line = strReader.ReadLine(); 52 | } 53 | } 54 | } 55 | return s_stdMacGlyphNames; 56 | } 57 | 58 | 59 | //'post' Format 1 60 | //The order in which glyphs are placed in a font is at the convenience of the font developer To use format 1, 61 | //a font must contain exactly the 258 glyphs in the standard Macintosh ordering. 62 | //For such fonts, the glyph names are taken from the system. 63 | //As a result, this format does not require a special subtable. 64 | //The names for these 258 glyphs are, in order: 65 | 66 | //this is a copy from 67 | //from https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6post.html 68 | 69 | //Glyph ID Name 70 | const string orgGlyphNames = 71 | @" 72 | 0 .notdef 73 | 1 .null 74 | 2 nonmarkingreturn 75 | 3 space 76 | 4 exclam 77 | 5 quotedbl 78 | 6 numbersign 79 | 7 dollar 80 | 8 percent 81 | 9 ampersand 82 | 10 quotesingle 83 | 11 parenleft 84 | 12 parenright 85 | 13 asterisk 86 | 14 plus 87 | 15 comma 88 | 16 hyphen 89 | 17 period 90 | 18 slash 91 | 19 zero 92 | 20 one 93 | 21 two 94 | 22 three 95 | 23 four 96 | 24 five 97 | 25 six 98 | 26 seven 99 | 27 eight 100 | 28 nine 101 | 29 colon 102 | 30 semicolon 103 | 31 less 104 | 32 equal 105 | 33 greater 106 | 34 question 107 | 35 at 108 | 36 A 109 | 37 B 110 | 38 C 111 | 39 D 112 | 40 E 113 | 41 F 114 | 42 G 115 | 43 H 116 | 44 I 117 | 45 J 118 | 46 K 119 | 47 L 120 | 48 M 121 | 49 N 122 | 50 O 123 | 51 P 124 | 52 Q 125 | 53 R 126 | 54 S 127 | 55 T 128 | 56 U 129 | 57 V 130 | 58 W 131 | 59 X 132 | 60 Y 133 | 61 Z 134 | 62 bracketleft 135 | 63 backslash 136 | 64 bracketright 137 | 65 asciicircum 138 | 66 underscore 139 | 67 grave 140 | 68 a 141 | 69 b 142 | 70 c 143 | 71 d 144 | 72 e 145 | 73 f 146 | 74 g 147 | 75 h 148 | 76 i 149 | 77 j 150 | 78 k 151 | 79 l 152 | 80 m 153 | 81 n 154 | 82 o 155 | 83 p 156 | 84 q 157 | 85 r 158 | 86 s 159 | 87 t 160 | 88 u 161 | 89 v 162 | 90 w 163 | 91 x 164 | 92 y 165 | 93 z 166 | 94 braceleft 167 | 95 bar 168 | 96 braceright 169 | 97 asciitilde 170 | 98 Adieresis 171 | 99 Aring 172 | 100 Ccedilla 173 | 101 Eacute 174 | 102 Ntilde 175 | 103 Odieresis 176 | 104 Udieresis 177 | 105 aacute 178 | 106 agrave 179 | 107 acircumflex 180 | 108 adieresis 181 | 109 atilde 182 | 110 aring 183 | 111 ccedilla 184 | 112 eacute 185 | 113 egrave 186 | 114 ecircumflex 187 | 115 edieresis 188 | 116 iacute 189 | 117 igrave 190 | 118 icircumflex 191 | 119 idieresis 192 | 120 ntilde 193 | 121 oacute 194 | 122 ograve 195 | 123 ocircumflex 196 | 124 odieresis 197 | 125 otilde 198 | 126 uacute 199 | 127 ugrave 200 | 128 ucircumflex 201 | 129 udieresis 202 | 130 dagger 203 | 131 degree 204 | 132 cent 205 | 133 sterling 206 | 134 section 207 | 135 bullet 208 | 136 paragraph 209 | 137 germandbls 210 | 138 registered 211 | 139 copyright 212 | 140 trademark 213 | 141 acute 214 | 142 dieresis 215 | 143 notequal 216 | 144 AE 217 | 145 Oslash 218 | 146 infinity 219 | 147 plusminus 220 | 148 lessequal 221 | 149 greaterequal 222 | 150 yen 223 | 151 mu 224 | 152 partialdiff 225 | 153 summation 226 | 154 product 227 | 155 pi 228 | 156 integral 229 | 157 ordfeminine 230 | 158 ordmasculine 231 | 159 Omega 232 | 160 ae 233 | 161 oslash 234 | 162 questiondown 235 | 163 exclamdown 236 | 164 logicalnot 237 | 165 radical 238 | 166 florin 239 | 167 approxequal 240 | 168 Delta 241 | 169 guillemotleft 242 | 170 guillemotright 243 | 171 ellipsis 244 | 172 nonbreakingspace 245 | 173 Agrave 246 | 174 Atilde 247 | 175 Otilde 248 | 176 OE 249 | 177 oe 250 | 178 endash 251 | 179 emdash 252 | 180 quotedblleft 253 | 181 quotedblright 254 | 182 quoteleft 255 | 183 quoteright 256 | 184 divide 257 | 185 lozenge 258 | 186 ydieresis 259 | 187 Ydieresis 260 | 188 fraction 261 | 189 currency 262 | 190 guilsinglleft 263 | 191 guilsinglright 264 | 192 fi 265 | 193 fl 266 | 194 daggerdbl 267 | 195 periodcentered 268 | 196 quotesinglbase 269 | 197 quotedblbase 270 | 198 perthousand 271 | 199 Acircumflex 272 | 200 Ecircumflex 273 | 201 Aacute 274 | 202 Edieresis 275 | 203 Egrave 276 | 204 Iacute 277 | 205 Icircumflex 278 | 206 Idieresis 279 | 207 Igrave 280 | 208 Oacute 281 | 209 Ocircumflex 282 | 210 apple 283 | 211 Ograve 284 | 212 Uacute 285 | 213 Ucircumflex 286 | 214 Ugrave 287 | 215 dotlessi 288 | 216 circumflex 289 | 217 tilde 290 | 218 macron 291 | 219 breve 292 | 220 dotaccent 293 | 221 ring 294 | 222 cedilla 295 | 223 hungarumlaut 296 | 224 ogonek 297 | 225 caron 298 | 226 Lslash 299 | 227 lslash 300 | 228 Scaron 301 | 229 scaron 302 | 230 Zcaron 303 | 231 zcaron 304 | 232 brokenbar 305 | 233 Eth 306 | 234 eth 307 | 235 Yacute 308 | 236 yacute 309 | 237 Thorn 310 | 238 thorn 311 | 239 minus 312 | 240 multiply 313 | 241 onesuperior 314 | 242 twosuperior 315 | 243 threesuperior 316 | 244 onehalf 317 | 245 onequarter 318 | 246 threequarters 319 | 247 franc 320 | 248 Gbreve 321 | 249 gbreve 322 | 250 Idotaccent 323 | 251 Scedilla 324 | 252 scedilla 325 | 253 Cacute 326 | 254 cacute 327 | 255 Ccaron 328 | 256 ccaron 329 | 257 dcroat"; 330 | 331 | } 332 | 333 | } 334 | -------------------------------------------------------------------------------- /OpenFont/Tables.AdvancedLayout/FeatureList.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2016-present, WinterDev 2 | 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | 8 | //https://docs.microsoft.com/en-us/typography/opentype/spec/featurelist 9 | //The order for applying standard features encoded in OpenType fonts: 10 | 11 | //Feature Feature function Layout operation Required 12 | //--------------------- 13 | //Language based forms: 14 | //--------------------- 15 | //ccmp Character composition/decomposition substitution GSUB 16 | //--------------------- 17 | //Typographical forms: 18 | //--------------------- 19 | //liga Standard ligature substitution GSUB 20 | //clig Contextual ligature substitution GSUB 21 | //Positioning features: 22 | //kern Pair kerning GPOS 23 | //mark Mark to base positioning GPOS X 24 | //mkmk Mark to mark positioning GPOS X 25 | 26 | //[GSUB = glyph substitution, GPOS = glyph positioning] 27 | 28 | public class FeatureList 29 | { 30 | 31 | 32 | public FeatureTable[] featureTables; 33 | public static FeatureList CreateFrom(BinaryReader reader, long beginAt) 34 | { 35 | //https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2 36 | 37 | //------------------ 38 | //FeatureList table 39 | //------------------ 40 | //Type Name Description 41 | //uint16 FeatureCount Number of FeatureRecords in this table 42 | //struct FeatureRecord[FeatureCount] Array of FeatureRecords-zero-based (first feature has FeatureIndex = 0)-listed alphabetically by FeatureTag 43 | //------------------ 44 | //FeatureRecord 45 | //------------------ 46 | //Type Name Description 47 | //Tag FeatureTag 4-byte feature identification tag 48 | //Offset16 Feature Offset to Feature table-from beginning of FeatureList 49 | //---------------------------------------------------- 50 | reader.BaseStream.Seek(beginAt, SeekOrigin.Begin); 51 | // 52 | FeatureList featureList = new FeatureList(); 53 | ushort featureCount = reader.ReadUInt16(); 54 | FeatureRecord[] featureRecords = new FeatureRecord[featureCount]; 55 | for (int i = 0; i < featureCount; ++i) 56 | { 57 | //read script record 58 | featureRecords[i] = new FeatureRecord( 59 | reader.ReadUInt32(), //feature tag 60 | reader.ReadUInt16()); //Offset16 61 | } 62 | //read each feature table 63 | FeatureTable[] featureTables = featureList.featureTables = new FeatureTable[featureCount]; 64 | for (int i = 0; i < featureCount; ++i) 65 | { 66 | FeatureRecord frecord = featureRecords[i]; 67 | (featureTables[i] = FeatureTable.CreateFrom(reader, beginAt + frecord.offset)).FeatureTag = frecord.featureTag; 68 | } 69 | return featureList; 70 | } 71 | readonly struct FeatureRecord 72 | { 73 | public readonly uint featureTag;//4-byte ScriptTag identifier 74 | public readonly ushort offset; //Script Offset to Script table-from beginning of ScriptList 75 | public FeatureRecord(uint featureTag, ushort offset) 76 | { 77 | this.featureTag = featureTag; 78 | this.offset = offset; 79 | } 80 | 81 | public string FeatureName => Utils.TagToString(featureTag); 82 | #if DEBUG 83 | public override string ToString() 84 | { 85 | return FeatureName + "," + offset; 86 | } 87 | #endif 88 | } 89 | 90 | 91 | //Feature Table 92 | 93 | //A Feature table defines a feature with one or more lookups. 94 | //The client uses the lookups to substitute or position glyphs. 95 | 96 | //Feature tables defined within the GSUB table contain references to glyph substitution lookups, 97 | //and feature tables defined within the GPOS table contain references to glyph positioning lookups. 98 | //If a text-processing operation requires both glyph substitution and positioning, 99 | //then both the GSUB and GPOS tables must each define a Feature table, 100 | //and the tables must use the same FeatureTags. 101 | 102 | //A Feature table consists of an offset to a Feature Parameters (FeatureParams) table 103 | //(if one has been defined for this feature - see note in the following paragraph), 104 | //a count of the lookups listed for the feature (LookupCount), 105 | //and an arbitrarily ordered array of indices into a LookupList (LookupListIndex). 106 | //The LookupList indices are references into an array of offsets to Lookup tables. 107 | 108 | //The format of the Feature Parameters table is specific to a particular feature, 109 | //and must be specified in the feature's entry in the Feature Tags section of the OpenType Layout Tag Registry. 110 | //The length of the Feature Parameters table must be implicitly or explicitly specified in the Feature Parameters table itself. 111 | //The FeatureParams field in the Feature Table records the offset relative to the beginning of the Feature Table. 112 | //If a Feature Parameters table is not needed, the FeatureParams field must be set to NULL. 113 | 114 | //To identify the features in a GSUB or GPOS table, 115 | //a text-processing client reads the FeatureTag of each FeatureRecord referenced in a given LangSys table. 116 | //Then the client selects the features it wants to implement and uses the LookupList to retrieve the Lookup indices of the chosen features. 117 | //Next, the client arranges the indices in the LookupList order. 118 | //Finally, the client applies the lookup data to substitute or position glyphs. 119 | 120 | //Example 3 at the end of this chapter shows the FeatureList and Feature tables used to substitute ligatures in two languages. 121 | // 122 | 123 | 124 | public class FeatureTable 125 | { 126 | public static FeatureTable CreateFrom(BinaryReader reader, long beginAt) 127 | { 128 | reader.BaseStream.Seek(beginAt, SeekOrigin.Begin); 129 | // 130 | ushort featureParams = reader.ReadUInt16(); 131 | ushort lookupCount = reader.ReadUInt16(); 132 | 133 | FeatureTable featureTable = new FeatureTable(); 134 | featureTable.LookupListIndices = Utils.ReadUInt16Array(reader, lookupCount); 135 | return featureTable; 136 | } 137 | public ushort[] LookupListIndices { get; private set; } 138 | public uint FeatureTag { get; set; } 139 | public string TagName => Utils.TagToString(this.FeatureTag); 140 | #if DEBUG 141 | public override string ToString() 142 | { 143 | return this.TagName; 144 | } 145 | #endif 146 | } 147 | } 148 | } -------------------------------------------------------------------------------- /OpenFont/Typeface_TrimableExtensions.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2020-present, WinterDev 2 | 3 | using System; 4 | using System.IO; 5 | 6 | namespace Typography.OpenFont.Trimmable 7 | { 8 | 9 | //------------------------- 10 | //This is our extension***, 11 | //NOT in OpenFont spec 12 | //------------------------- 13 | //user can reload a new clone of glyphs with fewer detail 14 | //or restore a new clone of glyphs with full detail 15 | 16 | //for unload and reload 17 | 18 | using Typography.OpenFont.Tables; 19 | 20 | public class RestoreTicket 21 | { 22 | internal RestoreTicket() 23 | { 24 | } 25 | internal string TypefaceName { get; set; } 26 | 27 | internal TableHeader[] Headers; 28 | internal bool HasTtf; 29 | internal bool HasCff; 30 | internal bool HasSvg; 31 | internal bool HasBitmapSource; 32 | 33 | internal bool ControlValues; 34 | internal bool PrepProgramBuffer; 35 | internal bool FpgmProgramBuffer; 36 | internal bool CPALTable; 37 | internal bool COLRTable; 38 | internal bool GaspTable; 39 | } 40 | 41 | public enum TrimMode 42 | { 43 | /// 44 | /// No trim, full glyph instruction 45 | /// 46 | No, //default 47 | /// 48 | /// only essential info for glyph layout 49 | /// 50 | EssentailLayoutInfo, 51 | /// 52 | /// restore again 53 | /// 54 | Restored, 55 | } 56 | 57 | public static class TypefaceExtensions 58 | { 59 | public static RestoreTicket TrimDown(this Typeface typeface) => typeface.TrimDownAndRemoveGlyphBuildingDetail(); 60 | public static TrimMode GetTrimMode(this Typeface typeface) => typeface._typefaceTrimMode; 61 | public static bool IsTrimmed(this Typeface typeface) => typeface._typefaceTrimMode == TrimMode.EssentailLayoutInfo; 62 | 63 | public static void RestoreUp(this Typeface typeface, RestoreTicket ticket, OpenFontReader openFontReader, Stream fontStream) 64 | { 65 | if (typeface.IsTrimmed()) 66 | { 67 | openFontReader.Read(typeface, ticket, fontStream); 68 | } 69 | } 70 | public static void RestoreUp(this Typeface typeface, RestoreTicket ticket, Stream fontStream) 71 | { 72 | //use default opent font reader 73 | RestoreUp(typeface, ticket, new OpenFontReader(), fontStream); 74 | } 75 | 76 | 77 | 78 | } 79 | } 80 | 81 | 82 | namespace Typography.OpenFont 83 | { 84 | using Typography.OpenFont.Tables; 85 | using Typography.OpenFont.Trimmable; 86 | 87 | //------------------------- 88 | //This is our extension***, 89 | //NOT in OpenFont spec 90 | //------------------------- 91 | //user can reload a new clone of glyphs with fewer detail 92 | //or restore a new clone of glyphs with full detail 93 | 94 | partial class Typeface 95 | { 96 | internal TrimMode _typefaceTrimMode; 97 | 98 | 99 | internal RestoreTicket TrimDownAndRemoveGlyphBuildingDetail() 100 | { 101 | switch (_typefaceTrimMode) 102 | { 103 | default: throw new NotSupportedException(); 104 | case TrimMode.EssentailLayoutInfo: return null;//same mode 105 | case TrimMode.Restored: 106 | case TrimMode.No: 107 | { 108 | RestoreTicket ticket = new RestoreTicket(); 109 | ticket.TypefaceName = Name; 110 | ticket.Headers = _tblHeaders; //a copy 111 | 112 | //FROM:GlyphLoadingMode.Full => TO: GlyphLoadingMode.EssentailLayoutInfo 113 | 114 | ticket.HasTtf = _hasTtfOutline; 115 | 116 | //cache glyph name before unload 117 | if (_cff1FontSet != null) 118 | { 119 | ticket.HasCff = true; 120 | UpdateCff1FontSetNamesCache();//*** 121 | _cff1FontSet = null; 122 | } 123 | 124 | //1.Ttf and Otf => clone each glyphs in NO building 125 | Glyph[] newClones = new Glyph[_glyphs.Length]; 126 | for (int i = 0; i < newClones.Length; ++i) 127 | { 128 | newClones[i] = Glyph.Clone_NO_BuildingInstructions(_glyphs[i]); 129 | } 130 | _glyphs = newClones; 131 | 132 | //and since glyph has no building instructions in this mode 133 | //so ... 134 | 135 | ticket.ControlValues = ControlValues != null; 136 | ControlValues = null; 137 | 138 | ticket.PrepProgramBuffer = PrepProgramBuffer != null; 139 | PrepProgramBuffer = null; 140 | 141 | ticket.FpgmProgramBuffer = FpgmProgramBuffer != null; 142 | FpgmProgramBuffer = null; 143 | 144 | ticket.CPALTable = CPALTable != null; 145 | CPALTable = null; 146 | 147 | ticket.COLRTable = COLRTable != null; 148 | COLRTable = null; 149 | 150 | ticket.GaspTable = GaspTable != null; 151 | GaspTable = null; 152 | 153 | // 154 | //3. Svg=> remove SvgTable 155 | if (_svgTable != null) 156 | { 157 | ticket.HasSvg = true; 158 | _svgTable.UnloadSvgData(); 159 | _svgTable = null; 160 | } 161 | 162 | //4. Bitmap Font => remove embeded bitmap data 163 | if (_bitmapFontGlyphSource != null) 164 | { 165 | ticket.HasBitmapSource = true; 166 | _bitmapFontGlyphSource.UnloadCBDT(); 167 | } 168 | 169 | 170 | _typefaceTrimMode = TrimMode.EssentailLayoutInfo; 171 | 172 | return ticket; 173 | } 174 | } 175 | } 176 | 177 | 178 | internal bool CompareOriginalHeadersWithNewlyLoadOne(TableHeader[] others) 179 | { 180 | if (_tblHeaders != null && others != null && 181 | _tblHeaders.Length == others.Length) 182 | { 183 | for (int i = 0; i < _tblHeaders.Length; ++i) 184 | { 185 | TableHeader a = _tblHeaders[i]; 186 | TableHeader b = others[i]; 187 | 188 | if (a.Tag != b.Tag || 189 | a.Offset != b.Offset || 190 | a.Length != b.Length || 191 | a.CheckSum != b.CheckSum) 192 | { 193 | #if DEBUG 194 | System.Diagnostics.Debugger.Break(); 195 | #endif 196 | 197 | return false; 198 | } 199 | } 200 | //pass all 201 | return true; 202 | } 203 | return false; 204 | } 205 | } 206 | 207 | } 208 | 209 | 210 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Dd]ebugPublic/ 19 | [Rr]elease/ 20 | [Rr]eleases/ 21 | x64/ 22 | x86/ 23 | [Aa][Rr][Mm]/ 24 | [Aa][Rr][Mm]64/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015/2017 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # Visual Studio 2017 auto generated files 36 | Generated\ Files/ 37 | 38 | # MSTest test Results 39 | [Tt]est[Rr]esult*/ 40 | [Bb]uild[Ll]og.* 41 | 42 | # NUNIT 43 | *.VisualState.xml 44 | TestResult.xml 45 | 46 | # Build Results of an ATL Project 47 | [Dd]ebugPS/ 48 | [Rr]eleasePS/ 49 | dlldata.c 50 | 51 | # Benchmark Results 52 | BenchmarkDotNet.Artifacts/ 53 | 54 | # .NET Core 55 | project.lock.json 56 | project.fragment.lock.json 57 | artifacts/ 58 | 59 | # StyleCop 60 | StyleCopReport.xml 61 | 62 | # Files built by Visual Studio 63 | *_i.c 64 | *_p.c 65 | *_h.h 66 | *.ilk 67 | *.meta 68 | *.obj 69 | *.iobj 70 | *.pch 71 | *.pdb 72 | *.ipdb 73 | *.pgc 74 | *.pgd 75 | *.rsp 76 | *.sbr 77 | *.tlb 78 | *.tli 79 | *.tlh 80 | *.tmp 81 | *.tmp_proj 82 | *_wpftmp.csproj 83 | *.log 84 | *.vspscc 85 | *.vssscc 86 | .builds 87 | *.pidb 88 | *.svclog 89 | *.scc 90 | 91 | # Chutzpah Test files 92 | _Chutzpah* 93 | 94 | # Visual C++ cache files 95 | ipch/ 96 | *.aps 97 | *.ncb 98 | *.opendb 99 | *.opensdf 100 | *.sdf 101 | *.cachefile 102 | *.VC.db 103 | *.VC.VC.opendb 104 | 105 | # Visual Studio profiler 106 | *.psess 107 | *.vsp 108 | *.vspx 109 | *.sap 110 | 111 | # Visual Studio Trace Files 112 | *.e2e 113 | 114 | # TFS 2012 Local Workspace 115 | $tf/ 116 | 117 | # Guidance Automation Toolkit 118 | *.gpState 119 | 120 | # ReSharper is a .NET coding add-in 121 | _ReSharper*/ 122 | *.[Rr]e[Ss]harper 123 | *.DotSettings.user 124 | 125 | # JustCode is a .NET coding add-in 126 | .JustCode 127 | 128 | # TeamCity is a build add-in 129 | _TeamCity* 130 | 131 | # DotCover is a Code Coverage Tool 132 | *.dotCover 133 | 134 | # AxoCover is a Code Coverage Tool 135 | .axoCover/* 136 | !.axoCover/settings.json 137 | 138 | # Visual Studio code coverage results 139 | *.coverage 140 | *.coveragexml 141 | 142 | # NCrunch 143 | _NCrunch_* 144 | .*crunch*.local.xml 145 | nCrunchTemp_* 146 | 147 | # MightyMoose 148 | *.mm.* 149 | AutoTest.Net/ 150 | 151 | # Web workbench (sass) 152 | .sass-cache/ 153 | 154 | # Installshield output folder 155 | [Ee]xpress/ 156 | 157 | # DocProject is a documentation generator add-in 158 | DocProject/buildhelp/ 159 | DocProject/Help/*.HxT 160 | DocProject/Help/*.HxC 161 | DocProject/Help/*.hhc 162 | DocProject/Help/*.hhk 163 | DocProject/Help/*.hhp 164 | DocProject/Help/Html2 165 | DocProject/Help/html 166 | 167 | # Click-Once directory 168 | publish/ 169 | 170 | # Publish Web Output 171 | *.[Pp]ublish.xml 172 | *.azurePubxml 173 | # Note: Comment the next line if you want to checkin your web deploy settings, 174 | # but database connection strings (with potential passwords) will be unencrypted 175 | *.pubxml 176 | *.publishproj 177 | 178 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 179 | # checkin your Azure Web App publish settings, but sensitive information contained 180 | # in these scripts will be unencrypted 181 | PublishScripts/ 182 | 183 | # NuGet Packages 184 | *.nupkg 185 | # The packages folder can be ignored because of Package Restore 186 | **/[Pp]ackages/* 187 | # except build/, which is used as an MSBuild target. 188 | !**/[Pp]ackages/build/ 189 | # Uncomment if necessary however generally it will be regenerated when needed 190 | #!**/[Pp]ackages/repositories.config 191 | # NuGet v3's project.json files produces more ignorable files 192 | *.nuget.props 193 | *.nuget.targets 194 | 195 | # Microsoft Azure Build Output 196 | csx/ 197 | *.build.csdef 198 | 199 | # Microsoft Azure Emulator 200 | ecf/ 201 | rcf/ 202 | 203 | # Windows Store app package directories and files 204 | AppPackages/ 205 | BundleArtifacts/ 206 | Package.StoreAssociation.xml 207 | _pkginfo.txt 208 | *.appx 209 | 210 | # Visual Studio cache files 211 | # files ending in .cache can be ignored 212 | *.[Cc]ache 213 | # but keep track of directories ending in .cache 214 | !?*.[Cc]ache/ 215 | 216 | # Others 217 | ClientBin/ 218 | ~$* 219 | *~ 220 | *.dbmdl 221 | *.dbproj.schemaview 222 | *.jfm 223 | *.pfx 224 | *.publishsettings 225 | orleans.codegen.cs 226 | 227 | # Including strong name files can present a security risk 228 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 229 | #*.snk 230 | 231 | # Since there are multiple workflows, uncomment next line to ignore bower_components 232 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 233 | #bower_components/ 234 | # ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true 235 | **/wwwroot/lib/ 236 | 237 | # RIA/Silverlight projects 238 | Generated_Code/ 239 | 240 | # Backup & report files from converting an old project file 241 | # to a newer Visual Studio version. Backup files are not needed, 242 | # because we have git ;-) 243 | _UpgradeReport_Files/ 244 | Backup*/ 245 | UpgradeLog*.XML 246 | UpgradeLog*.htm 247 | ServiceFabricBackup/ 248 | *.rptproj.bak 249 | 250 | # SQL Server files 251 | *.mdf 252 | *.ldf 253 | *.ndf 254 | 255 | # Business Intelligence projects 256 | *.rdl.data 257 | *.bim.layout 258 | *.bim_*.settings 259 | *.rptproj.rsuser 260 | 261 | # Microsoft Fakes 262 | FakesAssemblies/ 263 | 264 | # GhostDoc plugin setting file 265 | *.GhostDoc.xml 266 | 267 | # Node.js Tools for Visual Studio 268 | .ntvs_analysis.dat 269 | node_modules/ 270 | 271 | # Visual Studio 6 build log 272 | *.plg 273 | 274 | # Visual Studio 6 workspace options file 275 | *.opt 276 | 277 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 278 | *.vbw 279 | 280 | # Visual Studio LightSwitch build output 281 | **/*.HTMLClient/GeneratedArtifacts 282 | **/*.DesktopClient/GeneratedArtifacts 283 | **/*.DesktopClient/ModelManifest.xml 284 | **/*.Server/GeneratedArtifacts 285 | **/*.Server/ModelManifest.xml 286 | _Pvt_Extensions 287 | 288 | # Paket dependency manager 289 | .paket/paket.exe 290 | paket-files/ 291 | 292 | # FAKE - F# Make 293 | .fake/ 294 | 295 | # JetBrains Rider 296 | .idea/ 297 | *.sln.iml 298 | 299 | # CodeRush personal settings 300 | .cr/personal 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Local History for Visual Studio 338 | .localhistory/ 339 | 340 | # BeatPulse healthcheck temp database 341 | healthchecksdb 342 | -------------------------------------------------------------------------------- /OpenFont/Tables.Variations/HVar.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2019-present, WinterDev 2 | using System; 3 | using System.IO; 4 | 5 | namespace Typography.OpenFont.Tables 6 | { 7 | 8 | //https://docs.microsoft.com/en-us/typography/opentype/spec/hvar 9 | 10 | /// 11 | /// HVAR — Horizontal Metrics Variations Table 12 | /// 13 | class HVar : TableEntry 14 | { 15 | public const string _N = "HVAR"; 16 | public override string Name => _N; 17 | 18 | 19 | internal ItemVariationStoreTable _itemVartionStore; 20 | internal DeltaSetIndexMap[] _advanceWidthMapping; 21 | internal DeltaSetIndexMap[] _lsbMapping; 22 | internal DeltaSetIndexMap[] _rsbMapping; 23 | 24 | public HVar() 25 | { 26 | //The HVAR table is used in variable fonts to provide variations for horizontal glyph metrics values. 27 | //This can be used to provide variation data for advance widths in the 'hmtx' table. 28 | //In fonts with TrueType outlines, it can also be used to provide variation data for left and right side 29 | //bearings obtained from the 'hmtx' table and glyph bounding box. 30 | } 31 | protected override void ReadContentFrom(BinaryReader reader) 32 | { 33 | long beginAt = reader.BaseStream.Position; 34 | 35 | //Horizontal metrics variations table: 36 | //Type Name Description 37 | //uint16 majorVersion Major version number of the horizontal metrics variations table — set to 1. 38 | //uint16 minorVersion Minor version number of the horizontal metrics variations table — set to 0. 39 | //Offset32 itemVariationStoreOffset Offset in bytes from the start of this table to the item variation store table. 40 | //Offset32 advanceWidthMappingOffset Offset in bytes from the start of this table to the delta-set index mapping for advance widths (may be NULL). 41 | //Offset32 lsbMappingOffset Offset in bytes from the start of this table to the delta-set index mapping for left side bearings(may be NULL). 42 | //Offset32 rsbMappingOffset Offset in bytes from the start of this table to the delta-set index mapping for right side bearings(may be NULL). 43 | 44 | ushort majorVersion = reader.ReadUInt16(); 45 | ushort minorVersion = reader.ReadUInt16(); 46 | uint itemVariationStoreOffset = reader.ReadUInt32(); 47 | uint advanceWidthMappingOffset = reader.ReadUInt32(); 48 | uint lsbMappingOffset = reader.ReadUInt32(); 49 | uint rsbMappingOffset = reader.ReadUInt32(); 50 | // 51 | 52 | //itemVariationStore 53 | reader.BaseStream.Position = beginAt + itemVariationStoreOffset; 54 | _itemVartionStore = new ItemVariationStoreTable(); 55 | _itemVartionStore.ReadContentFrom(reader); 56 | 57 | //----------------------------------------- 58 | if (advanceWidthMappingOffset > 0) 59 | { 60 | reader.BaseStream.Position = beginAt + advanceWidthMappingOffset; 61 | _advanceWidthMapping = ReadDeltaSetIndexMapTable(reader); 62 | } 63 | if (lsbMappingOffset > 0) 64 | { 65 | reader.BaseStream.Position = beginAt + lsbMappingOffset; 66 | _lsbMapping = ReadDeltaSetIndexMapTable(reader); 67 | } 68 | if (rsbMappingOffset > 0) 69 | { 70 | reader.BaseStream.Position = beginAt + rsbMappingOffset; 71 | _rsbMapping = ReadDeltaSetIndexMapTable(reader); 72 | } 73 | } 74 | 75 | const int INNER_INDEX_BIT_COUNT_MASK = 0x000F; 76 | const int MAP_ENTRY_SIZE_MASK = 0x0030; 77 | const int MAP_ENTRY_SIZE_SHIFT = 4; 78 | 79 | public readonly struct DeltaSetIndexMap 80 | { 81 | public readonly ushort innerIndex; 82 | public readonly ushort outerIndex; 83 | 84 | public DeltaSetIndexMap(ushort innerIndex, ushort outerIndex) 85 | { 86 | this.innerIndex = innerIndex; 87 | this.outerIndex = outerIndex; 88 | } 89 | } 90 | 91 | static DeltaSetIndexMap[] ReadDeltaSetIndexMapTable(BinaryReader reader) 92 | { 93 | 94 | 95 | //DeltaSetIndexMap table: 96 | //Table 2 97 | //Type Name Description 98 | //uint16 entryFormat A packed field that describes the compressed representation of delta-set indices. See details below. 99 | //uint16 mapCount The number of mapping entries. 100 | //uint8 mapData[variable] The delta-set index mapping data. See details below. 101 | 102 | ushort entryFormat = reader.ReadUInt16(); 103 | ushort mapCount = reader.ReadUInt16(); 104 | 105 | //The mapCount field indicates the number of delta-set index mapping entries. 106 | //Glyph IDs are used as the index into the mapping array. 107 | //If a given glyph ID is greater than mapCount - 1, then the last entry is used. 108 | 109 | //Each mapping entry represents a delta-set outer-level index and inner-level index combination. 110 | //Logically, each of these indices is a 16-bit, unsigned value. 111 | //These are represented in a packed format that uses one, two, three or four bytes. 112 | //The entryFormat field is a packed bitfield that describes the compressed representation used in 113 | //the mapData field of the given deltaSetIndexMap table. 114 | //The format of the entryFormat field is as follows: 115 | 116 | //EntryFormat Field Masks 117 | //Table 3 118 | //Mask Name Description 119 | //0x000F INNER_INDEX_BIT_COUNT_MASK Mask for the low 4 bits, which give the count of bits minus one that are used in each entry for the inner-level index. 120 | //0x0030 MAP_ENTRY_SIZE_MASK Mask for bits that indicate the size in bytes minus one of each entry. 121 | //0xFFC0 Reserved Reserved for future use — set to 0. 122 | 123 | 124 | //see also: afdko\c\public\lib\source\varread\varread.c (Apache2) 125 | 126 | int entrySize = ((entryFormat & MAP_ENTRY_SIZE_MASK) >> MAP_ENTRY_SIZE_SHIFT) + 1; 127 | int innerIndexEntryMask = (1 << ((entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1)) - 1; 128 | int outerIndexEntryShift = (entryFormat & INNER_INDEX_BIT_COUNT_MASK) + 1; 129 | 130 | int mapDataSize = mapCount * entrySize; 131 | 132 | DeltaSetIndexMap[] deltaSetIndexMaps = new DeltaSetIndexMap[mapCount]; 133 | 134 | for (int i = 0; i < mapCount; ++i) 135 | { 136 | int entry; 137 | switch (entrySize) 138 | { 139 | default: throw new NotSupportedException(); 140 | case 1: entry = reader.ReadByte(); break; 141 | case 2: entry = (reader.ReadByte() << 8) | reader.ReadByte(); break; 142 | case 3: entry = (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(); break; 143 | case 4: entry = (reader.ReadByte() << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(); break; 144 | } 145 | //*** 146 | deltaSetIndexMaps[i] = new DeltaSetIndexMap((ushort)(entry & innerIndexEntryMask), (ushort)(entry >> outerIndexEntryShift)); 147 | } 148 | 149 | return deltaSetIndexMaps; 150 | } 151 | 152 | } 153 | } -------------------------------------------------------------------------------- /OpenFont/Tables/NameEntry.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System.IO; 5 | using System.Text; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | //https://docs.microsoft.com/en-us/typography/opentype/spec/name 9 | 10 | public class NameEntry : TableEntry 11 | { 12 | public const string _N = "name"; 13 | public override string Name => _N; 14 | // 15 | protected override void ReadContentFrom(BinaryReader reader) 16 | { 17 | 18 | ushort uFSelector = reader.ReadUInt16(); 19 | ushort uNRCount = reader.ReadUInt16(); 20 | ushort uStorageOffset = reader.ReadUInt16(); 21 | 22 | uint offset = this.Header.Offset; 23 | for (int j = 0; j <= uNRCount; j++) 24 | { 25 | var ttRecord = new TT_NAME_RECORD() 26 | { 27 | uPlatformID = reader.ReadUInt16(), 28 | uEncodingID = reader.ReadUInt16(), 29 | uLanguageID = reader.ReadUInt16(), 30 | uNameID = reader.ReadUInt16(), 31 | uStringLength = reader.ReadUInt16(), 32 | uStringOffset = reader.ReadUInt16(), 33 | }; 34 | 35 | 36 | long nPos = reader.BaseStream.Position; 37 | reader.BaseStream.Seek(offset + ttRecord.uStringOffset + uStorageOffset, SeekOrigin.Begin); 38 | 39 | byte[] buf = reader.ReadBytes(ttRecord.uStringLength); 40 | Encoding enc2; 41 | if (ttRecord.uEncodingID == 3 || ttRecord.uEncodingID == 1) 42 | { 43 | 44 | enc2 = Encoding.BigEndianUnicode; 45 | } 46 | else 47 | { 48 | enc2 = Encoding.UTF8; 49 | } 50 | string strRet = enc2.GetString(buf, 0, buf.Length); 51 | //.... 52 | switch ((NameIdKind)ttRecord.uNameID) 53 | { 54 | default: 55 | //skip 56 | break; 57 | case NameIdKind.VersionString: 58 | VersionString = strRet; 59 | break; 60 | case NameIdKind.FontFamilyName: 61 | FontName = strRet; 62 | break; 63 | case NameIdKind.FontSubfamilyName: 64 | FontSubFamily = strRet; 65 | break; 66 | case NameIdKind.UniqueFontIden: 67 | UniqueFontIden = strRet; 68 | break; 69 | case NameIdKind.FullFontName: 70 | FullFontName = strRet; 71 | break; 72 | // 73 | case NameIdKind.PostScriptName: 74 | PostScriptName = strRet; 75 | break; 76 | case NameIdKind.PostScriptCID_FindfontName: 77 | PostScriptCID_FindfontName = strRet; 78 | break; 79 | // 80 | case NameIdKind.TypographicFamilyName: 81 | TypographicFamilyName = strRet; 82 | break; 83 | case NameIdKind.TypographyicSubfamilyName: 84 | TypographyicSubfamilyName = strRet; 85 | break; 86 | 87 | } 88 | //move to saved pos 89 | reader.BaseStream.Seek(nPos, SeekOrigin.Begin); 90 | } 91 | } 92 | 93 | 94 | /// 95 | /// Font Family name. 96 | /// This family name is assumed to be shared among fonts that 97 | /// differ only in weight or style (italic, oblique). 98 | /// 99 | /// Font Family name is used in combination with Font Subfamily name (name ID 2)... 100 | /// 101 | public string FontName { get; private set; } 102 | /// 103 | /// Font Subfamily name. The Font Subfamily name distinguishes the fonts in a group with the 104 | /// same Font Family name (name ID 1). 105 | /// This is assumed to address style (italic, oblique) and weight variants only. 106 | /// 107 | /// A font with no distinctive weight or style (e.g. medium weight, not italic, and OS/2.fsSelection bit 6 set) 108 | /// should use the string “Regular” as the Font Subfamily name (for English language). 109 | /// 110 | public string FontSubFamily { get; private set; } 111 | public string UniqueFontIden { get; private set; } 112 | /// 113 | /// Full font name that reflects all family and relevant subfamily descriptors. 114 | /// The full font name is generally a combination of name IDs 1 and 2, or 115 | /// of name IDs 16 and 17, or a similar human-readable variant. 116 | /// 117 | public string FullFontName { get; set; } 118 | 119 | public string VersionString { get; set; } 120 | 121 | /// 122 | /// PostScript name for the font; Name ID 6 specifies a string which is used to invoke a PostScript language font that corresponds to this OpenType font. 123 | /// When translated to ASCII, the name string must be no longer than 63 characters and restricted to the printable ASCII subset, 124 | /// codes 33 to 126, except for the 10 characters '[', ']', '(', ')', '{', '}', '<', '>', '/', '%'. 125 | /// 126 | ///In a CFF OpenType font, there is no requirement that this name be the same as the font name in the CFF’s Name INDEX.Thus, 127 | ///the same CFF may be shared among multiple font components in a Font Collection. 128 | ///... 129 | /// 130 | public string PostScriptName { get; set; } 131 | public string PostScriptCID_FindfontName { get; set; } 132 | // 133 | public string TypographicFamilyName { get; set; } 134 | public string TypographyicSubfamilyName { get; set; } 135 | 136 | 137 | struct TT_NAME_RECORD 138 | { 139 | public ushort uPlatformID; 140 | public ushort uEncodingID; 141 | public ushort uLanguageID; 142 | public ushort uNameID; 143 | public ushort uStringLength; 144 | public ushort uStringOffset; 145 | } 146 | 147 | 148 | 149 | enum NameIdKind 150 | { 151 | //... 152 | //[A] The key information for this table for Microsoft platforms 153 | //relates to the use of strings 1, 2, 4, 16 and 17. 154 | //... 155 | 156 | 157 | CopyRightNotice, //0 158 | FontFamilyName, //1 , [A] 159 | FontSubfamilyName,//2, [A] 160 | UniqueFontIden, //3 161 | FullFontName, //4, [A] 162 | VersionString,//5 163 | PostScriptName,//6 164 | Trademark,//7 165 | ManufacturerName,//8 166 | Designer,//9 167 | Description, //10 168 | UrlVendor, //11 169 | UrlDesigner,//12 170 | LicenseDescription, //13 171 | LicenseInfoUrl,//14 172 | Reserved,//15 173 | TypographicFamilyName,//16 , [A] 174 | TypographyicSubfamilyName,//17, [A] 175 | CompatibleFull,//18 176 | SampleText,//19 177 | PostScriptCID_FindfontName,//20 178 | //------------------ 179 | WWSFamilyName,//21 180 | WWSSubfamilyName,//22 181 | //------------------ 182 | LightBackgroundPalette,//23, CPAL 183 | DarkBackgroundPalette,//24, CPAL 184 | //------------------ 185 | VariationsPostScriptNamePrefix,//25 186 | 187 | } 188 | 189 | 190 | 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /OpenFont/AdditionalInfo/Languages.cs: -------------------------------------------------------------------------------- 1 | //MIT, 2020-present, WinterDev 2 | using System; 3 | using System.Collections.Generic; 4 | using Typography.OpenFont.Tables; 5 | 6 | namespace Typography.OpenFont 7 | { 8 | /// 9 | /// supported langs, designed langs 10 | /// 11 | public class Languages 12 | { 13 | /// 14 | /// meta table's supported langs 15 | /// 16 | public string[] SupportedLangs { get; private set; } 17 | /// 18 | /// meta table's design langs 19 | /// 20 | public string[] DesignLangs { get; private set; } 21 | 22 | public ushort OS2Version { get; private set; }//for translate unicode ranges 23 | public uint UnicodeRange1 { get; private set; } 24 | public uint UnicodeRange2 { get; private set; } 25 | public uint UnicodeRange3 { get; private set; } 26 | public uint UnicodeRange4 { get; private set; } 27 | 28 | /// 29 | /// actual GSUB script list 30 | /// 31 | public ScriptList GSUBScriptList { get; private set; } 32 | /// 33 | /// actual GPOS script list 34 | /// 35 | public ScriptList GPOSScriptList { get; private set; } 36 | 37 | Cmap _cmap; 38 | 39 | internal void Update(OS2Table os2Tabble, Meta meta, Cmap cmap, GSUB gsub, GPOS gpos) 40 | { 41 | //https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur 42 | 43 | 44 | //This field is used to specify the Unicode blocks or ranges encompassed by the font file in 'cmap' subtables for platform 3, 45 | //encoding ID 1 (Microsoft platform, Unicode BMP) and platform 3, 46 | //encoding ID 10 (Microsoft platform, Unicode full repertoire). 47 | //If a bit is set (1), then the Unicode ranges assigned to that bit are considered functional. 48 | //If the bit is clear (0), then the range is not considered functional. 49 | 50 | //unicode BMP (Basic Multilingual Plane),OR plane0 (see https://unicode.org/roadmaps/bmp/) 51 | 52 | //Each of the bits is treated as an independent flag and the bits can be set in any combination. 53 | //The determination of “functional” is left up to the font designer, 54 | //although character set selection should attempt to be functional by ranges if at all possible. 55 | 56 | //-------------- 57 | //Different versions of the OS/2 table were created when different Unicode versions were current, 58 | //and the initial specification for a given version defined fewer bit assignments than for later versions. 59 | //Some applications may not support all assignments for fonts that have earlier OS/2 versions. 60 | 61 | //All of the bit assignments listed above are valid for any version of the OS/2 table, 62 | //though OS/2 versions 1 and 2 were specified with some assignments that did not correspond to well-defined Unicode ranges and 63 | //that conflict with later assignments — see the details below. 64 | //If a font has a version 1 or version 2 OS/2 table with one of these bits set, 65 | //the obsolete assignment may be the intended interpretation. 66 | //Because these assignments do not correspond to well-defined ranges, 67 | //however, the implied character coverage is unclear. 68 | 69 | //Version 0: When version 0 was first specified, no bit assignments were defined. 70 | //Some applications may ignore these fields in a version 0 OS/2 table. 71 | 72 | //Version 1: 73 | //Version 1 was first specified concurrent with Unicode 1.1, 74 | //and bit assigments were defined for bits 0 to 69 only. With fonts that have a version 1 table, 75 | //some applications might recognize only bits 0 to 69. 76 | 77 | //Also, version 1 was specified with some bit assignments that did not correspond to a well-defined Unicode range: 78 | 79 | // Bit 8: “Greek Symbols and Coptic” (bit 7 was specified as “Basic Greek”) 80 | // Bit 12: “Hebrew Extended” (bit 11 was specified as “Basic Hebrew”) 81 | // Bit 14: “Arabic Extended” (bit 13 was specified as “Basic Arabic”) 82 | // Bit 27: “Georgian Extended” (bit 26 was specified as “Basic Georgian”) 83 | 84 | //These assignments were discontinued as of version 2. 85 | 86 | //In addition, versions 1 and 2 were defined with bit 53 specified as “CJK Miscellaneous”, 87 | //which also does not correspond to any well-defined Unicode range. 88 | //This assignment was discontinued as of version 3. 89 | 90 | //Version 2: 91 | //Version 2 was defined in OpenType 1.1, which was concurrent with Unicode 2.1. 92 | //At that time, bit assignments were defined for bits 0 to 69 only. 93 | //Bit assignments for version 2 were updated in OpenType 1.3, 94 | //adding assignments for bits 70 to 83 corresponding to new blocks assigned in Unicode 2.0 and Unicode 3.0. 95 | //With fonts that have a version 2 table, 96 | //some applications might recognize only those bits assigned in OpenType 1.2 or OpenType 1.3. 97 | 98 | //Also, the specification for version 2 continued to use a problematic assignment for bit 53 — 99 | //see details for version 1. This assignment was discontinued as of version 3. 100 | 101 | //Version 3: Version 3 was defined in OpenType 1.4 with assignments for bits 84 to 91 corresponding to additional 102 | //ranges in Unicode 3.2. 103 | //In addition, some already-assigned bits were extended to cover additional Unicode ranges for related characters; s 104 | //ee details in the table above. 105 | 106 | //Version 4: Version 4 was defined in OpenType 1.5 with assignments for bit 58 and bits 92 to 122 corresponding to additional ranges in Unicode 5.1. 107 | //Also, bits 8, 12, 14, 27 and 53 were re-assigned (see version 1 for previous assignments). 108 | //In addition, some already-assigned bits were extended to cover additional Unicode ranges for related characters; 109 | //see details in the table above. 110 | 111 | OS2Version = os2Tabble.version; 112 | UnicodeRange1 = os2Tabble.ulUnicodeRange1; 113 | UnicodeRange2 = os2Tabble.ulUnicodeRange2; 114 | UnicodeRange3 = os2Tabble.ulUnicodeRange3; 115 | UnicodeRange4 = os2Tabble.ulUnicodeRange4; 116 | //ULONG ulUnicodeRange1 Bits 0-31 117 | //ULONG ulUnicodeRange2 Bits 32-63 118 | //ULONG ulUnicodeRange3 Bits 64-95 119 | //ULONG ulUnicodeRange4 Bits 96-127 120 | 121 | 122 | 123 | //------- 124 | //IMPORTANT:*** 125 | //All available bits were exhausted as of Unicode 5.1. *** 126 | //The bit assignments were last updated for OS/2 version 4 in OpenType 1.5. 127 | //There are many additional ranges supported in the current version of Unicode that are not supported by these fields in the OS/2 table. 128 | // 129 | //See the 'dlng' and 'slng' tags in the 'meta' table for an alternate mechanism to declare 130 | //what scripts or languages that a font can support or is designed for. 131 | //------- 132 | 133 | 134 | if (meta != null) 135 | { 136 | SupportedLangs = meta.SupportedLanguageTags; 137 | DesignLangs = meta.DesignLanguageTags; 138 | } 139 | 140 | //---- 141 | //gsub and gpos contains actual script_list that are in the typeface 142 | GSUBScriptList = gsub?.ScriptList; 143 | GPOSScriptList = gpos?.ScriptList; 144 | 145 | _cmap = cmap; 146 | } 147 | 148 | /// 149 | /// contains glyph for the given unicode code point or not 150 | /// 151 | /// 152 | /// 153 | public bool ContainGlyphForUnicode(int codepoint) => (_cmap != null) ? (_cmap.GetGlyphIndex(codepoint, 0, out bool skipNextCodePoint) > 0) : false; 154 | } 155 | 156 | 157 | } -------------------------------------------------------------------------------- /OpenFont/Tables/Head.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | //Apache2, 2014-2016, Samuel Carlsson, WinterDev 3 | 4 | using System; 5 | using System.IO; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | 9 | class Head : TableEntry 10 | { 11 | 12 | //https://docs.microsoft.com/en-us/typography/opentype/spec/head 13 | //This table gives global information about the font. 14 | //The bounding box values should be computed using only glyphs that have contours. 15 | //Glyphs with no contours should be ignored for the purposes of these calculations. 16 | 17 | public const string _N = "head"; 18 | public override string Name => _N; 19 | // 20 | short _indexToLocFormat; 21 | 22 | public Head() 23 | { 24 | } 25 | protected override void ReadContentFrom(BinaryReader input) 26 | { 27 | 28 | //Type Name Description 29 | //uint16 majorVersion Major version number of the font header table — set to 1. 30 | //uint16 minorVersion Minor version number of the font header table — set to 0. 31 | //Fixed fontRevision Set by font manufacturer. 32 | //uint32 checkSumAdjustment To compute: set it to 0, sum the entire font as uint32, then store 0xB1B0AFBA - sum. 33 | // If the font is used as a component in a font collection file, 34 | // the value of this field will be invalidated by changes to the file structure and font table directory, and must be ignored. 35 | //uint32 magicNumber Set to 0x5F0F3CF5. 36 | //uint16 flags Bit 0: Baseline for font at y=0; 37 | 38 | // Bit 1: Left sidebearing point at x=0 (relevant only for TrueType rasterizers) — see the note below regarding variable fonts; 39 | 40 | // Bit 2: Instructions may depend on point size; 41 | 42 | // Bit 3: Force ppem to integer values for all internal scaler math; may use fractional ppem sizes if this bit is clear; 43 | 44 | // Bit 4: Instructions may alter advance width (the advance widths might not scale linearly); 45 | 46 | // Bit 5: This bit is not used in OpenType, and should not be set in order to ensure compatible behavior on all platforms. If set, it may result in different behavior for vertical layout in some platforms. (See Apple’s specification for details regarding behavior in Apple platforms.) 47 | 48 | // Bits 6–10: These bits are not used in Opentype and should always be cleared. (See Apple’s specification for details regarding legacy used in Apple platforms.) 49 | 50 | // Bit 11: Font data is “lossless” as a result of having been subjected to optimizing transformation and/or compression (such as e.g. compression mechanisms defined by ISO/IEC 14496-18, MicroType Express, WOFF 2.0 or similar) where the original font functionality and features are retained but the binary compatibility between input and output font files is not guaranteed. As a result of the applied transform, the DSIG table may also be invalidated. 51 | 52 | // Bit 12: Font converted (produce compatible metrics) 53 | 54 | // Bit 13: Font optimized for ClearType™. Note, fonts that rely on embedded bitmaps (EBDT) for rendering should not be considered optimized for ClearType, and therefore should keep this bit cleared. 55 | 56 | // Bit 14: Last Resort font. If set, indicates that the glyphs encoded in the 'cmap' subtables are simply generic symbolic representations of code point ranges and don’t truly represent support for those code points. If unset, indicates that the glyphs encoded in the 'cmap' subtables represent proper support for those code points. 57 | 58 | // Bit 15: Reserved, set to 0 59 | //uint16 unitsPerEm Set to a value from 16 to 16384. Any value in this range is valid. 60 | // In fonts that have TrueType outlines, a power of 2 is recommended as this allows performance optimizations in some rasterizers. 61 | //LONGDATETIME created Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone. 64-bit integer 62 | //LONGDATETIME modified Number of seconds since 12:00 midnight that started January 1st 1904 in GMT/UTC time zone. 64-bit integer 63 | //int16 xMin For all glyph bounding boxes. 64 | //int16 yMin For all glyph bounding boxes. 65 | //int16 xMax For all glyph bounding boxes. 66 | //int16 yMax For all glyph bounding boxes. 67 | //uint16 macStyle Bit 0: Bold (if set to 1); 68 | // Bit 1: Italic (if set to 1) 69 | // Bit 2: Underline (if set to 1) 70 | // Bit 3: Outline (if set to 1) 71 | // Bit 4: Shadow (if set to 1) 72 | // Bit 5: Condensed (if set to 1) 73 | // Bit 6: Extended (if set to 1) 74 | // Bits 7–15: Reserved (set to 0). 75 | //uint16 lowestRecPPEM Smallest readable size in pixels. 76 | //int16 fontDirectionHint Deprecated (Set to 2). 77 | // 0: Fully mixed directional glyphs; 78 | // 1: Only strongly left to right; 79 | // 2: Like 1 but also contains neutrals; 80 | // -1: Only strongly right to left; 81 | // -2: Like -1 but also contains neutrals. 82 | 83 | //(A neutral character has no inherent directionality; it is not a character with zero (0) width. Spaces and punctuation are examples of neutral characters. Non-neutral characters are those with inherent directionality. For example, Roman letters (left-to-right) and Arabic letters (right-to-left) have directionality. In a “normal” Roman font where spaces and punctuation are present, the font direction hints should be set to two (2).) 84 | //int16 indexToLocFormat 0 for short offsets (Offset16), 1 for long (Offset32). 85 | //int16 glyphDataFormat 0 for current format. 86 | 87 | 88 | Version = input.ReadUInt32(); // 0x00010000 for version 1.0. 89 | FontRevision = input.ReadUInt32(); 90 | CheckSumAdjustment = input.ReadUInt32(); 91 | MagicNumber = input.ReadUInt32(); 92 | if (MagicNumber != 0x5F0F3CF5) throw new Exception("Invalid magic number!" + MagicNumber.ToString("x")); 93 | 94 | Flags = input.ReadUInt16(); 95 | UnitsPerEm = input.ReadUInt16(); // valid is 16 to 16384 96 | Created = input.ReadUInt64(); // International date (8-byte field). (?) 97 | Modified = input.ReadUInt64(); 98 | // bounding box for all glyphs 99 | Bounds = Utils.ReadBounds(input); 100 | MacStyle = input.ReadUInt16(); 101 | LowestRecPPEM = input.ReadUInt16(); 102 | FontDirectionHint = input.ReadInt16(); 103 | _indexToLocFormat = input.ReadInt16(); // 0 for 16-bit offsets, 1 for 32-bit. 104 | GlyphDataFormat = input.ReadInt16(); // 0 105 | } 106 | 107 | public uint Version { get; private set; } 108 | public uint FontRevision { get; private set; } 109 | public uint CheckSumAdjustment { get; private set; } 110 | public uint MagicNumber { get; private set; } 111 | public ushort Flags { get; private set; } 112 | public ushort UnitsPerEm { get; private set; } 113 | public ulong Created { get; private set; } 114 | public ulong Modified { get; private set; } 115 | public Bounds Bounds { get; private set; } 116 | public ushort MacStyle { get; private set; } 117 | public ushort LowestRecPPEM { get; private set; } 118 | public short FontDirectionHint { get; private set; } 119 | public bool WideGlyphLocations => _indexToLocFormat > 0; 120 | public short GlyphDataFormat { get; private set; } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /OpenFont/Tables.BitmapAndSvgFonts/SvgTable.cs: -------------------------------------------------------------------------------- 1 | //Apache2, 2017-present, WinterDev 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | namespace Typography.OpenFont.Tables 7 | { 8 | class SvgTable : TableEntry 9 | { 10 | public const string _N = "SVG "; //with 1 whitespace *** 11 | public override string Name => _N; 12 | // 13 | //https://docs.microsoft.com/en-us/typography/opentype/spec/svg 14 | //OpenType fonts with either TrueType or CFF outlines may also contain an optional 'SVG ' table, 15 | //which allows some or all glyphs in the font to be defined with color, gradients, or animation. 16 | 17 | 18 | Dictionary _dicSvgEntries; 19 | SvgDocumentEntry[] _entries; //TODO: review again 20 | protected override void ReadContentFrom(BinaryReader reader) 21 | { 22 | long svgTableStartAt = reader.BaseStream.Position; 23 | //SVG Main Header 24 | //Type Name Description 25 | //uint16 version Table version(starting at 0). Set to 0. 26 | //Offset32 svgDocIndexOffset Offset(relative to the start of the SVG table) to the SVG Documents Index.Must be non - zero. 27 | //uint32 reserved Set to 0. 28 | //----------- 29 | ushort version = reader.ReadUInt16(); 30 | uint offset32 = reader.ReadUInt32(); 31 | uint reserved = reader.ReadUInt32(); 32 | //------- 33 | 34 | 35 | //------- 36 | //SVG Document Index 37 | //The SVG Document Index is a set of SVG documents, each of which defines one or more glyph descriptions. 38 | //Type Name Description 39 | //uint16 numEntries Number of SVG Document Index Entries.Must be non - zero. 40 | //SVG Document Index Entry entries[numEntries] Array of SVG Document Index Entries. 41 | // 42 | long svgDocIndexStartAt = svgTableStartAt + offset32; 43 | reader.BaseStream.Seek(svgDocIndexStartAt, SeekOrigin.Begin); 44 | // 45 | ushort numEntries = reader.ReadUInt16(); 46 | // 47 | //SVG Document Index Entry 48 | //Each SVG Document Index Entry specifies a range[startGlyphID, endGlyphID], inclusive, 49 | //of glyph IDs and the location of its associated SVG document in the SVG table. 50 | //Type Name Description 51 | //uint16 startGlyphID The first glyph ID in the range described by this index entry. 52 | //uint16 endGlyphID The last glyph ID in the range described by this index entry. Must be >= startGlyphID. 53 | //Offset32 svgDocOffset Offset from the beginning of the SVG Document Index to an SVG document.Must be non - zero. 54 | //uint32 svgDocLength Length of the SVG document.Must be non - zero. 55 | 56 | //Index entries must be arranged in order of increasing startGlyphID. 57 | // 58 | //...this specification requires that the SVG documents be either plain-text or gzip-encoded [RFC1952]. 59 | //The encoding of the (uncompressed) SVG document must be UTF-8. 60 | //In both cases, svgDocLength encodes the length of the encoded data, not the decoded document. 61 | 62 | _entries = new SvgDocumentEntry[numEntries]; 63 | for (int i = 0; i < numEntries; ++i) 64 | { 65 | _entries[i] = new SvgDocumentEntry() 66 | { 67 | startGlyphID = reader.ReadUInt16(), 68 | endGlyphID = reader.ReadUInt16(), 69 | svgDocOffset = reader.ReadUInt32(), 70 | svgDocLength = reader.ReadUInt32() 71 | }; 72 | } 73 | 74 | //TODO: review lazy load 75 | for (int i = 0; i < numEntries; ++i) 76 | { 77 | //read data 78 | SvgDocumentEntry entry = _entries[i]; 79 | 80 | if (entry.endGlyphID - entry.startGlyphID > 0) 81 | { 82 | //TODO review here again 83 | throw new System.NotSupportedException(); 84 | } 85 | 86 | reader.BaseStream.Seek(svgDocIndexStartAt + entry.svgDocOffset, SeekOrigin.Begin); 87 | 88 | if (entry.svgDocLength == 0) 89 | { 90 | throw new System.NotSupportedException(); 91 | } 92 | 93 | // 94 | byte[] svgData = reader.ReadBytes((int)entry.svgDocLength); 95 | _entries[i].svgBuffer = svgData; 96 | 97 | #if DEBUG 98 | if (svgData.Length == 0) { throw new NotSupportedException(); } 99 | #endif 100 | 101 | if (svgData[0] == (byte)'<') 102 | { 103 | //should be plain-text 104 | #if DEBUG 105 | //string svgDataString = System.Text.Encoding.UTF8.GetString(svgData); 106 | //dbugSaveAsHtml("svg" + i + ".html", svgDataString); 107 | #endif 108 | } 109 | else 110 | { 111 | _entries[i].compressed = true; 112 | } 113 | } 114 | } 115 | 116 | public void UnloadSvgData() 117 | { 118 | if (_dicSvgEntries != null) 119 | { 120 | _dicSvgEntries.Clear(); 121 | _dicSvgEntries = null; 122 | } 123 | 124 | _entries = null; 125 | } 126 | 127 | public bool ReadSvgContent(ushort glyphIndex, System.Text.StringBuilder outputStBuilder) 128 | { 129 | if (_dicSvgEntries == null) 130 | { 131 | //create index 132 | _dicSvgEntries = new Dictionary(); 133 | for (int i = 0; i < _entries.Length; ++i) 134 | { 135 | _dicSvgEntries.Add(_entries[i].startGlyphID, i); 136 | } 137 | } 138 | 139 | if (_dicSvgEntries.TryGetValue(glyphIndex, out int entryIndex)) 140 | { 141 | SvgDocumentEntry docEntry = _entries[entryIndex]; 142 | 143 | if (docEntry.compressed) 144 | { 145 | throw new NotSupportedException(); 146 | //TODO: decompress this 147 | } 148 | 149 | outputStBuilder.Append(System.Text.Encoding.UTF8.GetString(docEntry.svgBuffer)); 150 | return true; 151 | } 152 | return false; 153 | } 154 | #if DEBUG 155 | static void dbugSaveAsHtml(string filename, string originalGlyphSvg) 156 | { 157 | //xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" 158 | //test save the svg 159 | //save as html document for test 160 | System.Text.StringBuilder stbuilder = new System.Text.StringBuilder(); 161 | stbuilder.Append(""); 162 | 163 | //TODO: add exact SVG reader here 164 | //to view in WebBrowser -> we do Y-flip 165 | string modified = originalGlyphSvg.Replace("xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\">", 166 | "xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"800\" height=\"1600\">" 167 | ).Replace("", ""); 168 | stbuilder.Append(modified); 169 | stbuilder.Append(""); 170 | File.WriteAllText(filename, stbuilder.ToString()); 171 | } 172 | #endif 173 | struct SvgDocumentEntry 174 | { 175 | public ushort startGlyphID; 176 | public ushort endGlyphID; 177 | public uint svgDocOffset; 178 | public uint svgDocLength; 179 | 180 | public byte[] svgBuffer; 181 | public bool compressed; 182 | 183 | #if DEBUG 184 | public override string ToString() 185 | { 186 | return "startGlyphID:" + startGlyphID + "," + 187 | "endGlyphID:" + endGlyphID + "," + 188 | "svgDocOffset:" + svgDocOffset + "," + 189 | "svgDocLength:" + svgDocLength; 190 | } 191 | #endif 192 | } 193 | 194 | } 195 | } --------------------------------------------------------------------------------