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 DefaultLangSys; // 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 LangSys; // 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 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(uint feature_idx, out string FeatureTag) { IntPtr feature_list_table_ptr = Increment(gsub_ptr, (int)header.FeatureList); ushort feature_count = SwapUInt16((ushort)Marshal.PtrToStructure(feature_list_table_ptr, typeof(ushort))); if (feature_idx >= feature_count) throw new Exception("Feature index out of bound"); IntPtr feature_record_ptr = Increment(feature_list_table_ptr, (int)(sizeof(ushort) + feature_idx * 6)); FeatureRecord feature_record = (FeatureRecord)Marshal.PtrToStructure(feature_record_ptr, typeof(FeatureRecord)); feature_record.Feature = SwapUInt16(feature_record.Feature); FeatureTag = "" + (char)(0xff & feature_record.FeatureTag) + (char)(0xff & (feature_record.FeatureTag >> 8)) + (char)(0xff & (feature_record.FeatureTag >> 16)) + (char)(0xff & (feature_record.FeatureTag >> 24)); IntPtr feature_table_ptr = Increment(feature_list_table_ptr, feature_record.Feature); FeatureTable feature_table = (FeatureTable)Marshal.PtrToStructure(feature_table_ptr, typeof(FeatureTable)); feature_table.LookupCount = SwapUInt16(feature_table.LookupCount); ushort[] OffsetLookupList = new ushort[feature_table.LookupCount]; IntPtr lookup_list_ptr = Increment(feature_table_ptr, Marshal.SizeOf(feature_table)); for (int i = 0; i < feature_table.LookupCount; i++) { ushort lookuip_index = 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(IntPtr lang_sys_rec_ptr) { Hashtable Features = new Hashtable(); LangSysTable lang_sys_table = (LangSysTable)Marshal.PtrToStructure(lang_sys_rec_ptr, typeof(LangSysTable)); lang_sys_table.LookupOrder = SwapUInt16(lang_sys_table.LookupOrder); lang_sys_table.ReqFeatureIndex = SwapUInt16(lang_sys_table.ReqFeatureIndex); lang_sys_table.FeatureCount = 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] = SwapUInt16((ushort)Marshal.PtrToStructure(feature_index_ptr, typeof(ushort))); string FeatureTag; ushort[] LookupOffsets = LoadFeature(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() { IntPtr script_list_table_ptr = Increment(gsub_ptr, (int)header.ScriptList); ScriptListTable script_list_table = (ScriptListTable)Marshal.PtrToStructure(script_list_table_ptr, typeof(ScriptListTable)); script_list_table.CountScripts = SwapUInt16(script_list_table.CountScripts); IntPtr script_record_ptr = Increment(script_list_table_ptr, Marshal.SizeOf(script_list_table)); for (int i = 0; i < script_list_table.CountScripts; i++) { ScriptListRecord script_record = (ScriptListRecord)Marshal.PtrToStructure(script_record_ptr, typeof(ScriptListRecord)); script_record.ScriptOffset = SwapUInt16(script_record.ScriptOffset); string ScriptTag = "" + (char)(0xff & script_record.ScriptTag) + (char)(0xff & (script_record.ScriptTag >> 8)) + (char)(0xff & (script_record.ScriptTag >> 16)) + (char)(0xff & (script_record.ScriptTag >> 24)); #if DEBUG_TTF Console.WriteLine("[" + ScriptTag + "]"); #endif Hashtable lang_sys_hash = new Hashtable(); script_list.Add(ScriptTag, lang_sys_hash); IntPtr script_table_ptr = Increment(script_list_table_ptr, script_record.ScriptOffset); ScriptTable script_table = (ScriptTable)Marshal.PtrToStructure(script_table_ptr, typeof(ScriptTable)); script_table.DefaultLangSys = SwapUInt16(script_table.DefaultLangSys); script_table.LangSysCount = SwapUInt16(script_table.LangSysCount); IntPtr lang_sys_rec_ptr; if (script_table.DefaultLangSys != 0) { lang_sys_rec_ptr = Increment(script_table_ptr, script_table.DefaultLangSys); #if DEBUG_TTF Console.WriteLine("\t\"!DEF\""); #endif lang_sys_hash.Add("", LoadLanguageSystemTable(lang_sys_rec_ptr)); } lang_sys_rec_ptr = Increment(script_table_ptr, Marshal.SizeOf(script_table)); for (int j = 0; j < script_table.LangSysCount; j++) { LangSysRecord lang_sys_rec = (LangSysRecord)Marshal.PtrToStructure(lang_sys_rec_ptr, typeof(LangSysRecord)); lang_sys_rec.LangSys = SwapUInt16(lang_sys_rec.LangSys); string LangSysTag = "" + (char)(0xff & lang_sys_rec.LangSysTag) + (char)(0xff & (lang_sys_rec.LangSysTag >> 8)) + (char)(0xff & (lang_sys_rec.LangSysTag >> 16)) + (char)(0xff & (lang_sys_rec.LangSysTag >> 24)); #if DEBUG_TTF Console.WriteLine("\t\"" + LangSysTag + "\""); #endif IntPtr lang_sys_rec_ptr_offset = Increment(script_table_ptr, lang_sys_rec.LangSys); lang_sys_hash.Add(LangSysTag, LoadLanguageSystemTable(lang_sys_rec_ptr_offset)); lang_sys_rec_ptr = Increment(lang_sys_rec_ptr, Marshal.SizeOf(lang_sys_rec)); } script_record_ptr = Increment(script_record_ptr, Marshal.SizeOf(script_record)); } } [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) } private void LoadLookupList() { IntPtr lookup_list_table_ptr = Increment(gsub_ptr, (int)header.LookupList); ushort LookupListCount = SwapUInt16((ushort)Marshal.PtrToStructure(lookup_list_table_ptr, typeof(ushort))); lookup_list = new LookupEntry[LookupListCount]; IntPtr lookup_list_ptr = Increment(lookup_list_table_ptr, sizeof(ushort)); for (int i = 0; i < LookupListCount; i++) { ushort lookup_index = SwapUInt16((ushort)Marshal.PtrToStructure(lookup_list_ptr, typeof(ushort))); lookup_list_ptr = Increment(lookup_list_ptr, sizeof(ushort)); IntPtr lookup_list_entry_ptr = Increment(lookup_list_table_ptr, lookup_index); IntPtr lookup_table_ptr = lookup_list_entry_ptr; lookup_list[i].record_header = (LookupTableRecordHeader)Marshal.PtrToStructure(lookup_list_entry_ptr, typeof(LookupTableRecordHeader)); lookup_list_entry_ptr = Increment(lookup_list_entry_ptr, Marshal.SizeOf(lookup_list[i].record_header)); lookup_list[i].record_header.LookupType = SwapUInt16(lookup_list[i].record_header.LookupType); lookup_list[i].record_header.LookupFlag = SwapUInt16(lookup_list[i].record_header.LookupFlag); lookup_list[i].record_header.SubTableCount = SwapUInt16(lookup_list[i].record_header.SubTableCount); lookup_list[i].subtable_offsets = new ushort[lookup_list[i].record_header.SubTableCount]; lookup_list[i].subtable_ptrs = new IntPtr[lookup_list[i].record_header.SubTableCount]; lookup_list[i].subs = new Substitution[lookup_list[i].record_header.SubTableCount]; for (int j = 0; j < lookup_list[i].record_header.SubTableCount; j++) { lookup_list[i].subtable_offsets[j] = SwapUInt16((ushort)Marshal.PtrToStructure(lookup_list_entry_ptr, typeof(ushort))); lookup_list[i].subtable_ptrs[j] = Increment(lookup_table_ptr, lookup_list[i].subtable_offsets[j]); lookup_list_entry_ptr = Increment(lookup_list_entry_ptr, sizeof(ushort)); lookup_list[i].subs[j] = LoadSubstitution(lookup_list[i].record_header.LookupType, lookup_list[i].subtable_ptrs[j]); } //to do: lookup list } } private Substitution LoadSubstitution(ushort lookupType, IntPtr intPtr) { #if DEBUG_TTF Console.WriteLine("\tLookupType[" + ((LookupTypes)lookupType).ToString() + "]"); #endif switch ((LookupTypes)lookupType) { case LookupTypes.Single: return LoadSingleSubstitution(intPtr); case LookupTypes.Multiple: return LoadMultipleSubstitution(intPtr); case LookupTypes.Alternate: break; case LookupTypes.Ligature: return LoadLigaturesSubtable(intPtr); //break; case LookupTypes.Context: return LoadContextSubstitution(intPtr); //break; case LookupTypes.ChainingContext: return LoadChainingContext(intPtr); //break; case LookupTypes.ExtensionSubstitution: ExtensionSubstFormat extensionSubstFormat = (ExtensionSubstFormat) Marshal.PtrToStructure(intPtr, typeof(ExtensionSubstFormat)); extensionSubstFormat.ExtensionLookupType = SwapUInt16(extensionSubstFormat.ExtensionLookupType); extensionSubstFormat.SubstFormat = SwapUInt16(extensionSubstFormat.SubstFormat); extensionSubstFormat.ExtensionOffset = SwapUInt32(extensionSubstFormat.ExtensionOffset); Extension extension = new Extension(); extension.Format = extensionSubstFormat.SubstFormat; extension.LookupType = extensionSubstFormat.ExtensionLookupType; extension.Substitution = LoadSubstitution( extension.LookupType, Increment(intPtr, extensionSubstFormat.ExtensionOffset)); return extension; case LookupTypes.ReverseChainingContextSingle: break; } return new VoidSubstitution(); } private Substitution LoadContextSubstitution(IntPtr lookup_entry) { ushort format = (ushort)Marshal.PtrToStructure(lookup_entry, typeof(ushort)); format = SwapUInt16(format); switch (format) { case 1: ContextSubstFormat1 type1 = (ContextSubstFormat1)Marshal.PtrToStructure(lookup_entry, typeof(ContextSubstFormat1)); type1.SubstFormat = SwapUInt16(type1.SubstFormat); type1.Coverage = SwapUInt16(type1.Coverage); type1.SubRuleSetCount = SwapUInt16(type1.SubRuleSetCount); IntPtr sets_offset = Increment(lookup_entry, Marshal.SizeOf(type1)); Contextual1.SubRule[][] subRuleSets = new Contextual1.SubRule[type1.SubRuleSetCount][]; for (int i = 0; i < type1.SubRuleSetCount; i++) { ushort offset = (ushort)Marshal.PtrToStructure(sets_offset, typeof(ushort)); offset = SwapUInt16(offset); IntPtr subruleset_offset_table = Increment(lookup_entry, offset); ushort subRuleCount = (ushort)Marshal.PtrToStructure(subruleset_offset_table, typeof(ushort)); subRuleCount = SwapUInt16(subRuleCount); IntPtr subrule_offsets = Increment(subruleset_offset_table, Marshal.SizeOf(subRuleCount)); 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 = (SubRuleTable)Marshal.PtrToStructure(subrule_table, typeof(SubRuleTable)); IntPtr subrule_table_arrays = Increment(subrule_table, Marshal.SizeOf(subRule)); subRule.GlyphCount = SwapUInt16(subRule.GlyphCount); subRule.SubstitutionCount = SwapUInt16(subRule.SubstitutionCount); ushort[] glyphs = new ushort[subRule.GlyphCount - 1]; for (int k = 0; k < subRule.GlyphCount - 1; k++) { ushort glyph = (ushort)Marshal.PtrToStructure(subrule_table_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); subrule_table_arrays = Increment(subrule_table_arrays, Marshal.SizeOf(glyph)); glyphs[k] = glyph; } SubstLookupRecord[] records = new SubstLookupRecord[subRule.SubstitutionCount]; for (int k = 0; k < subRule.SubstitutionCount; k++) { SubstLookupRecord record = (SubstLookupRecord)Marshal.PtrToStructure(subrule_table_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = SwapUInt16(record.LookupListIndex); subrule_table_arrays = Increment(subrule_table_arrays, Marshal.SizeOf(record)); records[k] = record; } subRuleSet[j] = new Contextual1.SubRule(glyphs, records); subrule_offsets = Increment(subrule_offsets, Marshal.SizeOf(subRuleOffset)); } subRuleSets[i] = subRuleSet; sets_offset = Increment(lookup_entry, Marshal.SizeOf(offset)); } return new Contextual1(this, subRuleSets, LoadCoverage(Increment(lookup_entry, type1.Coverage))); case 2: ContextSubstFormat2 type2 = (ContextSubstFormat2)Marshal.PtrToStructure(lookup_entry, typeof(ContextSubstFormat2)); type2.SubstFormat = SwapUInt16(type2.SubstFormat); type2.Coverage = SwapUInt16(type2.Coverage); type2.ClassDefOffset = SwapUInt16(type2.ClassDefOffset); type2.SubClassSetCount = SwapUInt16(type2.SubClassSetCount); Contextual2.SubClassRule[][] subClassRuleSets = new Contextual2.SubClassRule[type2.SubClassSetCount][]; IntPtr sub_class_set_offset = Increment(lookup_entry, Marshal.SizeOf(type2)); for (int i = 0; i < type2.SubClassSetCount; i++) { ushort offset = (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); ushort count = (ushort)Marshal.PtrToStructure(sub_class_set_table, typeof(ushort)); count = SwapUInt16(count); IntPtr sub_class_rule_offset = Increment(sub_class_set_table, Marshal.SizeOf(count)); Contextual2.SubClassRule[] subClassSet = new Contextual2.SubClassRule[count]; for (int j = 0; j < count; j++) { ushort offset2 = (ushort)Marshal.PtrToStructure(sub_class_rule_offset, typeof(ushort)); offset2 = SwapUInt16(offset2); IntPtr sub_class_rule_table = Increment(sub_class_set_table, offset2); SubClassRule subClassRule = (SubClassRule)Marshal.PtrToStructure(sub_class_rule_table, typeof(SubClassRule)); IntPtr subclassrule_table_arrays = Increment(sub_class_rule_table, Marshal.SizeOf(subClassRule)); subClassRule.ClassCount = SwapUInt16(subClassRule.ClassCount); subClassRule.SubstitutionCount = SwapUInt16(subClassRule.SubstitutionCount); ushort[] glyphClassess = new ushort[subClassRule.ClassCount - 1]; for (int k = 0; k < subClassRule.ClassCount - 1; k++) { ushort glyphClass = (ushort)Marshal.PtrToStructure(subclassrule_table_arrays, typeof(ushort)); glyphClass = SwapUInt16(glyphClass); subclassrule_table_arrays = Increment(subclassrule_table_arrays, Marshal.SizeOf(glyphClass)); glyphClassess[k] = glyphClass; } SubstLookupRecord[] records = new SubstLookupRecord[subClassRule.SubstitutionCount]; for (int k = 0; k < subClassRule.SubstitutionCount; k++) { SubstLookupRecord record = (SubstLookupRecord)Marshal.PtrToStructure(subclassrule_table_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = SwapUInt16(record.LookupListIndex); subclassrule_table_arrays = Increment(subclassrule_table_arrays, Marshal.SizeOf(record)); records[k] = record; } subClassSet[j] = new Contextual2.SubClassRule(glyphClassess, records); sub_class_rule_offset = Increment(sub_class_rule_offset, Marshal.SizeOf(offset2)); } subClassRuleSets[i] = subClassSet; } sub_class_set_offset = Increment(sub_class_set_offset, Marshal.SizeOf(offset)); } return new Contextual2(this, subClassRuleSets, LoadCoverage(Increment(lookup_entry, type2.Coverage)), LoadClassDefinition(Increment(lookup_entry, type2.ClassDefOffset))); case 3: ContextSubstFormat3 type3 = (ContextSubstFormat3)Marshal.PtrToStructure(lookup_entry, typeof(ContextSubstFormat3)); type3.SubstFormat = SwapUInt16(type3.SubstFormat); type3.GlyphCount = SwapUInt16(type3.GlyphCount); type3.SubstitutionCount = SwapUInt16(type3.SubstitutionCount); Coverage[] coverages = new Coverage[type3.GlyphCount]; SubstLookupRecord[] subRecords = new SubstLookupRecord[type3.SubstitutionCount]; IntPtr type3_arrays = Increment(lookup_entry, Marshal.SizeOf(type3)); for (int i = 0; i < type3.GlyphCount; i++) { ushort offset = (ushort)Marshal.PtrToStructure(type3_arrays, typeof(ushort)); offset = SwapUInt16(offset); coverages[i] = LoadCoverage(Increment(lookup_entry, offset)); type3_arrays = Increment(type3_arrays, Marshal.SizeOf(offset)); } for (int i = 0; i < type3.SubstitutionCount; i++) { SubstLookupRecord record = (SubstLookupRecord)Marshal.PtrToStructure(type3_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = 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(IntPtr lookup_entry) { MultipleSubstFormat1 type1 = (MultipleSubstFormat1)Marshal.PtrToStructure(lookup_entry, typeof(MultipleSubstFormat1)); type1.SubstFormat = SwapUInt16(type1.SubstFormat); type1.Coverage = SwapUInt16(type1.Coverage); type1.SequenceCount = SwapUInt16(type1.SequenceCount); //type1.SequenceOffset = SwapUInt16(type1.SequenceOffset); IntPtr sequence_offset = Increment(lookup_entry, Marshal.SizeOf(type1)); ushort[][] sequences = new ushort[type1.SequenceCount][]; for (int i = 0; i < type1.SequenceCount; i++) { ushort offset = (ushort)Marshal.PtrToStructure(sequence_offset, typeof(ushort)); offset = SwapUInt16(offset); IntPtr sequence_offset_table = Increment(lookup_entry, offset); ushort glyph_count = (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] = (ushort)Marshal.PtrToStructure(sequence_offset_table, typeof(ushort)); glyphs[j] = SwapUInt16(glyphs[j]); sequence_offset_table = Increment(lookup_entry, Marshal.SizeOf(glyphs[j])); } sequences[i] = glyphs; sequence_offset = Increment(lookup_entry, Marshal.SizeOf(offset)); } return new Multiple(sequences, LoadCoverage(Increment(lookup_entry, type1.Coverage))); //return new VoidSubstitution(); } private Substitution LoadSingleSubstitution(IntPtr lookup_entry) { SignleSubstitutionFormat1 type1 = (SignleSubstitutionFormat1)Marshal.PtrToStructure(lookup_entry, typeof(SignleSubstitutionFormat1)); type1.SubstFormat = SwapUInt16(type1.SubstFormat); type1.Coverage = SwapUInt16(type1.Coverage); type1.DeltaGlyphID = SwapInt16(type1.DeltaGlyphID); switch (type1.SubstFormat) { case 1: return new Single1(type1.DeltaGlyphID, LoadCoverage(Increment(lookup_entry, type1.Coverage))); 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] = (ushort)Marshal.PtrToStructure(lookup_entry_substitute, typeof(ushort)); substitute[i] = SwapUInt16(substitute[i]); lookup_entry_substitute = Increment(lookup_entry_substitute, Marshal.SizeOf(typeof(ushort))); } return new Single2(substitute, LoadCoverage(Increment(lookup_entry, type1.Coverage))); } return new VoidSubstitution(); } private ClassDefinition LoadClassDefinition(IntPtr class_definition_ptr) { ushort class_format = (ushort)Marshal.PtrToStructure(class_definition_ptr, typeof(ushort)); class_format = SwapUInt16(class_format); switch (class_format) { case 1: ClassDefFormat1 type1 = (ClassDefFormat1)Marshal.PtrToStructure(class_definition_ptr, typeof(ClassDefFormat1)); type1.ClassFormat = SwapUInt16(type1.ClassFormat); type1.GlyphCount = SwapUInt16(type1.GlyphCount); type1.StartGlyphID = SwapUInt16(type1.StartGlyphID); IntPtr class_value_array = Increment(class_definition_ptr, Marshal.SizeOf(type1)); ushort[] classValues = new ushort[type1.GlyphCount]; for (int i = 0; i < type1.GlyphCount; i++) { ushort classValue = (ushort)Marshal.PtrToStructure(class_value_array, typeof(ushort)); classValue = SwapUInt16(classValue); class_value_array = Increment(class_value_array, Marshal.SizeOf(classValue)); } return new ClassDefinition1(type1.StartGlyphID, classValues); case 2: ClassDefFormat2 type2 = (ClassDefFormat2)Marshal.PtrToStructure(class_definition_ptr, typeof(ClassDefFormat2)); type2.ClassFormat = SwapUInt16(type2.ClassFormat); type2.ClassRangeCount = SwapUInt16(type2.ClassRangeCount); IntPtr class_records_array = Increment(class_definition_ptr, Marshal.SizeOf(type2)); ClassRangeRecord[] records = new ClassRangeRecord[type2.ClassRangeCount]; for (int i = 0; i < type2.ClassRangeCount; i++) { ClassRangeRecord record = (ClassRangeRecord)Marshal.PtrToStructure(class_records_array, typeof(ClassRangeRecord)); record.StartGlyphID = SwapUInt16(record.StartGlyphID); record.EndGlyphID = SwapUInt16(record.EndGlyphID); record.ClassValue = SwapUInt16(record.ClassValue); class_records_array = Increment(class_records_array, Marshal.SizeOf(record)); records[i] = record; } return new ClassDefinition2(records); } return new VoidClassDefinition(); } private Coverage LoadCoverage(IntPtr coverage_table_ptr) { CoverageHeader ch = (CoverageHeader)Marshal.PtrToStructure(coverage_table_ptr, typeof(CoverageHeader)); ch.CoverageFormat = SwapUInt16(ch.CoverageFormat); ch.GlyphCount = 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] = SwapUInt16((ushort)Marshal.PtrToStructure(coverage_table_ptr, typeof(ushort))); coverage_table_ptr = Increment(coverage_table_ptr, Marshal.SizeOf(typeof(ushort))); } /*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] = (RangeRecord)Marshal.PtrToStructure(coverage_table_ptr, typeof(RangeRecord)); rrs[i].Start = SwapUInt16(rrs[i].Start); rrs[i].End = SwapUInt16(rrs[i].End); rrs[i].StartCoverageIndex = 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(IntPtr lookup_entry) { LigatureSubst type4 = (LigatureSubst)Marshal.PtrToStructure(lookup_entry, typeof(LigatureSubst)); type4.SubstFormat = SwapUInt16(type4.SubstFormat); type4.Coverage = SwapUInt16(type4.Coverage); type4.LigSetCount = SwapUInt16(type4.LigSetCount); //To do not null //LoadCoverageTable(null, Increment(lookup_entry, type4.Coverage)); Coverage coverage = LoadCoverage(Increment(lookup_entry, type4.Coverage)); // Ligature Set IntPtr ligature_set_ptr = Increment(lookup_entry, Marshal.SizeOf(type4)); IntPtr current_ptr = ligature_set_ptr; IntPtr[] LigatureSet = new IntPtr[type4.LigSetCount]; LigatureSet[][] ligatureSets = new LigatureSet[type4.LigSetCount][]; for (int i = 0; i < type4.LigSetCount; i++) { ushort offset = SwapUInt16((ushort)Marshal.PtrToStructure(current_ptr, typeof(ushort))); // Console.WriteLine("LigatresSet[" + i + "] at" + offset); LigatureSet[i] = Increment(lookup_entry, offset); // Ligature Table ushort LigatureCount = 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]; for (int j = 0; j < LigatureCount; j++) { ushort lig_offset = SwapUInt16((ushort)Marshal.PtrToStructure(ligature_ptr, typeof(ushort))); LigaturePtrs[j] = Increment(LigatureSet[i], lig_offset); // Console.WriteLine("\tLigature[" + j + "] at " + lig_offset); //// Ligature lh[j] = (LigatureHeader)Marshal.PtrToStructure(LigaturePtrs[j], typeof(LigatureHeader)); lh[j].LigGlyph = SwapUInt16(lh[j].LigGlyph); lh[j].CompCount = SwapUInt16(lh[j].CompCount); ushort[] glyphs = new ushort[lh[j].CompCount - 1]; IntPtr ligature_array = Increment(LigaturePtrs[j], Marshal.SizeOf(lh[j])); for (int k = 0; k < lh[j].CompCount - 1; k++) { glyphs[k] = SwapUInt16((ushort)Marshal.PtrToStructure(ligature_array, typeof(ushort))); ligature_array = Increment(ligature_array, Marshal.SizeOf(glyphs[k])); } //// --------- ligatureSets[i][j] = new LigatureSet(lh[j].LigGlyph, glyphs); ligature_ptr = Increment(ligature_ptr, sizeof(ushort)); } // ----------------- current_ptr = Increment(current_ptr, sizeof(ushort)); } 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 Coverage; // 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 Coverage; // 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(IntPtr lookup_entry) { ChainContextSubstFormat1 type1 = (ChainContextSubstFormat1)Marshal.PtrToStructure(lookup_entry, typeof(ChainContextSubstFormat1)); type1.SubstFormat = SwapUInt16(type1.SubstFormat); type1.ChainSubRuleSetCount = SwapUInt16(type1.ChainSubRuleSetCount); type1.Coverage = SwapUInt16(type1.Coverage); ChainingContextual1.ChainSubRule[][] chainSubRuleSets = new ChainingContextual1.ChainSubRule[type1.ChainSubRuleSetCount][]; IntPtr chain_sub_rule_set_offsets = Increment(lookup_entry, Marshal.SizeOf(type1)); for (int i = 0; i < type1.ChainSubRuleSetCount; i++) { ushort offset = (ushort)Marshal.PtrToStructure(chain_sub_rule_set_offsets, typeof(ushort)); offset = SwapUInt16(offset); IntPtr chain_sub_rule_set_table = Increment(lookup_entry, offset); ushort count = (ushort)Marshal.PtrToStructure(chain_sub_rule_set_table, typeof(ushort)); count = SwapUInt16(count); 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++) { ushort offset2 = (ushort)Marshal.PtrToStructure(chain_sub_rule_offset, typeof(ushort)); offset2 = SwapUInt16(offset2); IntPtr chain_sub_rule_table = Increment(chain_sub_rule_set_table, offset2); IntPtr chain_sub_rule_arrays = chain_sub_rule_table; //start array ushort backtrackGlyphCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); backtrackGlyphCount = SwapUInt16(backtrackGlyphCount); ushort[] backtrackSequence = new ushort[backtrackGlyphCount]; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(backtrackGlyphCount)); for (int k = 0; k < backtrackGlyphCount; k++) { ushort glyph = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); backtrackSequence[k] = glyph; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(glyph)); } //end array //start array ushort inputGlyphCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); inputGlyphCount = SwapUInt16(inputGlyphCount); 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++) { ushort glyph = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); inputSequence[k] = glyph; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(glyph)); } //end array //start array ushort lookaheadGlyphCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); lookaheadGlyphCount = SwapUInt16(lookaheadGlyphCount); ushort[] lookAheadSequence = new ushort[lookaheadGlyphCount]; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(lookaheadGlyphCount)); for (int k = 0; k < lookaheadGlyphCount; k++) { ushort glyph = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); lookAheadSequence[k] = glyph; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(glyph)); } //end array //start array ushort substitutionCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); substitutionCount = SwapUInt16(substitutionCount); SubstLookupRecord[] records = new SubstLookupRecord[substitutionCount]; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(substitutionCount)); for (int k = 0; k < substitutionCount; k++) { SubstLookupRecord record = (SubstLookupRecord)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = SwapUInt16(record.LookupListIndex); records[k] = record; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(record)); } //end array chainSubRuleSet[j] = new ChainingContextual1.ChainSubRule(backtrackSequence, inputSequence, lookAheadSequence, records); chain_sub_rule_offset = Increment(chain_sub_rule_offset, Marshal.SizeOf(offset2)); } chainSubRuleSets[i] = chainSubRuleSet; chain_sub_rule_set_offsets = Increment(chain_sub_rule_set_offsets, Marshal.SizeOf(offset)); } return new ChainingContextual1(this, LoadCoverage(Increment(lookup_entry, type1.Coverage)), chainSubRuleSets); } private Substitution LoadChainingContextSubstFormat2(IntPtr lookup_entry) { ChainContextSubstFormat2 type2 = (ChainContextSubstFormat2)Marshal.PtrToStructure(lookup_entry, typeof(ChainContextSubstFormat2)); type2.SubstFormat = SwapUInt16(type2.SubstFormat); type2.Coverage = SwapUInt16(type2.Coverage); type2.BacktrackClassDefOffset = SwapUInt16(type2.BacktrackClassDefOffset); type2.InputClassDefOffset = SwapUInt16(type2.InputClassDefOffset); type2.LookaheadClassDefOffset = SwapUInt16(type2.LookaheadClassDefOffset); type2.ChainSubClassSetCount = SwapUInt16(type2.ChainSubClassSetCount); IntPtr chain_sub_class_set_offsets = Increment(lookup_entry, Marshal.SizeOf(type2)); ChainingContextual2.ChainSubClassRule[][] chainSubClassRuleSets = new ChainingContextual2.ChainSubClassRule[type2.ChainSubClassSetCount][]; for (int i = 0; i < type2.ChainSubClassSetCount; i++) { ushort offset = (ushort)Marshal.PtrToStructure(chain_sub_class_set_offsets, typeof(ushort)); offset = SwapUInt16(offset); if(offset == 0) { chain_sub_class_set_offsets = Increment(chain_sub_class_set_offsets, Marshal.SizeOf(offset)); continue; } IntPtr chain_sub_class_set_table = Increment(lookup_entry, offset); ushort count = (ushort)Marshal.PtrToStructure(chain_sub_class_set_table, typeof(ushort)); count = SwapUInt16(count); IntPtr chain_sub_class_offset = Increment(chain_sub_class_set_table, Marshal.SizeOf(count)); ChainingContextual2.ChainSubClassRule[] chainSubClassSet = new ChainingContextual2.ChainSubClassRule[count]; for (int j = 0; j < count; j++) { ushort offset2 = (ushort)Marshal.PtrToStructure(chain_sub_class_offset, typeof(ushort)); offset2 = SwapUInt16(offset2); IntPtr chain_sub_rule_table = Increment(chain_sub_class_set_table, offset2); IntPtr chain_sub_rule_arrays = chain_sub_rule_table; //start array ushort backtrackGlyphCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); backtrackGlyphCount = SwapUInt16(backtrackGlyphCount); ushort[] backtrackSequence = new ushort[backtrackGlyphCount]; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(backtrackGlyphCount)); for (int k = 0; k < backtrackGlyphCount; k++) { ushort glyph = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); backtrackSequence[k] = glyph; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(glyph)); } //end array //start array ushort inputGlyphCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); inputGlyphCount = SwapUInt16(inputGlyphCount); 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++) { ushort glyph = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); inputSequence[k] = glyph; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(glyph)); } //end array //start array ushort lookaheadGlyphCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); lookaheadGlyphCount = SwapUInt16(lookaheadGlyphCount); ushort[] lookAheadSequence = new ushort[lookaheadGlyphCount]; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(lookaheadGlyphCount)); for (int k = 0; k < lookaheadGlyphCount; k++) { ushort glyph = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); glyph = SwapUInt16(glyph); lookAheadSequence[k] = glyph; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(glyph)); } //end array //start array ushort substitutionCount = (ushort)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(ushort)); substitutionCount = SwapUInt16(substitutionCount); SubstLookupRecord[] records = new SubstLookupRecord[substitutionCount]; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(substitutionCount)); for (int k = 0; k < substitutionCount; k++) { SubstLookupRecord record = (SubstLookupRecord)Marshal.PtrToStructure(chain_sub_rule_arrays, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = SwapUInt16(record.LookupListIndex); records[k] = record; chain_sub_rule_arrays = Increment(chain_sub_rule_arrays, Marshal.SizeOf(record)); } //end array chainSubClassSet[j] = new ChainingContextual2.ChainSubClassRule(backtrackSequence, inputSequence, lookAheadSequence, records); chain_sub_class_offset = Increment(chain_sub_class_offset, Marshal.SizeOf(offset2)); } chainSubClassRuleSets[i] = chainSubClassSet; chain_sub_class_set_offsets = Increment(chain_sub_class_set_offsets, Marshal.SizeOf(offset)); } return new ChainingContextual2(this, chainSubClassRuleSets, LoadCoverage(Increment(lookup_entry, type2.Coverage)), LoadClassDefinition(Increment(lookup_entry, type2.BacktrackClassDefOffset)), LoadClassDefinition(Increment(lookup_entry, type2.InputClassDefOffset)), LoadClassDefinition(Increment(lookup_entry, type2.LookaheadClassDefOffset))); } private Substitution LoadChainingContextSubstFormat3(IntPtr lookup_entry) { IntPtr chain_format_3_array = Increment(lookup_entry, Marshal.SizeOf(typeof(ushort))); //start array ushort backtrackGlyphCount = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); backtrackGlyphCount = SwapUInt16(backtrackGlyphCount); Coverage[] backtrackSequence = new Coverage[backtrackGlyphCount]; chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(backtrackGlyphCount)); for (int k = 0; k < backtrackGlyphCount; k++) { ushort offset = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); offset = SwapUInt16(offset); backtrackSequence[k] = LoadCoverage(Increment(lookup_entry, offset)); chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(offset)); } //end array //start array ushort inputGlyphCount = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); inputGlyphCount = SwapUInt16(inputGlyphCount); Coverage[] inputSequence = new Coverage[inputGlyphCount]; chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(inputGlyphCount)); for (int k = 0; k < inputGlyphCount; k++) { ushort offset = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); offset = SwapUInt16(offset); inputSequence[k] = LoadCoverage(Increment(lookup_entry, offset)); chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(offset)); } //end array //start array ushort lookaheadGlyphCount = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); lookaheadGlyphCount = SwapUInt16(lookaheadGlyphCount); Coverage[] lookAheadSequence = new Coverage[lookaheadGlyphCount]; chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(lookaheadGlyphCount)); for (int k = 0; k < lookaheadGlyphCount; k++) { ushort offset = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); offset = SwapUInt16(offset); lookAheadSequence[k] = LoadCoverage(Increment(lookup_entry, offset)); chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(offset)); } //end array //start array ushort substitutionCount = (ushort)Marshal.PtrToStructure(chain_format_3_array, typeof(ushort)); substitutionCount = SwapUInt16(substitutionCount); SubstLookupRecord[] records = new SubstLookupRecord[substitutionCount]; chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(substitutionCount)); for (int k = 0; k < substitutionCount; k++) { SubstLookupRecord record = (SubstLookupRecord)Marshal.PtrToStructure(chain_format_3_array, typeof(SubstLookupRecord)); record.GlyphSequenceIndex = SwapUInt16(record.GlyphSequenceIndex); record.LookupListIndex = SwapUInt16(record.LookupListIndex); records[k] = record; chain_format_3_array = Increment(chain_format_3_array, Marshal.SizeOf(record)); } //end array return new ChainingContextual3(this, records, backtrackSequence, inputSequence, lookAheadSequence); } #if DEBUG_TTF static int debug = 0; #endif //[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] [System.Security.SecurityCritical] private Substitution LoadChainingContext(IntPtr lookup_entry) { try { ushort format = (ushort)Marshal.PtrToStructure(lookup_entry, typeof(ushort)); format = SwapUInt16(format); #if DEBUG_TTF debug++; Console.WriteLine("LoadChainingContext format" + format.ToString() + " Iteration " + debug.ToString()); if (debug == 123) ; #endif switch (format) { case 1: return LoadChainingContextSubstFormat1(lookup_entry); case 2: return LoadChainingContextSubstFormat2(lookup_entry); case 3: return LoadChainingContextSubstFormat3(lookup_entry); default: throw new NotSupportedException("Format of ChaingContext"); } } catch (System.AccessViolationException e) { Console.WriteLine(e.StackTrace); } return new VoidSubstitution(); } //[System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] [System.Security.SecurityCritical] internal override void Load(IntPtr font) { try { gsub_ptr = Increment(font, (int)this.Offset); header = (GSUB_Header)Marshal.PtrToStructure(gsub_ptr, typeof(GSUB_Header)); ChangeEndian(); LoadLookupList(); LoadScriptList(); //if(do_features) LoadFeatureList(); #if DEBUG_TTF Console.WriteLine("====================================================="); #endif } catch (System.Exception e) { throw e; } //foreach (DictionaryEntry script in script_list) //{ // Console.WriteLine(script.Key); // foreach (DictionaryEntry language in script.Value as Hashtable) // { // if (language.Key != "") // Console.WriteLine("\t" + language.Key); // else // Console.WriteLine("\tDEFAULT"); // foreach (DictionaryEntry features in language.Value as Hashtable) // { // Console.WriteLine("\t\t" + features.Key /*+ "--> LookupTypes:" */ ); // foreach (ushort offset in features.Value as ushort[]) // { // LookupEntry le = (LookupEntry)lookup_list[offset]; // // for (int i = 0; i < le.record_header.SubTableCount; i++) // { // Console.Write("\t\t\t"); // switch ((LookupTypes)le.record_header.LookupType) // { // case LookupTypes.Single: // Console.WriteLine("Single"); // break; // case LookupTypes.Multiple: // Console.WriteLine("Multiple"); // break; // case LookupTypes.Alternate: // Console.WriteLine("Alternate"); // break; // case LookupTypes.Ligature: // Console.WriteLine("Ligature"); // break; // case LookupTypes.Context: // Console.WriteLine("Context"); // break; // case LookupTypes.ChainingContext: // Console.WriteLine("ChainingContext"); // break; // case LookupTypes.ExtensionSubstitution: // Console.WriteLine("ExtensionSubstitution"); // break; // case LookupTypes.ReverseChainingContextSingle: // Console.WriteLine("ReverseChainingContextSingle"); // break; // default: // Console.WriteLine("!!! Unknown lookup type"); // break; // } // } // } // } // } //} //Console.WriteLine("====================================================="); } internal override uint Save(IntPtr 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