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