using System; using System.Collections; using System.Collections.Generic; using System.Runtime.InteropServices; using FastReport.Fonts.LinqExts; #pragma warning disable CS3001, CS3002, CS3003, CS1591 // Missing XML comment for publicly visible type or member namespace FastReport.Fonts { /// /// GlyphSubstitution table /// public class GlyphSubstitutionClass : TrueTypeTable { #region "Structure definition" [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct GSUB_Header { [FieldOffset(0)] public uint Version; // Version of the GSUB table-initially set to 0x00010000 [FieldOffset(4)] public ushort ScriptList; // Offset to ScriptList table-from beginning of GSUB table [FieldOffset(6)] public ushort FeatureList; // Offset to FeatureList table-from beginning of GSUB table [FieldOffset(8)] public ushort LookupList; // Offset to LookupList table-from beginning of GSUB table } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct ScriptListTable { [FieldOffset(0)] public ushort CountScripts; // Count of ScriptListRecord } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct ScriptListRecord { [FieldOffset(0)] public uint ScriptTag; // 4-byte ScriptTag identifier [FieldOffset(4)] public ushort ScriptOffset; // Offset to Script table-from beginning of ScriptList } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct ScriptTable { [FieldOffset(0)] public ushort DefaultLangSysOffset; // Offset to DefaultLangSys table-from beginning of Script table-may be NULL [FieldOffset(2)] public ushort LangSysCount; // Number of LangSysRecords for this script-excluding the DefaultLangSys } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct LangSysRecord { [FieldOffset(0)] public uint LangSysTag; // 4-byte LangSysTag identifier [FieldOffset(4)] public ushort LangSysOffset; // Offset to LangSys table-from beginning of Script table } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct LangSysTable { [FieldOffset(0)] public ushort LookupOrder; // = NULL (reserved for an offset to a reordering table) [FieldOffset(2)] public ushort ReqFeatureIndex; // Index of a feature required for this language system- if no required features = 0xFFFF [FieldOffset(4)] public ushort FeatureCount; // Number of FeatureIndex values for this language system-excludes the required feature } // Related to feature table [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct FeaturesListTable { [FieldOffset(0)] public ushort CountFeatures; // Count of FeaturesListRecord } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct FeatureRecord { [FieldOffset(0)] public uint FeatureTag; // 4-byte feature identification tag [FieldOffset(4)] public ushort Feature; // Offset to Feature table-from beginning of FeatureList } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct FeatureTable { [FieldOffset(0)] public ushort FeatureParams; [FieldOffset(2)] public ushort LookupCount; } #endregion private GSUB_Header header; private uint featureVariationsOffset; private IntPtr gsub_ptr; private Hashtable script_list = new Hashtable(); private LookupEntry[] lookup_list; public IEnumerable Scripts { get { foreach (string script_str in script_list.Keys) yield return script_str; } } private ushort[] LoadFeature(FontStream stream, uint feature_idx, out string FeatureTag) { //IntPtr feature_list_table_ptr = Increment(gsub_ptr, (int)header.FeatureList); stream.Position = this.Offset + header.FeatureList; ushort feature_count = stream.ReadUInt16();// nSwapUInt16((ushort)Marshal.PtrToStructure(feature_list_table_ptr, typeof(ushort))); if (feature_idx >= feature_count) throw new Exception("Feature index out of bound"); long feature_list_pos = stream.Position; // IntPtr feature_record_ptr = Increment(feature_list_table_ptr, (int)(sizeof(ushort) + feature_idx * 6)); stream.Position = feature_list_pos + feature_idx * 6; FeatureRecord feature_record = new FeatureRecord(); // Marshal.PtrToStructure(feature_record_ptr, typeof(FeatureRecord)); feature_record.FeatureTag = stream.ReadUInt32(); feature_record.Feature = stream.ReadUInt16(); // SwapUInt16(feature_record.Feature); FeatureTag = "" + (char)(0xff & (feature_record.FeatureTag >> 24)) + (char)(0xff & (feature_record.FeatureTag >> 16)) + (char)(0xff & (feature_record.FeatureTag >> 8)) + (char)(0xff & feature_record.FeatureTag); // IntPtr feature_table_ptr = Increment(feature_list_table_ptr, feature_record.Feature); stream.Position = this.Offset + header.FeatureList + feature_record.Feature; FeatureTable feature_table = new FeatureTable(); // Marshal.PtrToStructure(feature_table_ptr, typeof(FeatureTable)); feature_table.FeatureParams = stream.ReadUInt16(); feature_table.LookupCount = stream.ReadUInt16(); // SwapUInt16(feature_table.LookupCount); // IntPtr lookup_list_ptr = Increment(feature_table_ptr, Marshal.SizeOf(feature_table)); ushort[] OffsetLookupList = new ushort[feature_table.LookupCount]; for (int i = 0; i < feature_table.LookupCount; i++) { ushort lookuip_index = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(lookup_list_ptr, typeof(ushort))); OffsetLookupList[i] = lookuip_index; if (lookuip_index == 30) { //to do remove } // lookup_list_ptr = Increment(lookup_list_ptr, sizeof(ushort)); } return OffsetLookupList; } internal IEnumerable GetFeatures(string script, string language) { if (script_list.ContainsKey(script)) if ((script_list[script] as Hashtable).ContainsKey(language)) { foreach (string feature_str in ((script_list[script] as Hashtable)[language] as Hashtable).Keys) yield return feature_str; } } internal IEnumerable Languages(string script) { if (script_list.ContainsKey(script)) { foreach (string lang_str in (script_list[script] as Hashtable).Keys) yield return lang_str; } } private Hashtable LoadLanguageSystemTable(FontStream stream /*IntPtr lang_sys_rec_ptr*/) { Hashtable Features = new Hashtable(); LangSysTable lang_sys_table = new LangSysTable(); // Marshal.PtrToStructure(lang_sys_rec_ptr, typeof(LangSysTable)); lang_sys_table.LookupOrder = stream.ReadUInt16(); // SwapUInt16(lang_sys_table.LookupOrder); lang_sys_table.ReqFeatureIndex = stream.ReadUInt16(); // SwapUInt16(lang_sys_table.ReqFeatureIndex); lang_sys_table.FeatureCount = stream.ReadUInt16();// SwapUInt16(lang_sys_table.FeatureCount); // IntPtr feature_index_ptr = Increment(lang_sys_rec_ptr, Marshal.SizeOf(lang_sys_table)); ushort[] feature_indexes = new ushort[lang_sys_table.FeatureCount]; for (int k = 0; k < lang_sys_table.FeatureCount; k++) feature_indexes[k] = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(feature_index_ptr, typeof(ushort))); for (int k = 0; k < lang_sys_table.FeatureCount; k++) { string FeatureTag; ushort[] LookupOffsets = LoadFeature(stream, feature_indexes[k], out FeatureTag); #if DEBUG_TTF Console.WriteLine("\t\t[" + k + "]: " + FeatureTag + " of " + LookupOffsets.Length); #endif if (!Features.ContainsKey(FeatureTag)) Features.Add(FeatureTag, LookupOffsets); #if DEBUG_TTF else Console.WriteLine("Duplicated record " + FeatureTag); #endif // feature_index_ptr = Increment(feature_index_ptr, sizeof(ushort)); } return Features; } private void LoadScriptList(FontStream stream) { //IntPtr script_list_table_ptr = Increment(gsub_ptr, (int)header.ScriptList); long ls_pos = stream.Position = this.Offset + header.ScriptList; ScriptListTable script_list_table = new ScriptListTable(); // Marshal.PtrToStructure(script_list_table_ptr, typeof(ScriptListTable)); script_list_table.CountScripts = stream.ReadUInt16(); ScriptListRecord[] script_record = new ScriptListRecord[script_list_table.CountScripts]; for (int i = 0; i < script_list_table.CountScripts; i++) { script_record[i].ScriptTag = stream.ReadUInt32(); script_record[i].ScriptOffset = stream.ReadUInt16(); // SwapUInt16(script_record.ScriptOffset); } for (int i = 0; i < script_list_table.CountScripts; i++) { string ScriptTag = "" + (char)(0xff & (script_record[i].ScriptTag >> 24)) + (char)(0xff & (script_record[i].ScriptTag >> 16)) + (char)(0xff & (script_record[i].ScriptTag >> 8)) + (char)(0xff & script_record[i].ScriptTag); #if DEBUG_TTF Console.WriteLine("[" + ScriptTag + "]"); #endif Hashtable lang_sys_hash = new Hashtable(); script_list.Add(ScriptTag, lang_sys_hash); stream.Position = ls_pos + script_record[i].ScriptOffset; ScriptTable script_table = new ScriptTable(); // Marshal.PtrToStructure(script_table_ptr, typeof(ScriptTable)); script_table.DefaultLangSysOffset = stream.ReadUInt16(); // SwapUInt16(script_table.DefaultLangSys); script_table.LangSysCount = stream.ReadUInt16(); // SwapUInt16(script_table.LangSysCount); LangSysRecord[] lang_sys_rec = new LangSysRecord[script_table.LangSysCount]; for (int j = 0; j < script_table.LangSysCount; j++) { lang_sys_rec[j].LangSysTag = stream.ReadUInt32(); lang_sys_rec[j].LangSysOffset = stream.ReadUInt16(); // SwapUInt16(lang_sys_rec.LangSys); } for (int j = 0; j < script_table.LangSysCount; j++) { string LangSysTag = "" + (char)(0xff & (lang_sys_rec[j].LangSysTag >> 24)) + (char)(0xff & (lang_sys_rec[j].LangSysTag >> 16)) + (char)(0xff & (lang_sys_rec[j].LangSysTag >> 8)) + (char)(0xff & lang_sys_rec[j].LangSysTag); #if DEBUG_TTF Console.WriteLine("\t\"" + LangSysTag + "\""); #endif stream.Position = ls_pos + script_record[i].ScriptOffset + lang_sys_rec[j].LangSysOffset; lang_sys_hash.Add(LangSysTag, LoadLanguageSystemTable(stream)); } if (script_table.DefaultLangSysOffset != 0) { // lang_sys_rec_ptr = Increment(script_table_ptr, script_table.DefaultLangSys); stream.Position = ls_pos + script_record[i].ScriptOffset + script_table.DefaultLangSysOffset; #if DEBUG_TTF Console.WriteLine("\t\"!DEF\""); #endif lang_sys_hash.Add("", LoadLanguageSystemTable(stream /*lang_sys_rec_ptr*/)); } } } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct LookupTableRecordHeader { [FieldOffset(0)] public ushort LookupType; // Different enumerations for GSUB and GPOS [FieldOffset(2)] public ushort LookupFlag; // Lookup qualifiers [FieldOffset(4)] public ushort SubTableCount; // Number of SubTables for this lookup [FieldOffset(2)] } internal struct LookupEntry { public LookupTableRecordHeader record_header; public ushort[] subtable_offsets; public IntPtr[] subtable_ptrs; public Substitution[] subs; public override string ToString() { return "" + ((LookupTypes)record_header.LookupType).ToString() + " : " + record_header.LookupFlag + " [" + record_header.SubTableCount.ToString() + "]"; } } public enum LookupTypes { Single = 1, // Replace one glyph with one glyph Multiple = 2, // Replace one glyph with more than one glyph Alternate = 3, // Replace one glyph with one of many glyphs Ligature = 4, // Replace multiple glyphs with one glyph Context = 5, // Replace one or more glyphs in context ChainingContext = 6, // Replace one or more glyphs in chained context ExtensionSubstitution = 7, // Extension mechanism for other substitutions (i.e. this excludes the Extension type substitution itself) ReverseChainingContextSingle = 8 // Applied in reverse order, replace single glyph in chaining context //9+ Reserved For future use (set to zero) } /// /// Stream position must point to lookup record /// /// /// private LookupEntry LoadLookupRecord(FontStream stream, ref LookupTableRecordHeader lookup_table_header) { ushort markFilteringSet = 0; long record_pos = stream.Position; LookupEntry result = new LookupEntry(); // -------------------------- lookup_table_header.LookupType = stream.ReadUInt16(); lookup_table_header.LookupFlag = stream.ReadUInt16(); lookup_table_header.SubTableCount = stream.ReadUInt16(); result.record_header = lookup_table_header; ushort[] subtableOffsets = new ushort[lookup_table_header.SubTableCount]; for (int j = 0; j < subtableOffsets.Length; j++) subtableOffsets[j] = stream.ReadUInt16(); if (0 != (lookup_table_header.LookupFlag & 0x0010)) markFilteringSet = stream.ReadUInt16(); result.subs = new Substitution[subtableOffsets.Length]; ushort hold_lookup_type = lookup_table_header.LookupType; for (int j = 0; j < subtableOffsets.Length; j++) { bool infinity = false; lookup_table_header.LookupType = hold_lookup_type; stream.Position = record_pos + subtableOffsets[j]; loop_extension: switch ((LookupTypes)lookup_table_header.LookupType) { case LookupTypes.Single: // Single Substitution Format result.subs[j] = LoadSingleSubstitution(stream); continue; case LookupTypes.Multiple: // Multiple Substitution Format result.subs[j] = LoadMultipleSubstitution(stream); continue; case LookupTypes.Alternate: Console.WriteLine("TTF: Alternate format not supported\n"); continue; case LookupTypes.Ligature: // Ligature Substitution Format result.subs[j] = LoadLigaturesSubtable(stream); continue; case LookupTypes.Context: // result.subs[j] = LoadContextSubstitution(stream); continue; case LookupTypes.ChainingContext: // Chained Contexts Substitution result.subs[j] = LoadChainingContext(stream); continue; case LookupTypes.ExtensionSubstitution: // Extension Substitution ushort substFormat = stream.ReadUInt16(); ushort extensionLookupType = stream.ReadUInt16(); uint extensionOffset = stream.ReadUInt32(); if (substFormat != 1) Console.WriteLine("Detected extension of Extension Substitution in Lookup record\n"); if (infinity) { Console.WriteLine("Infinity loop detected"); continue; } stream.Position = record_pos + subtableOffsets[j] + extensionOffset; lookup_table_header.LookupType = extensionLookupType; infinity = true; goto loop_extension; default: Console.WriteLine("Lookup type is " + lookup_table_header.LookupType); break; } } return result; } private void LoadLookupList(FontStream stream) { stream.Position = this.Offset + header.LookupList; ushort LookupListCount = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(lookup_list_table_ptr, typeof(ushort))); ushort[] lookupOffsets = new ushort[LookupListCount]; for (int i = 0; i < LookupListCount; i++) lookupOffsets[i] = stream.ReadUInt16(); lookup_list = new LookupEntry[LookupListCount]; LookupTableRecordHeader[] lookup_table_header = new LookupTableRecordHeader[LookupListCount]; for (int i = 0; i < LookupListCount; i++) { stream.Position = this.Offset + header.LookupList + lookupOffsets[i]; #if DEBUG_TTF //Console.WriteLine("index is {0}", i); if (i == 21) Console.WriteLine("Debug!"); #endif lookup_list[i] = LoadLookupRecord(stream, ref lookup_table_header[i]); } } private Substitution LoadContextSubstitution(FontStream stream) { long lookup_entry = stream.Position; ushort format = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(lookup_entry, typeof(ushort)); switch (format) { case 1: ContextSubstFormat1 type1 = new ContextSubstFormat1(); // Marshal.PtrToStructure(lookup_entry, typeof(ContextSubstFormat1)); type1.SubstFormat = 1; type1.CoverageOffset = stream.ReadUInt16(); type1.SubRuleSetCount = stream.ReadUInt16(); ushort[] seqRuleSetOffsets = new ushort[type1.SubRuleSetCount]; for (int k = 0; k < type1.SubRuleSetCount; k++) seqRuleSetOffsets[k] = stream.ReadUInt16(); Contextual1.SubRule[][] subRuleSets = new Contextual1.SubRule[type1.SubRuleSetCount][]; for (int i = 0; i < type1.SubRuleSetCount; i++) { if (seqRuleSetOffsets[i] == 0) { // offsets may be NULL continue; } stream.Position = lookup_entry + seqRuleSetOffsets[i]; ushort subRuleCount = stream.ReadUInt16(); ushort[] subrule_offsets = new ushort[subRuleCount]; for (int j = 0; j < subRuleCount; j++) subrule_offsets[j] = stream.ReadUInt16(); Contextual1.SubRule[] subRuleSet = new Contextual1.SubRule[subRuleCount]; for (int j = 0; j < subRuleCount; j++) { // ushort subRuleOffset = (ushort)Marshal.PtrToStructure(subrule_offsets, typeof(ushort)); // IntPtr subrule_table = Increment(subruleset_offset_table, subRuleOffset); SubRuleTable subRule = new SubRuleTable(); // Marshal.PtrToStructure(subrule_table, typeof(SubRuleTable)); stream.Position = lookup_entry + subrule_offsets[j]; subRule.GlyphCount = stream.ReadUInt16(); // SwapUInt16(subRule.GlyphCount); subRule.SubstitutionCount = stream.ReadUInt16(); // SwapUInt16(subRule.SubstitutionCount); ushort[] glyphs = new ushort[subRule.GlyphCount - 1]; // IntPtr subrule_table_arrays = Increment(subrule_table, Marshal.SizeOf(subRule)); for (int k = 0; k < subRule.GlyphCount - 1; k++) glyphs[k] = stream.ReadUInt16(); SubstLookupRecord[] records = new SubstLookupRecord[subRule.SubstitutionCount]; for (int k = 0; k < subRule.SubstitutionCount; k++) { SubstLookupRecord record = new SubstLookupRecord(); // Marshal.PtrToStructure(subrule_table_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = stream.ReadUInt16(); // SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = stream.ReadUInt16(); // SwapUInt16(record.LookupListIndex); records[k] = record; } subRuleSet[j] = new Contextual1.SubRule(glyphs, records); // subrule_offsets = Increment(subrule_offsets, Marshal.SizeOf(subRuleOffset)); } subRuleSets[i] = subRuleSet; } stream.Position = lookup_entry + type1.CoverageOffset; ; return new Contextual1(this, subRuleSets, LoadCoverage(stream)); case 2: ContextSubstFormat2 type2 = new ContextSubstFormat2(); // Marshal.PtrToStructure(lookup_entry, typeof(ContextSubstFormat2)); type2.SubstFormat = 2; // SwapUInt16(type2.SubstFormat); type2.Coverage = stream.ReadUInt16(); // SwapUInt16(type2.Coverage); type2.ClassDefOffset = stream.ReadUInt16(); // SwapUInt16(type2.ClassDefOffset); type2.SubClassSetCount = stream.ReadUInt16(); // SwapUInt16(type2.SubClassSetCount); Contextual2.SubClassRule[][] subClassRuleSets = new Contextual2.SubClassRule[type2.SubClassSetCount][]; long sub_class_set_offset = stream.Position; for (int i = 0; i < type2.SubClassSetCount; i++) { ushort offset = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(sub_class_set_offset, typeof(ushort)); if (offset == 0) subClassRuleSets[i] = null; else { // offset = SwapUInt16(offset); // IntPtr sub_class_set_table = Increment(lookup_entry, offset); long sub_class_set_table = stream.Position; ushort count = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(sub_class_set_table, typeof(ushort)); // IntPtr sub_class_rule_offset = Increment(sub_class_set_table, Marshal.SizeOf(count)); long sub_class_rule_offset = stream.Position; Contextual2.SubClassRule[] subClassSet = new Contextual2.SubClassRule[count]; for (int j = 0; j < count; j++) { ushort offset2 = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(sub_class_rule_offset, typeof(ushort)); // IntPtr sub_class_rule_table = Increment(sub_class_set_table, offset2); long sub_class_rule_table = sub_class_set_table + offset2; SubClassRule subClassRule = new SubClassRule(); // Marshal.PtrToStructure(sub_class_rule_table, typeof(SubClassRule)); //IntPtr subclassrule_table_arrays = Increment(sub_class_rule_table, Marshal.SizeOf(subClassRule)); long subclassrule_table_arrays = sub_class_rule_table + 4; subClassRule.ClassCount = stream.ReadUInt16(); // SwapUInt16(subClassRule.ClassCount); subClassRule.SubstitutionCount = stream.ReadUInt16(); // SwapUInt16(subClassRule.SubstitutionCount); ushort[] glyphClassess = new ushort[subClassRule.ClassCount - 1]; for (int k = 0; k < subClassRule.ClassCount - 1; k++) glyphClassess[k] = stream.ReadUInt16(); SubstLookupRecord[] records = new SubstLookupRecord[subClassRule.SubstitutionCount]; for (int k = 0; k < subClassRule.SubstitutionCount; k++) { SubstLookupRecord record = new SubstLookupRecord(); // Marshal.PtrToStructure(subclassrule_table_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = stream.ReadUInt16(); // SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = stream.ReadUInt16(); // SwapUInt16(record.LookupListIndex); records[k] = record; } subClassSet[j] = new Contextual2.SubClassRule(glyphClassess, records); // sub_class_rule_offset = Increment(sub_class_rule_offset, Marshal.SizeOf(offset2)); sub_class_rule_offset += 2; } subClassRuleSets[i] = subClassSet; } // sub_class_set_offset = Increment(sub_class_set_offset, Marshal.SizeOf(offset)); sub_class_set_offset += 2; } stream.Position = lookup_entry + type2.Coverage; Coverage coverage = LoadCoverage(stream); stream.Position = lookup_entry + type2.ClassDefOffset; ClassDefinition class_definition = LoadClassDefinition(stream); return new Contextual2(this, subClassRuleSets, coverage, class_definition); case 3: ContextSubstFormat3 type3 = new ContextSubstFormat3(); // Marshal.PtrToStructure(lookup_entry, typeof(ContextSubstFormat3)); type3.SubstFormat = 3; // SwapUInt16(type3.SubstFormat); type3.GlyphCount = stream.ReadUInt16(); // SwapUInt16(type3.GlyphCount); type3.SubstitutionCount = stream.ReadUInt16(); // SwapUInt16(type3.SubstitutionCount); Coverage[] coverages = new Coverage[type3.GlyphCount]; SubstLookupRecord[] subRecords = new SubstLookupRecord[type3.SubstitutionCount]; ushort[] offsets3 = new ushort[type3.GlyphCount]; for (int i = 0; i < type3.GlyphCount; i++) offsets3[i] = stream.ReadUInt16(); long type3_arrays = stream.Position; for (int i = 0; i < type3.GlyphCount; i++) { stream.Position = lookup_entry + offsets3[i]; coverages[i] = LoadCoverage(stream); } stream.Position = type3_arrays; for (int i = 0; i < type3.SubstitutionCount; i++) { SubstLookupRecord record = new SubstLookupRecord(); // Marshal.PtrToStructure(type3_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = stream.ReadUInt16(); // SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = stream.ReadUInt16(); // SwapUInt16(record.LookupListIndex); subRecords[i] = record; // type3_arrays = Increment(type3_arrays, Marshal.SizeOf(record)); } return new Contextual3(this, subRecords, coverages); } return new VoidSubstitution(); } private Substitution LoadMultipleSubstitution(FontStream stream) { long lookup_entry = stream.Position; MultipleSubstFormat1 type1 = new MultipleSubstFormat1(); // Marshal.PtrToStructure(lookup_entry, typeof(MultipleSubstFormat1)); type1.SubstFormat = stream.ReadUInt16(); // SwapUInt16(type1.SubstFormat); type1.Coverage = stream.ReadUInt16(); // SwapUInt16(type1.Coverage); type1.SequenceCount = stream.ReadUInt16(); // SwapUInt16(type1.SequenceCount); //type1.SequenceOffset = SwapUInt16(type1.SequenceOffset); ushort[][] sequences = new ushort[type1.SequenceCount][]; for (int i = 0; i < type1.SequenceCount; i++) { ushort offset = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(sequence_offset, typeof(ushort)); // offset = SwapUInt16(offset); // IntPtr sequence_offset_table = Increment(lookup_entry, offset); stream.Position = lookup_entry + offset; ushort glyph_count = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(sequence_offset_table, typeof(ushort)); // glyph_count = SwapUInt16(glyph_count); ushort[] glyphs = new ushort[glyph_count]; for (int j = 0; j < glyph_count; j++) { glyphs[j] = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(sequence_offset_table, typeof(ushort)); glyphs[j] = stream.ReadUInt16(); // SwapUInt16(glyphs[j]); // sequence_offset_table = Increment(lookup_entry, Marshal.SizeOf(glyphs[j])); } sequences[i] = glyphs; //sequence_offset = Increment(lookup_entry, Marshal.SizeOf(offset)); stream.Position = lookup_entry + 2; } stream.Position = lookup_entry + type1.Coverage; return new Multiple(sequences, LoadCoverage(stream)); } private Substitution LoadSingleSubstitution(FontStream stream) { long lookup_entry = stream.Position; SignleSubstitutionFormat1 type1 = new SignleSubstitutionFormat1(); // Marshal.PtrToStructure(lookup_entry, typeof(SignleSubstitutionFormat1)); type1.SubstFormat = stream.ReadUInt16(); // SwapUInt16(type1.SubstFormat); type1.Coverage = stream.ReadUInt16(); // SwapUInt16(type1.Coverage); type1.DeltaGlyphID = stream.ReadInt16(); // SwapInt16(type1.DeltaGlyphID); switch (type1.SubstFormat) { case 1: stream.Position = lookup_entry + type1.Coverage; return new Single1(type1.DeltaGlyphID, LoadCoverage(stream)); case 2: ushort[] substitute = new ushort[type1.DeltaGlyphID]; // IntPtr lookup_entry_substitute = Increment(lookup_entry, Marshal.SizeOf(typeof(SignleSubstitutionFormat1))); for (int i = 0; i < type1.DeltaGlyphID; i++) substitute[i] = stream.ReadUInt16(); stream.Position = lookup_entry + type1.Coverage; return new Single2(substitute, LoadCoverage(stream)); } return new VoidSubstitution(); } private ClassDefinition LoadClassDefinition(FontStream stream) { long class_definition_ptr = stream.Position; ushort class_format = stream.ReadUInt16(); class_format = SwapUInt16(class_format); switch (class_format) { case 1: ClassDefFormat1 type1 = new ClassDefFormat1(); // Marshal.PtrToStructure(class_definition_ptr, typeof(ClassDefFormat1)); type1.ClassFormat = stream.ReadUInt16(); // SwapUInt16(type1.ClassFormat); type1.StartGlyphID = stream.ReadUInt16(); // SwapUInt16(type1.StartGlyphID); type1.GlyphCount = stream.ReadUInt16(); // SwapUInt16(type1.GlyphCount); ushort[] classValues = new ushort[type1.GlyphCount]; for (int i = 0; i < type1.GlyphCount; i++) classValues[i] = stream.ReadUInt16(); ; return new ClassDefinition1(type1.StartGlyphID, classValues); case 2: ClassDefFormat2 type2 = new ClassDefFormat2(); // Marshal.PtrToStructure(class_definition_ptr, typeof(ClassDefFormat2)); type2.ClassFormat = stream.ReadUInt16(); type2.ClassRangeCount = stream.ReadUInt16(); ClassRangeRecord[] records = new ClassRangeRecord[type2.ClassRangeCount]; for (int i = 0; i < type2.ClassRangeCount; i++) { ClassRangeRecord record = new ClassRangeRecord(); // Marshal.PtrToStructure(class_records_array, typeof(ClassRangeRecord)); record.StartGlyphID = stream.ReadUInt16(); record.EndGlyphID = stream.ReadUInt16(); record.ClassValue = stream.ReadUInt16(); records[i] = record; } return new ClassDefinition2(records); } return new VoidClassDefinition(); } private Coverage LoadCoverage(FontStream stream) { CoverageHeader ch = new CoverageHeader(); // Marshal.PtrToStructure(coverage_table_ptr, typeof(CoverageHeader)); ch.CoverageFormat = stream.ReadUInt16(); // SwapUInt16(ch.CoverageFormat); ch.GlyphCount = stream.ReadUInt16(); // SwapUInt16(ch.GlyphCount); switch (ch.CoverageFormat) { case 1: { ushort[] glyphs = new ushort[ch.GlyphCount]; //coverage_table_ptr = Increment(coverage_table_ptr, Marshal.SizeOf(ch)); for (int i = 0; i < ch.GlyphCount; i++) { glyphs[i] = stream.ReadUInt16(); } /*if((new Coverage1(ch.GlyphCount, glyphs)).IsSubstituteGetIndex(296) >=0) { //to do remove }*/ return new Coverage1(ch.GlyphCount, glyphs); } //break; case 2: { RangeRecord[] rrs = new RangeRecord[ch.GlyphCount]; // coverage_table_ptr = Increment(coverage_table_ptr, Marshal.SizeOf(ch)); for (int i = 0; i < ch.GlyphCount; i++) { rrs[i] = new RangeRecord(); // Marshal.PtrToStructure(coverage_table_ptr, typeof(RangeRecord)); rrs[i].Start = stream.ReadUInt16(); // SwapUInt16(rrs[i].Start); rrs[i].End = stream.ReadUInt16(); // SwapUInt16(rrs[i].End); rrs[i].StartCoverageIndex = stream.ReadUInt16(); // SwapUInt16(rrs[i].StartCoverageIndex); // coverage_table_ptr = Increment(coverage_table_ptr, Marshal.SizeOf(rrs[i])); } /*if ((new Coverage2(ch.GlyphCount, rrs)).IsSubstituteGetIndex(296) >= 0) { //to do remove }*/ return new Coverage2(ch.GlyphCount, rrs); } //break; } return new VoidCoverage(); } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ExtensionSubstFormat { [FieldOffset(0)] public ushort SubstFormat; [FieldOffset(2)] public ushort ExtensionLookupType; [FieldOffset(4)] public uint ExtensionOffset; public override string ToString() { return ((LookupTypes)ExtensionLookupType).ToString(); } } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct LigatureSubst { [FieldOffset(0)] public ushort SubstFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort Coverage; // Offset to Coverage table-from beginning of Substitution table [FieldOffset(4)] public ushort LigSetCount; // Number of LigatureSet tables // public ushort [LigSetCount] Array of offsets to LigatureSet tables-from beginning of Substitution table-ordered by Coverage Index } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct LigatureHeader { [FieldOffset(0)] public ushort LigGlyph; // GlyphID of ligature to substitute [FieldOffset(2)] public ushort CompCount; // Number of components in the ligature } internal Substitution LoadLigaturesSubtable(FontStream stream) { long lookup_entry = stream.Position; LigatureSubst type4 = new LigatureSubst(); // Marshal.PtrToStructure(lookup_entry, typeof(LigatureSubst)); type4.SubstFormat = stream.ReadUInt16(); // SwapUInt16(type4.SubstFormat); type4.Coverage = stream.ReadUInt16(); // SwapUInt16(type4.Coverage); type4.LigSetCount = stream.ReadUInt16(); // SwapUInt16(type4.LigSetCount); //To do not null //LoadCoverageTable(null, Increment(lookup_entry, type4.Coverage)); stream.Position = lookup_entry + type4.Coverage; Coverage coverage = LoadCoverage(stream); // Ligature Set // IntPtr ligature_set_ptr = Increment(lookup_entry, Marshal.SizeOf(type4)); stream.Position = lookup_entry + 6; //IntPtr current_ptr = ligature_set_ptr; //long current_ptr = lookup_entry + 6; // IntPtr[] LigatureSet = new IntPtr[type4.LigSetCount]; ushort[] LigatureSet = new ushort[type4.LigSetCount]; for (int i = 0; i < type4.LigSetCount; i++) LigatureSet[i] = stream.ReadUInt16(); LigatureSet[][] ligatureSets = new LigatureSet[type4.LigSetCount][]; for (int i = 0; i < type4.LigSetCount; i++) { //ushort offset = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(current_ptr, typeof(ushort))); // Console.WriteLine("LigatresSet[" + i + "] at" + offset); // LigatureSet[i] = stream.ReadUInt16(); // Increment(lookup_entry, offset); // Ligature Table stream.Position = lookup_entry + LigatureSet[i]; long current_ptr = stream.Position; ushort LigatureCount = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(LigatureSet[i], typeof(ushort))); // IntPtr ligature_ptr = Increment(LigatureSet[i], sizeof(ushort)); IntPtr[] LigaturePtrs = new IntPtr[LigatureCount]; LigatureHeader[] lh = new LigatureHeader[LigatureCount]; ligatureSets[i] = new LigatureSet[LigatureCount]; ushort[] lig_offsets = new ushort[LigatureCount]; for (int j = 0; j < LigatureCount; j++) lig_offsets[j] = stream.ReadUInt16(); for (int j = 0; j < LigatureCount; j++) { // ushort lig_offset = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(ligature_ptr, typeof(ushort))); //LigaturePtrs[j] = Increment(LigatureSet[i], lig_offset); // Console.WriteLine("\tLigature[" + j + "] at " + lig_offset); // stream.Position = current_ptr + LigatureSet[j] + lig_offsets[j]; // stream.Position = lookup_entry + LigatureSet[i] + lig_offsets[j]; stream.Position = current_ptr + lig_offsets[j]; //// Ligature lh[j] = new LigatureHeader(); // Marshal.PtrToStructure(LigaturePtrs[j], typeof(LigatureHeader)); lh[j].LigGlyph = stream.ReadUInt16(); // SwapUInt16(lh[j].LigGlyph); lh[j].CompCount = stream.ReadUInt16(); // SwapUInt16(lh[j].CompCount); ushort[] glyphs = new ushort[lh[j].CompCount - 1]; for (int k = 0; k < lh[j].CompCount - 1; k++) glyphs[k] = stream.ReadUInt16(); // SwapUInt16((ushort)Marshal.PtrToStructure(ligature_array, typeof(ushort))); //// --------- ligatureSets[i][j] = new LigatureSet(lh[j].LigGlyph, glyphs); } // ----------------- } return new Ligature(coverage, ligatureSets); } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct CoverageHeader { [FieldOffset(0)] public ushort CoverageFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort GlyphCount; // Number of glyphs in the GlyphArray } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct RangeRecord { [FieldOffset(0)] public ushort Start; // First GlyphID in the range [FieldOffset(2)] public ushort End; // Last GlyphID in the range [FieldOffset(4)] public ushort StartCoverageIndex; // Coverage Index of first GlyphID in range } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct SignleSubstitutionFormat1 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort Coverage; // Offset to Coverage table-from beginning of Substitution table [FieldOffset(4)] public short DeltaGlyphID; // Add to original GlyphID to get substitute GlyphID } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct MultipleSubstFormat1 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort Coverage; // Offset to Coverage table-from beginning of Substitution table [FieldOffset(4)] public ushort SequenceCount; // Number of Sequence table offsets in the Sequence array //[FieldOffset(6)] //public ushort SequenceOffset; // Array of offsets to Sequence tables-from beginning of Substitution table-ordered by Coverage Index } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ContextSubstFormat1 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort CoverageOffset; // Offset to Coverage table-from beginning of Substitution table [FieldOffset(4)] public ushort SubRuleSetCount; // Number of SubRuleSet tables — must equal glyphCount in Coverage table //[FieldOffset(6)] //public ushort[] subRuleSetOffsets; // Array of offsets to SubRuleSet tables. Offsets are from beginning of substitution subtable, ordered by Coverage index } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ContextSubstFormat2 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort Coverage; // Offset to Coverage table-from beginning of Substitution table [FieldOffset(4)] public ushort ClassDefOffset; // Offset to glyph ClassDef table, from beginning of substitution subtable [FieldOffset(6)] public ushort SubClassSetCount; // Number of SubClassSet tables //[FieldOffset(8)] //public ushort[] subClassSetOffsets; // Array of offsets to SubClassSet tables. Offsets are from beginning of substitution subtable, ordered by class (may be NULL). } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ContextSubstFormat3 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier-format = 1 [FieldOffset(2)] public ushort GlyphCount; // Number of glyphs in the input glyph sequence [FieldOffset(4)] public ushort SubstitutionCount; // Number of SubstLookupRecords /* Offset16 coverageOffsets[glyphCount] Array of offsets to Coverage tables. Offsets are from beginning of substitution subtable, in glyph sequence order. SubstLookupRecord substLookupRecords[substitutionCount] Array of SubstLookupRecords, in design order. */ } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct SubClassRule { [FieldOffset(0)] public ushort ClassCount;//GlyphCount; // Total number of classes specified for the context in the rule — includes the first class [FieldOffset(2)] public ushort SubstitutionCount; // Number of SubstLookupRecords /* uint16 inputSequence[glyphCount - 1] Array of classes to be matched to the input glyph sequence, beginning with the second glyph position. SubstLookupRecord substLookupRecords[substitutionCount] Array of Substitution lookups, in design order. */ } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct SubstLookupRecord { [FieldOffset(0)] public ushort GlyphSequenceIndex; // Index into current glyph sequence — first glyph = 0. [FieldOffset(2)] public ushort LookupListIndex; // Lookup to apply to that position — zero-based index. } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct SubRuleTable { [FieldOffset(0)] public ushort GlyphCount; // Total number of glyphs in input glyph sequence — includes the first glyph. [FieldOffset(2)] public ushort SubstitutionCount; // Number of SubstLookupRecords //[FieldOffset(4)] //public ushort inputSequence; // [glyphCount - 1] Array of input glyph IDs — start with second glyph //[FieldOffset(6)] //public SubstLookupRecord[] substLookupRecords; // [substitutionCount] Array of SubstLookupRecords, in design order } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct SequenceFormat { [FieldOffset(0)] public ushort GlyphCount; // Number of glyph IDs in the Substitute array. This should always be greater than 0. // Substitute [GlyphCount] String of glyph IDs to substitute } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ClassDefFormat1 { [FieldOffset(0)] public ushort ClassFormat; // Format identifier — format = 1 [FieldOffset(2)] public ushort StartGlyphID; // First glyph ID of the classValueArray [FieldOffset(4)] public ushort GlyphCount; // Size of the classValueArray //[FieldOffset(6)] //public ushort[] classValueArray; // [glyphCount] Array of Class Values — one per glyph ID } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ClassDefFormat2 { [FieldOffset(0)] public ushort ClassFormat; // Format identifier — format = 1 [FieldOffset(2)] public ushort ClassRangeCount; // First glyph ID of the classValueArray //[FieldOffset(4)] //public ClassRangeRecord[] classRangeRecords; // Array of ClassRangeRecords — ordered by startGlyphID } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct ClassRangeRecord { [FieldOffset(0)] public ushort StartGlyphID; // Format identifier — format = 1 [FieldOffset(2)] public ushort EndGlyphID; // First glyph ID of the classValueArray [FieldOffset(4)] public ushort ClassValue; // Array of ClassRangeRecords — ordered by startGlyphID } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ChainContextSubstFormat1 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier: format = 1 [FieldOffset(2)] public ushort Coverage; // Offset to Coverage table, from beginning of substitution subtable. [FieldOffset(4)] public ushort ChainSubRuleSetCount; // Number of ChainSubRuleSet tables — must equal GlyphCount in Coverage table. /* Offset16 chainSubRuleSetOffsets[chainSubRuleSetCount] Array of offsets to ChainSubRuleSet tables. Offsets are from beginning of substitution subtable, ordered by Coverage index. */ } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ChainContextSubstFormat2 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier: format = 2 [FieldOffset(2)] public ushort CoverageOffset; // Offset to Coverage table, from beginning of substitution subtable. [FieldOffset(4)] public ushort BacktrackClassDefOffset; // Offset to glyph ClassDef table containing backtrack sequence data, from beginning of substitution subtable. [FieldOffset(6)] public ushort InputClassDefOffset; // Offset to glyph ClassDef table containing input sequence data, from beginning of substitution subtable. [FieldOffset(8)] public ushort LookaheadClassDefOffset; // Offset to glyph ClassDef table containing lookahead sequence data, from beginning of substitution subtable. [FieldOffset(10)] public ushort ChainSubClassSetCount; // Number of ChainSubClassSet tables /* Offset16 chainSubClassSetOffsets[chainSubClassSetCount] Array of offsets to ChainSubClassSet tables. Offsets are from beginning of substitution subtable, ordered by input class (may be NULL) */ } [StructLayout(LayoutKind.Explicit, Pack = 1)] internal struct ChainContextSubstFormat3 { [FieldOffset(0)] public ushort SubstFormat; // Format identifier: format = 3 /* uint16 backtrackGlyphCount Number of glyphs in the backtracking sequence. Offset16 backtrackCoverageOffsets[backtrackGlyphCount] Array of offsets to coverage tables in backtracking sequence. Offsets are from beginning of substition subtable, in glyph sequence order. uint16 inputGlyphCount Number of glyphs in input sequence Offset16 inputCoverageOffsets[inputGlyphCount] Array of offsets to coverage tables in input sequence. Offsets are from beginning of substition subtable, in glyph sequence order. uint16 lookaheadGlyphCount Number of glyphs in lookahead sequence Offset16 lookaheadCoverageOffsets[lookaheadGlyphCount] Array of offsets to coverage tables in lookahead sequence. Offsets are from beginning of substitution subtable, in glyph sequence order. uint16 substitutionCount Number of SubstLookupRecords SubstLookupRecord substLookupRecords[substitutionCount] Array of SubstLookupRecords, in design order */ } private Substitution LoadChainingContextSubstFormat1(FontStream stream) { long lookup_entry = stream.Position - 2; ChainContextSubstFormat1 type1 = new ChainContextSubstFormat1(); // Marshal.PtrToStructure(lookup_entry, typeof(ChainContextSubstFormat1)); // type1.SubstFormat = stream.ReadUInt16(); // SwapUInt16(type1.SubstFormat); type1.Coverage = stream.ReadUInt16(); // SwapUInt16(type1.Coverage); type1.ChainSubRuleSetCount = stream.ReadUInt16(); // SwapUInt16(type1.ChainSubRuleSetCount); ChainingContextual1.ChainSubRule[][] chainSubRuleSets = new ChainingContextual1.ChainSubRule[type1.ChainSubRuleSetCount][]; //IntPtr chain_sub_rule_set_offsets = Increment(lookup_entry, Marshal.SizeOf(type1)); long chain_sub_rule_set_offsets = stream.Position; ushort[] offsets = new ushort[type1.ChainSubRuleSetCount]; for (int i = 0; i < type1.ChainSubRuleSetCount; i++) offsets[i] = stream.ReadUInt16(); for (int i = 0; i < type1.ChainSubRuleSetCount; i++) { long stream_position = stream.Position; stream.Position = lookup_entry + offsets[i]; ushort count = stream.ReadUInt16(); long chain_sub_rule_offset = stream.Position; ushort[] offset2 = new ushort[count]; // (ushort)Marshal.PtrToStructure(chain_sub_rule_offset, typeof(ushort)); for (int j = 0; j < count; j++) offset2[j] = stream.ReadUInt16(); //IntPtr chain_sub_rule_offset = Increment(chain_sub_rule_set_table, Marshal.SizeOf(count)); ChainingContextual1.ChainSubRule[] chainSubRuleSet = new ChainingContextual1.ChainSubRule[count]; for (int j = 0; j < count; j++) { stream.Position = lookup_entry + offsets[i] + offset2[j]; ushort backtrackGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); ushort[] backtrackSequence = new ushort[backtrackGlyphCount]; for (int k = 0; k < backtrackGlyphCount; k++) backtrackSequence[k] = stream.ReadUInt16(); //end array //start array ushort inputGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); ushort[] inputSequence = new ushort[inputGlyphCount - 1]; // chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(inputGlyphCount)); for (int k = 0; k < inputGlyphCount - 1; k++) inputSequence[k] = stream.ReadUInt16(); ; //end array //start array ushort lookaheadGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); ushort[] lookAheadSequence = new ushort[lookaheadGlyphCount]; for (int k = 0; k < lookaheadGlyphCount; k++) lookAheadSequence[k] = stream.ReadUInt16(); //end array //start array ushort substitutionCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); SubstLookupRecord[] records = new SubstLookupRecord[substitutionCount]; for (int k = 0; k < substitutionCount; k++) { SubstLookupRecord record = new SubstLookupRecord(); // Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = stream.ReadUInt16(); // SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = stream.ReadUInt16(); // SwapUInt16(record.LookupListIndex); records[k] = record; } //end array chainSubRuleSet[j] = new ChainingContextual1.ChainSubRule(backtrackSequence, inputSequence, lookAheadSequence, records); //chain_sub_rule_offset = Increment(chain_sub_rule_offset, Marshal.SizeOf(offset2)); chain_sub_rule_offset += 2; } chainSubRuleSets[i] = chainSubRuleSet; // chain_sub_rule_set_offsets = Increment(chain_sub_rule_set_offsets, Marshal.SizeOf(offset)); chain_sub_rule_set_offsets += 2; } stream.Position = lookup_entry + type1.Coverage; return new ChainingContextual1(this, LoadCoverage(stream), chainSubRuleSets); } private Substitution LoadChainingContextSubstFormat2(FontStream stream) { ChainContextSubstFormat2 type2 = new ChainContextSubstFormat2(); // Marshal.PtrToStructure(lookup_entry, typeof(ChainContextSubstFormat2)); long lookup_entry = stream.Position - 2; //type2.SubstFormat = stream.ReadUInt16(); // SwapUInt16(type2.SubstFormat); type2.CoverageOffset = stream.ReadUInt16(); // SwapUInt16(type2.Coverage); type2.BacktrackClassDefOffset = stream.ReadUInt16(); // SwapUInt16(type2.BacktrackClassDefOffset); type2.InputClassDefOffset = stream.ReadUInt16(); // SwapUInt16(type2.InputClassDefOffset); type2.LookaheadClassDefOffset = stream.ReadUInt16(); // SwapUInt16(type2.LookaheadClassDefOffset); type2.ChainSubClassSetCount = stream.ReadUInt16(); // SwapUInt16(type2.ChainSubClassSetCount); ChainingContextual2.ChainSubClassRule[][] chainSubClassRuleSets = new ChainingContextual2.ChainSubClassRule[type2.ChainSubClassSetCount][]; ushort[] offsets = new ushort[type2.ChainSubClassSetCount]; for (int i = 0; i < type2.ChainSubClassSetCount; i++) offsets[i] = stream.ReadUInt16(); for (int i = 0; i < type2.ChainSubClassSetCount; i++) { if (offsets[i] == 0) { // If no patterns are defined with an input sequence beginning with a particular class, // then the offset for that class value can be set to NULL. continue; } long chain_sub_class_ruleset_table = lookup_entry + offsets[i]; stream.Position = chain_sub_class_ruleset_table; ushort count = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_class_set_table, typeof(ushort)); // 20231110: Read chainedClassSeqRuleOffsets ushort[] chainedClassSeqRuleOffsets = new ushort[count]; for (int k = 0; k < count; k++) chainedClassSeqRuleOffsets[k] = stream.ReadUInt16(); ChainingContextual2.ChainSubClassRule[] chainSubClassSet = new ChainingContextual2.ChainSubClassRule[count]; for (int j = 0; j < count; j++) { stream.Position = chain_sub_class_ruleset_table + chainedClassSeqRuleOffsets[j]; //start array ushort backtrackGlyphCount = stream.ReadUInt16(); ushort[] backtrackSequence = new ushort[backtrackGlyphCount]; for (int k = 0; k < backtrackGlyphCount; k++) backtrackSequence[k] = stream.ReadUInt16(); //end array //start array ushort inputGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); ushort[] inputSequence = new ushort[inputGlyphCount - 1]; for (int k = 0; k < inputGlyphCount - 1; k++) inputSequence[k] = stream.ReadUInt16(); //end array //start array ushort lookaheadGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); ushort[] lookAheadSequence = new ushort[lookaheadGlyphCount]; for (int k = 0; k < lookaheadGlyphCount; k++) lookAheadSequence[k] = stream.ReadUInt16(); //end array //start array ushort substitutionCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); SubstLookupRecord[] records = new SubstLookupRecord[substitutionCount]; for (int k = 0; k < substitutionCount; k++) { SubstLookupRecord record = new SubstLookupRecord(); // Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = stream.ReadUInt16(); // SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = stream.ReadUInt16(); // SwapUInt16(record.LookupListIndex); records[k] = record; } //end array chainSubClassSet[j] = new ChainingContextual2.ChainSubClassRule(backtrackSequence, inputSequence, lookAheadSequence, records); } chainSubClassRuleSets[i] = chainSubClassSet; } stream.Position = lookup_entry + type2.CoverageOffset; Coverage coverage = LoadCoverage(stream); stream.Position = lookup_entry + type2.BacktrackClassDefOffset; ClassDefinition backtrackClassDefinition = LoadClassDefinition(stream); stream.Position = lookup_entry + type2.InputClassDefOffset; ClassDefinition inputClassDefinition = LoadClassDefinition(stream); stream.Position = lookup_entry + type2.LookaheadClassDefOffset; ClassDefinition lookaheadClassDefinition = LoadClassDefinition(stream); return new ChainingContextual2(this, chainSubClassRuleSets, coverage, backtrackClassDefinition, inputClassDefinition, lookaheadClassDefinition); } private Substitution LoadChainingContextSubstFormat3(FontStream stream) { long lookup_entry = stream.Position - 2; //IntPtr chain_format_3_array = Increment(lookup_entry, Marshal.SizeOf(typeof(ushort))); long chain_format_3_array = lookup_entry + 2; //start array ushort backtrackGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); ushort[] offsets = new ushort[backtrackGlyphCount]; for (int k = 0; k < backtrackGlyphCount; k++) offsets[k] = stream.ReadUInt16(); ushort inputGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); ushort[] offsets_gc = new ushort[inputGlyphCount]; for (int k = 0; k < inputGlyphCount; k++) offsets_gc[k] = stream.ReadUInt16(); ushort lookaheadGlyphCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); ushort[] offsets_as = new ushort[lookaheadGlyphCount]; for (int k = 0; k < lookaheadGlyphCount; k++) offsets_as[k] = stream.ReadUInt16(); ushort lookuoCount = stream.ReadUInt16(); // (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); long lookupPosition = stream.Position; //start array Coverage[] backtrackSequence = new Coverage[backtrackGlyphCount]; for (int k = 0; k < backtrackGlyphCount; k++) { stream.Position = lookup_entry + offsets[k]; backtrackSequence[k] = LoadCoverage(stream); } //end array //start array Coverage[] inputSequence = new Coverage[inputGlyphCount]; for (int k = 0; k < inputGlyphCount; k++) { stream.Position = lookup_entry + offsets_gc[k]; inputSequence[k] = LoadCoverage(stream); } //end array //start array Coverage[] lookAheadSequence = new Coverage[lookaheadGlyphCount]; for (int k = 0; k < lookaheadGlyphCount; k++) { stream.Position = lookup_entry + offsets_as[k]; lookAheadSequence[k] = LoadCoverage(stream); } //end array //start array stream.Position = lookupPosition; SubstLookupRecord[] records = new SubstLookupRecord[lookuoCount]; for (int k = 0; k < lookuoCount; k++) { SubstLookupRecord record = new SubstLookupRecord(); // Marshal.PtrToStructure(chain_format_3_array, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = stream.ReadUInt16(); // SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = stream.ReadUInt16(); // SwapUInt16(record.LookupListIndex); records[k] = record; } //end array return new ChainingContextual3(this, records, backtrackSequence, inputSequence, lookAheadSequence); } #if DEBUG_TTF static int debug = 0; #endif private Substitution LoadChainingContext(FontStream stream) { // long lookup_entry = stream.Position; ushort format = stream.ReadUInt16(); #if DEBUG_TTF debug++; Console.WriteLine("LoadChainingContext format" + format.ToString() + " Iteration " + debug.ToString()); if (debug == 123) ; #endif switch (format) { case 1: return LoadChainingContextSubstFormat1(stream); case 2: return LoadChainingContextSubstFormat2(stream); case 3: return LoadChainingContextSubstFormat3(stream); default: throw new NotSupportedException("Format of GSUB ChaingContext"); } return new VoidSubstitution(); } internal override void Load(FontStream stream) { stream.Seek(this.Offset, System.IO.SeekOrigin.Begin); try { header.Version = stream.ReadUInt32(); // Version of the GSUB table-initially set to 0x00010000 header.ScriptList = stream.ReadUInt16(); // Offset to ScriptList table-from beginning of GSUB table header.FeatureList = stream.ReadUInt16(); // Offset to FeatureList table-from beginning of GSUB table header.LookupList = stream.ReadUInt16(); // Offset to LookupList table-from beginning of GSUB table if (header.Version == 0x00010001) featureVariationsOffset = stream.ReadUInt32(); LoadLookupList(stream); LoadScriptList(stream); //if(do_features) LoadFeatureList(); } catch (System.IO.EndOfStreamException ex) { Console.WriteLine("End of stream exception"); } } //internal override uint Save(FontStream font, uint offset) //{ // this.Offset = offset; // return base.Save(font, offset); //} private void ChangeEndian() { header.Version = SwapUInt32(header.Version); header.ScriptList = SwapUInt16(header.ScriptList); header.LookupList = SwapUInt16(header.LookupList); header.FeatureList = SwapUInt16(header.FeatureList); } public GlyphSubstitutionClass(TrueTypeTable src) : base(src) { } internal List ApplyGlyph(string script, string lang, ushort[] chars) { if (script == null) { script = "latn"; foreach (string name in script_list.Keys) { script = name; break; } } if (script_list.ContainsKey(script)) { Hashtable lang_sys_hash = script_list[script] as Hashtable; Hashtable lang_sys = null; if (lang_sys_hash.ContainsKey(lang)) { lang_sys = lang_sys_hash[lang] as Hashtable; } else if (lang_sys_hash.ContainsKey(string.Empty)) { lang_sys = lang_sys_hash[string.Empty] as Hashtable; } if (lang_sys != null) { switch (script) { case "arab": return ApplyGlyphArabic(lang_sys, chars); } } } return new List(chars); } private void ApplyGlyphFeature(List result, ushort[] offsets, ushort[] chars, ref int i) { foreach (ushort offset in offsets) { LookupEntry le = lookup_list[offset]; for (int j = 0; j < le.subs.Length; j++) if (le.subs[j].Apply(result, chars, ref i)) return; } result.Add(chars[i]); } private bool IsApplyGlyphFeature(int index, ushort[] offsets, ushort[] chars) { if (index < 0) return false; if (index >= chars.Length) return false; foreach (ushort offset in offsets) { LookupEntry le = lookup_list[offset]; for (int j = 0; j < le.subs.Length; j++) if (le.subs[j].IsApply(chars, index) >= 0) return true; } return false; } private List ApplyGlyphArabic(Hashtable lang_sys, ushort[] chars) { List result = new List(); if (lang_sys.Contains("ccmp")) { ushort[] ccmp = lang_sys["ccmp"] as ushort[]; for (int i = 0; i < chars.Length; i++) { ApplyGlyphFeature(result, ccmp, chars, ref i); } chars = result.ToArray(); result.Clear(); } ushort[] isol; if (lang_sys.Contains("isol")) isol = lang_sys["isol"] as ushort[]; else isol = new ushort[0]; ushort[] init; if (lang_sys.Contains("init")) init = lang_sys["init"] as ushort[]; else init = new ushort[0]; ushort[] medi; if (lang_sys.Contains("medi")) medi = lang_sys["medi"] as ushort[]; else medi = new ushort[0]; ushort[] fina; if (lang_sys.Contains("fina")) fina = lang_sys["fina"] as ushort[]; else fina = new ushort[0]; ArabicCharType[] types = new ArabicCharType[chars.Length]; //Ýòî êîñòûëü, òàê êàê ÿ íå çíàþ êàêèì îáðàçîì âûòàùèòü íóæíûå èíäåêñû, ñäåëàþ òàêîé âàðèàíò áóäó ãóãëèòü for (int i = 0; i < chars.Length; i++) { bool nextMedi = IsApplyGlyphFeature(i + 1, medi, chars); bool nextFina = IsApplyGlyphFeature(i + 1, fina, chars); bool prevMedi = IsApplyGlyphFeature(i - 1, medi, chars); bool prevInit = IsApplyGlyphFeature(i - 1, init, chars); if (nextMedi || nextFina) { if (prevMedi || prevInit) { bool curMedi = IsApplyGlyphFeature(i, medi, chars); if (curMedi) { types[i] = ArabicCharType.Medi; continue; } } else { bool curInit = IsApplyGlyphFeature(i, init, chars); if (curInit) { types[i] = ArabicCharType.Init; continue; } } types[i] = ArabicCharType.None; continue; } if (prevInit || prevMedi) { bool curFina = IsApplyGlyphFeature(i, fina, chars); if (curFina) { types[i] = ArabicCharType.Fina; continue; } types[i] = ArabicCharType.None; continue; } if (!nextMedi && !nextFina && !prevInit && !prevMedi) { bool curIsol = IsApplyGlyphFeature(i, isol, chars); if (curIsol) { types[i] = ArabicCharType.Isol; continue; } types[i] = ArabicCharType.None; continue; } types[i] = ArabicCharType.None; } for (int i = 0; i < chars.Length; i++) { switch (types[i]) { case ArabicCharType.None: result.Add(chars[i]); break; case ArabicCharType.Isol: ApplyGlyphFeature(result, isol, chars, ref i); break; case ArabicCharType.Fina: ApplyGlyphFeature(result, fina, chars, ref i); break; case ArabicCharType.Medi: ApplyGlyphFeature(result, medi, chars, ref i); break; case ArabicCharType.Init: ApplyGlyphFeature(result, init, chars, ref i); break; } } chars = result.ToArray(); result.Clear(); if (lang_sys.Contains("rlig")) { ushort[] rlig = lang_sys["rlig"] as ushort[]; for (int i = 0; i < chars.Length; i++) { ApplyGlyphFeature(result, rlig, chars, ref i); } chars = result.ToArray(); result.Clear(); } if (lang_sys.Contains("calt")) { ushort[] calt = lang_sys["calt"] as ushort[]; for (int i = 0; i < chars.Length; i++) { ApplyGlyphFeature(result, calt, chars, ref i); } chars = result.ToArray(); result.Clear(); } return new List(chars); } enum ArabicCharType { None, Isol, Init, Medi, Fina } private ushort[] ApplySubstLookupRecord(ushort[] resultArr, SubstLookupRecord[] records) { List result = new List(); for (int j = 0; j < records.Length; j++) { result.Clear(); int resultIndex = 0; for (; resultIndex < records[j].GlyphSequenceIndex; resultIndex++) result.Add(resultArr[resultIndex]); LookupEntry le = lookup_list[records[j].LookupListIndex]; for (int k = 0; k < le.subs.Length; k++) { if (le.subs[k].Apply(result, resultArr, ref resultIndex)) { resultIndex += 1; break; } } for (; resultIndex < resultArr.Length; resultIndex++) result.Add(resultArr[resultIndex]); resultArr = result.ToArray(); } return resultArr; } public interface Substitution { // need to index = index + step - 1; /// /// Return true if was applied /// /// /// /// /// bool Apply(List list, ushort[] chars, ref int index); /// /// Return coverageIndex for ApplyForce or if fail then return -1 /// /// /// /// int IsApply(ushort[] chars, int index); /// /// Apply this Substitution with specified coverageIndex, cant be called only after IsApply /// /// /// /// /// void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex); IEnumerable> GetList(LookupTypes[] types); } public interface Coverage { int IsSubstituteGetIndex(ushort ch); ushort[] GetGlyphs(); ushort GetFirstGlyph(); } public struct Coverage1 : Coverage { public ushort GlyphCount; public ushort[] Glyphs; public Coverage1(ushort glyphCount, ushort[] glyphs) { this.GlyphCount = glyphCount; this.Glyphs = glyphs; } public ushort GetFirstGlyph() { if (Glyphs.Length > 0) return Glyphs[0]; return 1; } public ushort[] GetGlyphs() { return Glyphs; } public int IsSubstituteGetIndex(ushort ch) { for (int i = 0; i < GlyphCount; i++) { if (Glyphs[i] == ch) return i; } return -1; } } public struct Coverage2 : Coverage { public ushort GlyphCount; public RangeRecord[] RangeRecords; public Coverage2(ushort glyphCount, RangeRecord[] rangeRecords) { this.GlyphCount = glyphCount; this.RangeRecords = rangeRecords; } public ushort GetFirstGlyph() { if (RangeRecords.Length > 0) return RangeRecords[0].Start; return 1; } public ushort[] GetGlyphs() { Dictionary results = new Dictionary(); for (int i = 0; i < RangeRecords.Length; i++) { for (int j = 0; j + RangeRecords[i].Start <= RangeRecords[i].End; j++) { results[j + RangeRecords[i].StartCoverageIndex] = (ushort)(RangeRecords[i].Start + j); } } ushort[] result = new ushort[results.Count]; foreach (ushort key in results.Keys) { result[key] = results[key]; } return result; } public int IsSubstituteGetIndex(ushort ch) { //var arr = RangeRecords.Select(a => a).OrderBy(a => a.StartCoverageIndex).ToArray(); for (int i = 0; i < GlyphCount; i++) { if (RangeRecords[i].Start <= ch && RangeRecords[i].End >= ch) return RangeRecords[i].End - RangeRecords[i].Start + RangeRecords[i].StartCoverageIndex; } return -1; } } public struct Single1 : Substitution { public short DeltaGlyphIDOrGlyphCount; public Coverage Coverage; public Single1(short deltaGlyphID, Coverage coverage) { this.DeltaGlyphIDOrGlyphCount = deltaGlyphID; this.Coverage = coverage; } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { list.Add((ushort)(chars[index] + DeltaGlyphIDOrGlyphCount)); } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Single)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { yield return new KeyValuePair( new ushort[] { glyphs[i] }, new ushort[] { (ushort)(glyphs[i] + DeltaGlyphIDOrGlyphCount) } ); } } public int IsApply(ushort[] chars, int index) { return Coverage.IsSubstituteGetIndex(chars[index]); } } public struct Single2 : Substitution { public ushort[] Substitutes; public Coverage Coverage; public Single2(ushort[] substitutes, Coverage coverage) { this.Substitutes = substitutes; this.Coverage = coverage; } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { list.Add(Substitutes[coverageIndex]); } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Single)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { yield return new KeyValuePair( new ushort[] { glyphs[i] }, new ushort[] { Substitutes[i] } ); } } public int IsApply(ushort[] chars, int index) { return Coverage.IsSubstituteGetIndex(chars[index]); } } public struct Multiple : Substitution { public ushort[][] Sequences; public Coverage Coverage; public Multiple(ushort[][] sequences, Coverage coverage) { this.Sequences = sequences; this.Coverage = coverage; } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { list.AddRange(Sequences[coverageIndex]); } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Multiple)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { yield return new KeyValuePair( new ushort[] { glyphs[i] }, Sequences[i]); } } public int IsApply(ushort[] chars, int index) { return Coverage.IsSubstituteGetIndex(chars[index]); } } public struct Ligature : Substitution { public Coverage Coverage; public LigatureSet[][] LigatureSets; LigatureSet LastSetIsApply; public Ligature(Coverage coverage, LigatureSet[][] ligatureSets) { this.Coverage = coverage; this.LigatureSets = ligatureSets; LastSetIsApply = new LigatureSet(); } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { index += LastSetIsApply.Components.Length; list.Add(LastSetIsApply.LigGlyph); } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Ligature)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { LigatureSet[] set = LigatureSets[i]; for (int j = 0; j < set.Length; j++) { ushort[] key = new ushort[set[j].Components.Length + 1]; key[0] = glyphs[i]; for (int k = 0; k < set[j].Components.Length; k++) key[k + 1] = set[j].Components[k]; yield return new KeyValuePair( key, new ushort[] { set[j].LigGlyph }); } } } public int IsApply(ushort[] chars, int index) { int index2 = Coverage.IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { LigatureSet[] sets = LigatureSets[index2]; foreach (LigatureSet set in sets) { if (chars.Length - 1 - index - set.Components.Length >= 0) { bool flag = true; for (int i = 0; i < set.Components.Length; i++) { if (set.Components[i] != chars[index + 1 + i]) { flag = false; break; } } if (flag) { LastSetIsApply = set; return index2; } } } } return -1; } } public struct LigatureSet { public ushort LigGlyph; public ushort[] Components; public LigatureSet(ushort ligGlyph, ushort[] components) { this.LigGlyph = ligGlyph; this.Components = components; } } public struct Contextual1 : Substitution { public struct SubRule { public ushort[] InputSequence; public SubstLookupRecord[] Records; public SubRule(ushort[] inputSequence, SubstLookupRecord[] records) { InputSequence = inputSequence; Records = records; } } public SubRule[][] SubRuleSets; public Coverage Coverage; private GlyphSubstitutionClass gsub_table; SubRule LastSubRuleIsApply; public Contextual1(GlyphSubstitutionClass gsub_table, SubRule[][] subRuleSets, Coverage coverage) { this.gsub_table = gsub_table; SubRuleSets = subRuleSets; Coverage = coverage; LastSubRuleIsApply = new SubRule(); } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Context)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { SubRule[] set = SubRuleSets[i]; for (int j = 0; j < set.Length; j++) { ushort[] key = new ushort[set[j].InputSequence.Length + 1]; key[0] = glyphs[i]; for (int k = 0; k < set[j].InputSequence.Length; k++) key[k + 1] = set[j].InputSequence[k]; yield return new KeyValuePair( key, gsub_table.ApplySubstLookupRecord(key, set[j].Records)); } } } public int IsApply(ushort[] chars, int index) { int index2 = Coverage.IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { SubRule[] subRules = SubRuleSets[index2]; foreach (SubRule rule in subRules) { if (chars.Length - 1 - index - rule.InputSequence.Length >= 0) { bool flag = true; for (int i = 0; i < rule.InputSequence.Length; i++) { if (rule.InputSequence[i] != chars[index + 1 + i]) { flag = false; break; } } if (flag) { LastSubRuleIsApply = rule; return index2; } } } } return -1; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { ushort[] resultArr = new ushort[LastSubRuleIsApply.InputSequence.Length + 1]; for (int j = 0; j < resultArr.Length; j++) { resultArr[j] = chars[j + index]; } list.AddRange(gsub_table.ApplySubstLookupRecord(resultArr, LastSubRuleIsApply.Records)); index += LastSubRuleIsApply.InputSequence.Length; } } public struct Contextual2 : Substitution { public struct SubClassRule { public ushort[] InputSequence; public SubstLookupRecord[] Records; public SubClassRule(ushort[] inputSequence, SubstLookupRecord[] records) { InputSequence = inputSequence; Records = records; } } public SubClassRule[][] SubClassRuleSets; private GlyphSubstitutionClass gsub_table; public Coverage Coverage; public ClassDefinition ClassDefinition; SubClassRule LastSubClassRule; public Contextual2(GlyphSubstitutionClass gsub_table, SubClassRule[][] subClassRuleSets, Coverage coverage, ClassDefinition classDefinition) { this.gsub_table = gsub_table; SubClassRuleSets = subClassRuleSets; Coverage = coverage; ClassDefinition = classDefinition; LastSubClassRule = new SubClassRule(); } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Context)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { SubClassRule[] set = SubClassRuleSets[i]; if (set != null) for (int j = 0; j < set.Length; j++) { ushort[] key = new ushort[set[j].InputSequence.Length + 1]; key[0] = glyphs[i]; for (int k = 0; k < set[j].InputSequence.Length; k++) key[k + 1] = ClassDefinition.GetFirstGlyphByClassValue(set[j].InputSequence[k]); yield return new KeyValuePair( key, gsub_table.ApplySubstLookupRecord(key, set[j].Records)); } } } public int IsApply(ushort[] chars, int index) { int index2 = Coverage.IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { SubClassRule[] subClassRules = SubClassRuleSets[index2]; if (subClassRules != null) foreach (SubClassRule rule in subClassRules) { if (chars.Length - 1 - index - rule.InputSequence.Length >= 0) { bool flag = true; for (int i = 0; i < rule.InputSequence.Length; i++) { if (rule.InputSequence[i] != ClassDefinition.GetClassValue(chars[index + 1 + i])) { flag = false; break; } } if (flag) { LastSubClassRule = rule; return index2; } } } } return -1; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { ushort[] resultArr = new ushort[LastSubClassRule.InputSequence.Length + 1]; for (int j = 0; j < resultArr.Length; j++) { resultArr[j] = chars[j + index]; } list.AddRange(gsub_table.ApplySubstLookupRecord(resultArr, LastSubClassRule.Records)); index += LastSubClassRule.InputSequence.Length; } } public struct Contextual3 : Substitution { public SubstLookupRecord[] Records; public Coverage[] Coverages; private GlyphSubstitutionClass gsub_table; public Contextual3(GlyphSubstitutionClass gsub_table, SubstLookupRecord[] records, Coverage[] coverages) { this.gsub_table = gsub_table; Records = records; Coverages = coverages; } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { ushort[] resultArr = new ushort[Coverages.Length]; for (int j = 0; j < resultArr.Length; j++) { resultArr[j] = chars[j + index]; } list.AddRange(gsub_table.ApplySubstLookupRecord(resultArr, Records)); index += Coverages.Length - 1; } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.Context)) yield break; if (Coverages.Length > 0) { ushort[] glyphs = Coverages[0].GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { ushort[] key = new ushort[Coverages.Length]; key[0] = glyphs[i]; for (int k = 1; k < Coverages.Length; k++) key[k] = Coverages[k].GetFirstGlyph(); yield return new KeyValuePair( key, gsub_table.ApplySubstLookupRecord(key, Records)); } } } public int IsApply(ushort[] chars, int index) { if (Coverages.Length > 0) { int index2 = Coverages[0].IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { if (chars.Length - index - Coverages.Length >= 0) { bool flag = true; for (int i = 1; i < Coverages.Length; i++) { if (Coverages[i].IsSubstituteGetIndex(chars[index + i]) < 0) { flag = false; break; } } if (flag) { return index; } } } } return -1; } } public struct ChainingContextual1 : Substitution { public struct ChainSubRule { public ushort[] BacktrackSequence; public ushort[] InputSequence; // count -= 1; public ushort[] LookAheadSequence; public SubstLookupRecord[] Records; public ChainSubRule(ushort[] backtrackSequence, ushort[] inputSequence, ushort[] lookAheadSequence, SubstLookupRecord[] records) { BacktrackSequence = backtrackSequence; InputSequence = inputSequence; LookAheadSequence = lookAheadSequence; Records = records; } } public GlyphSubstitutionClass gsub_table; public ChainSubRule[][] ChainSubRuleSets; public Coverage Coverage; ChainSubRule LastChainSubRule; public ChainingContextual1(GlyphSubstitutionClass gsub_table, Coverage coverage, ChainSubRule[][] chainSubRuleSets) { this.gsub_table = gsub_table; ChainSubRuleSets = chainSubRuleSets; Coverage = coverage; LastChainSubRule = new ChainSubRule(); } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.ChainingContext)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { ChainSubRule[] set = ChainSubRuleSets[i]; for (int j = 0; j < set.Length; j++) { ushort[] key = new ushort[set[j].InputSequence.Length + 1]; key[0] = glyphs[i]; for (int k = 0; k < set[j].InputSequence.Length; k++) key[k + 1] = set[j].InputSequence[k]; yield return new KeyValuePair( key, gsub_table.ApplySubstLookupRecord(key, set[j].Records)); } } } public int IsApply(ushort[] chars, int index) { int index2 = Coverage.IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { ChainSubRule[] subRules = ChainSubRuleSets[index2]; foreach (ChainSubRule rule in subRules) { if (chars.Length - 1 - index - rule.InputSequence.Length >= 0 && chars.Length - 1 - index - rule.LookAheadSequence.Length - rule.InputSequence.Length >= 0 && index >= rule.BacktrackSequence.Length) { bool flag = true; for (int i = 0; i < rule.InputSequence.Length; i++) { if (rule.InputSequence[i] != chars[index + 1 + i]) { flag = false; break; } } if (flag) for (int i = 0; i < rule.BacktrackSequence.Length; i++) { if (rule.BacktrackSequence[i] != chars[index - i - 1]) { flag = false; break; } } if (flag) for (int i = 0; i < rule.LookAheadSequence.Length; i++) { if (rule.LookAheadSequence[i] != chars[index + i + 1 + rule.InputSequence.Length]) { flag = false; break; } } if (flag) { LastChainSubRule = rule; return index2; } } } } return -1; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { ushort[] resultArr = new ushort[LastChainSubRule.InputSequence.Length + 1]; for (int j = 0; j < resultArr.Length; j++) { resultArr[j] = chars[j + index]; } list.AddRange(gsub_table.ApplySubstLookupRecord(resultArr, LastChainSubRule.Records)); index += LastChainSubRule.InputSequence.Length; } } public struct ChainingContextual2 : Substitution { public struct ChainSubClassRule { public ushort[] BacktrackSequence; public ushort[] InputSequence; // count -= 1; public ushort[] LookAheadSequence; public SubstLookupRecord[] Records; public ChainSubClassRule(ushort[] backtrackSequence, ushort[] inputSequence, ushort[] lookAheadSequence, SubstLookupRecord[] records) { BacktrackSequence = backtrackSequence; InputSequence = inputSequence; LookAheadSequence = lookAheadSequence; Records = records; } } public ChainSubClassRule[][] SubClassRuleSets; private GlyphSubstitutionClass gsub_table; public Coverage Coverage; public ClassDefinition BacktrackClassDefinition; public ClassDefinition InputClassDefinition; public ClassDefinition LookaheadClassDefinition; ChainSubClassRule LastChainSubClassRule; public ChainingContextual2( GlyphSubstitutionClass gsub_table, ChainSubClassRule[][] subClassRuleSets, Coverage coverage, ClassDefinition backtrackClassDefinition, ClassDefinition inputClassDefinition, ClassDefinition lookaheadClassDefinition) { this.gsub_table = gsub_table; SubClassRuleSets = subClassRuleSets; Coverage = coverage; BacktrackClassDefinition = backtrackClassDefinition; InputClassDefinition = inputClassDefinition; LookaheadClassDefinition = lookaheadClassDefinition; LastChainSubClassRule = new ChainSubClassRule(); } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.ChainingContext)) yield break; ushort[] glyphs = Coverage.GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { ChainSubClassRule[] set = SubClassRuleSets[i]; if (set != null) for (int j = 0; j < set.Length; j++) { ushort[] key = new ushort[set[j].InputSequence.Length + 1]; key[0] = glyphs[i]; for (int k = 0; k < set[j].InputSequence.Length; k++) key[k + 1] = InputClassDefinition.GetFirstGlyphByClassValue(set[j].InputSequence[k]); yield return new KeyValuePair( key, gsub_table.ApplySubstLookupRecord(key, set[j].Records)); } } } public int IsApply(ushort[] chars, int index) { int index2 = Coverage.IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { ChainSubClassRule[] subClassRules = SubClassRuleSets[index2]; if (subClassRules != null) foreach (ChainSubClassRule rule in subClassRules) { if (chars.Length - 1 - index - rule.InputSequence.Length >= 0 && chars.Length - 1 - index - rule.LookAheadSequence.Length - rule.InputSequence.Length >= 0 && index >= rule.BacktrackSequence.Length) { bool flag = true; for (int i = 0; i < rule.InputSequence.Length; i++) { if (rule.InputSequence[i] != InputClassDefinition.GetClassValue(chars[index + 1 + i])) { flag = false; break; } } if (flag) for (int i = 0; i < rule.BacktrackSequence.Length; i++) { if (rule.BacktrackSequence[i] != BacktrackClassDefinition.GetClassValue(chars[index - i - 1])) { flag = false; break; } } if (flag) for (int i = 0; i < rule.LookAheadSequence.Length; i++) { if (rule.LookAheadSequence[i] != LookaheadClassDefinition.GetClassValue( chars[index + i + 1 + rule.InputSequence.Length])) { flag = false; break; } } if (flag) { LastChainSubClassRule = rule; return index2; } } } } return -1; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { ushort[] resultArr = new ushort[LastChainSubClassRule.InputSequence.Length + 1]; for (int j = 0; j < resultArr.Length; j++) { resultArr[j] = chars[j + index]; } list.AddRange(gsub_table.ApplySubstLookupRecord(resultArr, LastChainSubClassRule.Records)); index += LastChainSubClassRule.InputSequence.Length; } } public struct ChainingContextual3 : Substitution { public SubstLookupRecord[] Records; public Coverage[] BacktrackCoverages; public Coverage[] InputCoverages; public Coverage[] LookaheadCoverages; private GlyphSubstitutionClass gsub_table; public ChainingContextual3(GlyphSubstitutionClass gsub_table, SubstLookupRecord[] records, Coverage[] backtrackCoverages, Coverage[] inputCoverages, Coverage[] lookaheadCoverages) { this.gsub_table = gsub_table; Records = records; BacktrackCoverages = backtrackCoverages; InputCoverages = inputCoverages; LookaheadCoverages = lookaheadCoverages; } public bool Apply(List list, ushort[] chars, ref int index) { int index2 = IsApply(chars, index); if (index2 >= 0) { ApplyForce(list, chars, ref index, index2); return true; } return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { ushort[] resultArr = new ushort[InputCoverages.Length]; for (int j = 0; j < resultArr.Length; j++) { resultArr[j] = chars[j + index]; } list.AddRange(gsub_table.ApplySubstLookupRecord(resultArr, Records)); index += InputCoverages.Length - 1; } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.ChainingContext)) yield break; if (InputCoverages.Length > 0) { ushort[] glyphs = InputCoverages[0].GetGlyphs(); for (int i = 0; i < glyphs.Length; i++) { ushort[] key = new ushort[InputCoverages.Length]; key[0] = glyphs[i]; if (InputCoverages.Length > 0) for (int k = 1; k < InputCoverages.Length; k++) key[k] = InputCoverages[k].GetFirstGlyph(); yield return new KeyValuePair( key, gsub_table.ApplySubstLookupRecord(key, Records)); } } } public int IsApply(ushort[] chars, int index) { if (InputCoverages.Length > 0) { int index2 = InputCoverages[0].IsSubstituteGetIndex(chars[index]); if (index2 >= 0) { if (chars.Length - 1 - index - InputCoverages.Length >= 0 && chars.Length - 1 - index - LookaheadCoverages.Length - InputCoverages.Length >= 0 && index >= BacktrackCoverages.Length) { bool flag = true; for (int i = 1; i < InputCoverages.Length; i++) { if (InputCoverages[i].IsSubstituteGetIndex(chars[index + i]) < 0) { flag = false; break; } } if (flag) for (int i = 0; i < BacktrackCoverages.Length; i++) { if (BacktrackCoverages[i].IsSubstituteGetIndex(chars[index - i - 1]) < 0) { flag = false; break; } } if (flag) for (int i = 0; i < LookaheadCoverages.Length; i++) { if (LookaheadCoverages[i].IsSubstituteGetIndex( chars[index + i + 1 + InputCoverages.Length]) < 0) { flag = false; break; } } if (flag) { return index2; } } } } return -1; } } public struct Extension : Substitution { public Substitution Substitution; public ushort LookupType; public ushort Format; public bool Apply(List list, ushort[] chars, ref int index) { return Substitution.Apply(list, chars, ref index); } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { Substitution.ApplyForce(list, chars, ref index, coverageIndex); } public IEnumerable> GetList(LookupTypes[] types) { if (types != null && !ExtFor2005.Contains(types, LookupTypes.ExtensionSubstitution)) return new KeyValuePair[0]; return Substitution.GetList(types); } public int IsApply(ushort[] chars, int index) { return Substitution.IsApply(chars, index); } } public struct VoidSubstitution : Substitution { public bool Apply(List list, ushort[] chars, ref int index) { return false; } public void ApplyForce(List list, ushort[] chars, ref int index, int coverageIndex) { } public IEnumerable> GetList(LookupTypes[] types) { yield break; } public int IsApply(ushort[] chars, int index) { return -1; } } internal IEnumerable> GetSubstitutions(string script, string language, string feature, LookupTypes[] types) { if (script_list.ContainsKey(script)) { Hashtable lang_sys_hash = script_list[script] as Hashtable; Hashtable lang_sys = null; if (lang_sys_hash.ContainsKey(language)) { lang_sys = lang_sys_hash[language] as Hashtable; } else if (lang_sys_hash.ContainsKey(string.Empty)) { lang_sys = lang_sys_hash[string.Empty] as Hashtable; } if (lang_sys != null) { if (lang_sys.ContainsKey(feature)) { foreach (ushort offset in (ushort[])lang_sys[feature]) { LookupEntry le = (LookupEntry)lookup_list[offset]; for (int i = 0; i < le.subs.Length; i++) { foreach (KeyValuePair sub in le.subs[i].GetList(types)) yield return sub; } } } } } } public struct VoidCoverage : Coverage { public ushort GetFirstGlyph() { return 1; } public ushort[] GetGlyphs() { return new ushort[0]; } public int IsSubstituteGetIndex(ushort ch) { return -1; } } public interface ClassDefinition { ushort GetClassValue(ushort glyph); ushort GetFirstGlyphByClassValue(ushort v); } public struct ClassDefinition1 : ClassDefinition { ushort StartGlyphId; ushort[] ClassValues; public ClassDefinition1(ushort startGlyphId, ushort[] classValues) { StartGlyphId = startGlyphId; ClassValues = classValues; } public ushort GetClassValue(ushort glyph) { int index = glyph - StartGlyphId; if (index >= 0 && index < ClassValues.Length) return ClassValues[index]; return 0; } public ushort GetFirstGlyphByClassValue(ushort v) { for (int i = 0; i < ClassValues.Length; i++) if (ClassValues[i] == v) return (ushort)(StartGlyphId + i); return StartGlyphId; } } public struct ClassDefinition2 : ClassDefinition { ClassRangeRecord[] Records; public ClassDefinition2(ClassRangeRecord[] records) { Records = records; } public ushort GetClassValue(ushort glyph) { foreach (ClassRangeRecord record in Records) if (record.StartGlyphID <= glyph && record.EndGlyphID >= glyph) return record.ClassValue; return 0; } public ushort GetFirstGlyphByClassValue(ushort v) { foreach (ClassRangeRecord record in Records) if (record.ClassValue == v) return record.StartGlyphID; if (Records.Length > 0) return Records[0].StartGlyphID; return 0; } } public struct VoidClassDefinition : ClassDefinition { public ushort GetClassValue(ushort glyph) { return 0; } public ushort GetFirstGlyphByClassValue(ushort v) { //TODO //TO DO // WHAT TO DO??? just return zero value, let it be space plz :) return 1; } } } } #pragma warning restore