Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / wpf / src / Core / CSharp / MS / Internal / Shaping / OpenTypeCommon.cs / 1 / OpenTypeCommon.cs
//+------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation, 2002 // // File: Common.cs // // Contents: OpentTypeLayout higher level internal engine and structures // // contact: sergeym // // History: 2002-03-23 Created (sergeym) // //----------------------------------------------------------------------- using System.Diagnostics; using System.Security; using System.Security.Permissions; using System; using System.IO; using MS.Internal; namespace MS.Internal.Shaping { ////// Provide core layout functionality /// internal static unsafe class LayoutEngine { ////// Main layout loop. Execute lookups in right order for enabled ranges /// /// In: Font access interface /// In: Layout engine wokrspace /// In: Layout table tag (GSUB or GPOS) /// In: Layout Table (GSUB or GPOS) /// In: Layout metrics /// In: Language system table /// In: FeatureList /// In: Lookup list /// In: List of feature to apply /// In: Actual number of features in/// In: offset of input characters inside FeatureSet /// In: Characters count (i.e. Charmap.Length) /// InOut: Character to glyph method /// InOut: List of Glyph Information structures /// InOut: Glyph advances (used only for positioning) /// InOut: Glyph offsets (used only for positioning) /// /// Critical - calls critical code, accepts pointer parameters, unsafe code /// [SecurityCritical] public static void ApplyFeatures( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, OpenTypeTags TableTag, FontTable Table, LayoutMetrics Metrics, LangSysTable LangSys, FeatureList Features, LookupList Lookups, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList GlyphInfo, int* Advances, LayoutOffset* Offsets ) { UpdateGlyphFlags(Font, GlyphInfo, 0, GlyphInfo.Length, false, GlyphFlags.NotChanged); // if client did not supply us with workspace // we will create our own (temporarily) if (workspace == null) { workspace = new OpenTypeLayoutWorkspace(); } ushort lookupCount=Lookups.LookupCount(Table); //Compile feature set CompileFeatureSet( FeatureSet, featureCount, featureSetOffset, CharCount, Table, LangSys, Features, lookupCount, workspace ); OpenTypeLayoutCache.InitCache(Font, TableTag, GlyphInfo, workspace); for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } int firstChar=0, afterLastChar=0, firstGlyph=0, afterLastGlyph=0; OpenTypeLayoutCache.FindNextLookup(workspace, GlyphInfo, lookupIndex, out lookupIndex, out firstGlyph); // We need to check this again, because FindNextLookup will change lookupIndex if (lookupIndex >= lookupCount) { break; } if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } LookupTable lookup = Lookups.Lookup(Table, lookupIndex); uint parameter=0; bool isLookupReversal = IsLookupReversal(TableTag, lookup.LookupType()); while(firstGlyph < GlyphInfo.Length) // While we have ranges to work on { if (!OpenTypeLayoutCache.FindNextGlyphInLookup(workspace, lookupIndex, isLookupReversal, ref firstGlyph, ref afterLastGlyph)) { firstGlyph = afterLastGlyph; } if (firstGlyph < afterLastGlyph) // Apply lookup while in one range { int nextGlyph; int oldLength = GlyphInfo.Length; int glyphsAfterLastChar = oldLength - afterLastGlyph; bool match = ApplyLookup( Font, // In: Font access interface TableTag, // Layout table tag (GSUB or GPOS) Table, // Layout table (GSUB or GPOS) Metrics, // In: LayoutMetrics lookup, // Lookup definition structure CharCount, Charmap, // In: Char to glyph mapping GlyphInfo, // In/out: List of GlyphInfo structs Advances, // In/out: Glyph adv.widths Offsets, // In/out: Glyph offsets firstGlyph, // where to apply it afterLastGlyph, // how long is a context we can use parameter, // lookup parameter 0, // Nesting level for contextual lookups out nextGlyph // out: next glyph index // !!!: for reversal lookup, should // return new afterLastGlyph ); if (match) { //Adjust range end if length changed, // for reversal changes happens beyond afterLast, no change needed if (!isLookupReversal) { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, firstGlyph, nextGlyph); afterLastGlyph = GlyphInfo.Length - glyphsAfterLastChar; firstGlyph = nextGlyph; } else { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, nextGlyph, afterLastGlyph); afterLastGlyph = nextGlyph; } } else { if (isLookupReversal) afterLastGlyph = nextGlyph; else firstGlyph = nextGlyph; } } else // End of range. Get next { GetNextEnabledGlyphRange( FeatureSet, featureCount, featureSetOffset, Table, workspace, LangSys, Features, lookupIndex, CharCount, Charmap, afterLastChar, afterLastGlyph, GlyphInfo.Length, out firstChar, out afterLastChar, out firstGlyph, out afterLastGlyph, out parameter); } } } } ////// Critical - calls critical code (GetNextGlyphInLookup), /// accepts pointer parameters, unsafe code /// [SecurityCritical] internal static bool ApplyLookup( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics LookupTable Lookup, // List definition structure int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Char to glyph mapping GlyphInfoList GlyphInfo, // List of GlyphInfo structs int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use uint Parameter, // lookup parameter int nestingLevel, // Contextual lookup nesting level out int NextGlyph // out: next glyph index ) { Debug.Assert(TableTag==OpenTypeTags.GSUB || TableTag==OpenTypeTags.GPOS); Debug.Assert(FirstGlyph=AfterLastGlyph) return match; ushort originalLookupType = lookupType; // We need it to recover, if extension lookup updated lookupFormat for(ushort subtableIndex=0; !match && subtableIndex < subtableCount; subtableIndex++) { lookupType = originalLookupType; int subtableOffset = Lookup.SubtableOffset(Table, subtableIndex); switch (TableTag) { case OpenTypeTags.GSUB: { if (lookupType == 7) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(Table); subtableOffset = extension.LookupSubtableOffset(Table); } switch (lookupType) { case 1: //SingleSubst SingleSubstitutionSubtable singleSub = new SingleSubstitutionSubtable(subtableOffset); match = singleSub.Apply( Table, GlyphInfo, FirstGlyph, out NextGlyph ); break; case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); match = multipleSub.Apply( Font, Table, CharCount, Charmap, GlyphInfo, lookupFlags, FirstGlyph, AfterLastGlyph, out NextGlyph ); break; case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); match = alternateSub.Apply( Table, GlyphInfo, Parameter, FirstGlyph, out NextGlyph ); break; case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); match = ligaSub.Apply( Font, Table, CharCount, Charmap, GlyphInfo, lookupFlags, FirstGlyph, AfterLastGlyph, out NextGlyph ); break; case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); match = contextSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); match = chainingSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 7: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it. // Just skip it (do nothing); NextGlyph = FirstGlyph + 1; break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); match = reverseChainingSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, out NextGlyph ); break; default: // Unknown format NextGlyph = FirstGlyph+1; break; } if (match) { if (!IsLookupReversal(TableTag,lookupType)) { UpdateGlyphFlags(Font,GlyphInfo,FirstGlyph,NextGlyph,true,GlyphFlags.Substituted); } else { UpdateGlyphFlags(Font,GlyphInfo,NextGlyph,AfterLastGlyph,true,GlyphFlags.Substituted); } } break; } case OpenTypeTags.GPOS: { if (lookupType == 9) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(Table); subtableOffset = extension.LookupSubtableOffset(Table); } switch (lookupType) { case 1: //SinglePos SinglePositioningSubtable singlePos = new SinglePositioningSubtable(subtableOffset); match = singlePos.Apply(Table, Metrics, GlyphInfo, Advances, Offsets, FirstGlyph, AfterLastGlyph, out NextGlyph ); break; case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); match = pairPos.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 3: // CursivePos // Under construction CursivePositioningSubtable cursivePositioningSubtable = new CursivePositioningSubtable(subtableOffset); cursivePositioningSubtable.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); match = markToBasePos.Apply(Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); match = markToLigaPos.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups CharCount, // Characters count (i.e. Charmap.Length); Charmap, // Char to glyph mapping Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); match = markToMarkPos.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 7: // Contextual ContextSubtable contextSub = new ContextSubtable(subtableOffset); match = contextSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 8: // Chaining ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); match = chainingSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 9: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it. // Just skip it (do nothing); NextGlyph = FirstGlyph + 1; break; default: // Unknown format NextGlyph = FirstGlyph + 1; break; } if (match) { UpdateGlyphFlags(Font,GlyphInfo,FirstGlyph,NextGlyph,false,GlyphFlags.Positioned); } break; } default: Debug.Assert(false,"Unknown OpenType layout table!"); break; } } return match; } private static bool IsLookupReversal(OpenTypeTags TableTag, ushort LookupType) { return (TableTag == OpenTypeTags.GSUB && LookupType == 8); } /// /// Critical - The method reads into raw font table bits. /// [SecurityCritical] private static void CompileFeatureSet( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual number of features in FeatureSet int featureSetOffset, //In: Offset of character input sequence inside feature set int charCount, // In: number of characters in the input string FontTable Table, // In: Layout table (GSUB or GPOS) LangSysTable LangSys, // In: Language system FeatureList Features, // In: List of Features in layout table int lookupCount, // In: number of lookup in layout table OpenTypeLayoutWorkspace workspace // In: workspace with compiled feature set ) { workspace.InitLookupUsageFlags(lookupCount, featureCount); //Set lookup uasge flags for required feature FeatureTable requiredFeatureTable = LangSys.RequiredFeature(Table, Features); if (!requiredFeatureTable.IsNull) { int featureLookupCount = requiredFeatureTable.LookupCount(Table); for(ushort lookup = 0; lookup < featureLookupCount; lookup++) { workspace.SetRequiredFeatureFlag(requiredFeatureTable.LookupIndex(Table,lookup)); } } //Set lookup usage flags for each feature in the FeatureSet for(int feature = 0; feature < featureCount; feature++) { Feature featureDescription = FeatureSet[feature]; //Filter out features which: // Not enabled or applied completely before or after input characters if (featureDescription.Parameter == 0 || featureDescription.StartIndex >= (featureSetOffset + charCount) || (featureDescription.StartIndex+featureDescription.Length) <= featureSetOffset ) { continue; } FeatureTable featureTable = LangSys.FindFeature( Table, Features, featureDescription.Tag); if (featureTable.IsNull) { continue; } int featureLookupCount = featureTable.LookupCount(Table); for(ushort lookup = 0; lookup < featureLookupCount; lookup++) { workspace.SetFeatureFlag(featureTable.LookupIndex(Table,lookup), feature); } } } ////// Critical - This method reads into unsafe cluster map. /// [SecurityCritical] private static void GetNextEnabledGlyphRange( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual nubmer of features in FeatureSet int featureSetOffset, // In: offset of input chars inside feature set FontTable Table, // Layout table (GSUB or GPOS) OpenTypeLayoutWorkspace workspace, // workspace with compiled feature set LangSysTable LangSys, // Language system FeatureList Features, // List of Features in layout table ushort lookupIndex, // List of lokups definitions in layout table int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Character to glyph mapping int StartChar, int StartGlyph, int GlyphRunLength, out int FirstChar, // First char in enabled range out int AfterLastChar, // next char after enabled range out int FirstGlyph, // First char in enabled range out int AfterLastGlyph, // next char after enabled range out uint Parameter // applied feature parameter ) { FirstChar = int.MaxValue; AfterLastChar = int.MaxValue; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; Parameter = 0; if (workspace.IsRequiredFeatureFlagSet(lookupIndex)) { FirstChar = StartChar; AfterLastChar = CharCount; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; return; } for(int feature=0; feature < featureCount; feature++) { if (!workspace.IsFeatureFlagSet(lookupIndex,feature)) { continue; } Feature featureDescription = FeatureSet[feature]; // Shift values from the feature by specified offset and // work with these values from here int featureStart = featureDescription.StartIndex - featureSetOffset; if (featureStart < 0) { featureStart = 0; } int featureAfterEnd = featureDescription.StartIndex + featureDescription.Length - featureSetOffset; if (featureAfterEnd > CharCount) { featureAfterEnd = CharCount; } //If feature is disabled there should not be any flag set Debug.Assert(featureDescription.Parameter != 0); if (featureAfterEnd <= StartChar) { continue; } if (featureStart < FirstChar || ( featureStart == FirstChar && featureAfterEnd >= AfterLastChar ) ) { FirstChar = featureStart; AfterLastChar = featureAfterEnd; Parameter = featureDescription.Parameter; continue; } } //No ranges found if (FirstChar == int.MaxValue) { FirstGlyph = GlyphRunLength; AfterLastGlyph = GlyphRunLength; } else { if (StartGlyph > Charmap[FirstChar]) FirstGlyph = StartGlyph; else FirstGlyph = Charmap[FirstChar]; if (AfterLastChar < CharCount) AfterLastGlyph = Charmap[AfterLastChar]; else AfterLastGlyph = GlyphRunLength; } } ////// Critical - access protected font information (FontTable) /// [SecurityCritical] private static void UpdateGlyphFlags( IOpenTypeFont Font, GlyphInfoList GlyphInfo, int FirstGlyph, int AfterLastGlyph, bool DoAll, GlyphFlags FlagToSet ) { Debug.Assert( FlagToSet==GlyphFlags.NotChanged || FlagToSet==GlyphFlags.Substituted || FlagToSet==GlyphFlags.Positioned); ushort typemask = (ushort)GlyphFlags.GlyphTypeMask; FontTable gdefTable = Font.GetFontTable(OpenTypeTags.GDEF); if (!gdefTable.IsPresent) { //GDEF(i.e. class def in it) is not present. //Assign unassigned to all glyphs for(int i=FirstGlyph;i/// Critical - Access protected font information (FontTable). /// [SecurityCritical] internal static int GetNextGlyphInLookup( IOpenTypeFont Font, // GlyphInfoList GlyphInfo, // Glyph run int FirstGlyph, // Current glyph index ushort LookupFlags, // Lookup flags to use int Direction // Search direction (forward/back) ) { FontTable gdefTable; ClassDefTable markAttachClassDef; //assign them only to avoid error: using unassigned variable gdefTable = null; markAttachClassDef = ClassDefTable.InvalidClassDef; if (LookupFlags==0) return FirstGlyph; //we will mark classes only if mark filter is set if ((LookupFlags&(ushort)LookupFlagMarkAttachmentTypeMask)!=0) { gdefTable = Font.GetFontTable(OpenTypeTags.GDEF); if (gdefTable.IsPresent) { markAttachClassDef = (new GDEFHeader(0)).GetMarkAttachClassDef(gdefTable); } } UshortList glyphFlags = GlyphInfo.GlyphFlags; ushort attachClass = (ushort)((LookupFlags&LookupFlagMarkAttachmentTypeMask)>>8); int glyph; int glyphRunLength = GlyphInfo.Length; for(glyph=FirstGlyph; glyph =0; glyph+=Direction) { if ( (LookupFlags&LookupFlagIgnoreBases)!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Base ) continue; if ( (LookupFlags&LookupFlagIgnoreMarks)!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Mark ) continue; if ( (LookupFlags&LookupFlagIgnoreLigatures)!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Ligature ) continue; if (attachClass!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Mark && !markAttachClassDef.IsInvalid && attachClass!=markAttachClassDef.GetClass(gdefTable,GlyphInfo.Glyphs[glyph]) ) continue; return glyph; } return glyph; } /// /// Returns list of the languages, that can not be optimized for simple shaping /// ////// Critical - The method reads into raw font table bits /// [SecurityCritical] internal static void GetComplexLanguageList( OpenTypeTags tableTag, FontTable table, uint[] featureTagsList, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId, out WritingSystem[] complexLanguages, out int complexLanguageCount ) { ScriptList scriptList = new ScriptList(0); FeatureList featureList = new FeatureList(0); LookupList lookupList = new LookupList(0); Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); switch (tableTag) { case OpenTypeTags.GSUB: GSUBHeader gsubHeader = new GSUBHeader(0); scriptList = gsubHeader.GetScriptList(table); featureList = gsubHeader.GetFeatureList(table); lookupList = gsubHeader.GetLookupList(table); break; case OpenTypeTags.GPOS: GPOSHeader gposHeader = new GPOSHeader(0); scriptList = gposHeader.GetScriptList(table); featureList = gposHeader.GetFeatureList(table); lookupList = gposHeader.GetLookupList(table); break; } int scriptCount = scriptList.GetScriptCount(table); int featureCount = featureList.FeatureCount(table); int lookupCount = lookupList.LookupCount(table); // We will mark lookups that should be tested. // At the end, we will have only complex ones marked uint[] lookupBits = new uint[(lookupCount+31)>>5]; for(int i = 0; i < (lookupCount+31)>>5; i++) { lookupBits[i] = 0; } // Iterate through list of featuers in the table for(ushort featureIndex = 0; featureIndex < featureCount; featureIndex++) { uint featureTag = (uint)featureList.FeatureTag(table, featureIndex); bool tagFound = false; //Search for tag in the list of features in question for(int j = 0; j < featureTagsList.Length; j++) { if (featureTagsList[j] == featureTag) { tagFound = true; break; } } if (tagFound) { // We should mark all lookup mapped to this feature FeatureTable featureTable = featureList.FeatureTable(table, featureIndex); ushort featureLookupCount = featureTable.LookupCount(table); for(ushort j = 0; j < featureLookupCount; j++) { ushort lookupIndex = featureTable.LookupIndex(table, j); if (lookupIndex >= lookupCount) { //This should be invalid font. Lookup associated with the feature is not in lookup array. throw new FileFormatException(); } lookupBits[lookupIndex>>5] |= (uint)(1 << (lookupIndex%32)); } } } // // //Now test all marked lookups for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if ((lookupBits[lookupIndex>>5] & (1 << (lookupIndex%32))) == 0) { continue; } LookupTable lookup = lookupList.Lookup(table,lookupIndex); ushort lookupType = lookup.LookupType(); ushort subtableCount = lookup.SubTableCount(); bool lookupIsCovered = false; ushort originalLookupType = lookupType; // We need it to recover, // if extension lookup updated lookupFormat for(ushort subtableIndex = 0; !lookupIsCovered && subtableIndex < subtableCount; subtableIndex++) { lookupType = originalLookupType; int subtableOffset = lookup.SubtableOffset(table, subtableIndex); switch (tableTag) { case OpenTypeTags.GSUB: { if (lookupType == 7) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SingleSubst SingleSubstitutionSubtable singleSub = new SingleSubstitutionSubtable(subtableOffset); lookupIsCovered = singleSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); lookupIsCovered = multipleSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); lookupIsCovered = alternateSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); lookupIsCovered = ligaSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); lookupIsCovered = contextSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); lookupIsCovered = chainingSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 7: //Extension lookup Debug.Assert(false,"Ext.Lookup processed earlier!"); break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); lookupIsCovered = reverseChainingSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; default: // Unknown format lookupIsCovered = true; break; } break; } case OpenTypeTags.GPOS: { if (lookupType == 9) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SinglePos SinglePositioningSubtable singlePos = new SinglePositioningSubtable(subtableOffset); lookupIsCovered = singlePos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); lookupIsCovered = pairPos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 3: // CursivePos // Under construction CursivePositioningSubtable cursivePositioningSubtable = new CursivePositioningSubtable(subtableOffset); lookupIsCovered = cursivePositioningSubtable.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); lookupIsCovered = markToBasePos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); lookupIsCovered = markToLigaPos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); lookupIsCovered = markToMarkPos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 7: // Contextual ContextSubtable contextSub = new ContextSubtable(subtableOffset); lookupIsCovered = contextSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 8: // Chaining ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); lookupIsCovered = chainingSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 9: //Extension lookup Debug.Assert(false,"Ext.Lookup processed earlier!"); break; default: // Unknown format lookupIsCovered = true; break; } break; } default: Debug.Assert(false,"Unknown OpenType layout table!"); break; } } if (!lookupIsCovered) { // Clean the flag lookupBits[lookupIndex>>5] &= ~(uint)(1 << (lookupIndex%32)); } } // Check if we have any lookup left bool complexLookupFound = false; for(int i = 0; i < (lookupCount+31)>>5; i++) { if (lookupBits[i] != 0) { complexLookupFound = true; break; } } if (!complexLookupFound) { // There are no complex lookups complexLanguages = null; complexLanguageCount = 0; return; } // Now go through all langauages and fill the list complexLanguages = new WritingSystem[10]; complexLanguageCount = 0; for(ushort scriptIndex = 0; scriptIndex < scriptCount; scriptIndex++) { ScriptTable scriptTable = scriptList.GetScriptTable(table, scriptIndex); uint scriptTag = scriptList.GetScriptTag(table, scriptIndex); ushort langSysCount = scriptTable.GetLangSysCount(table); if (scriptTable.IsDefaultLangSysExists(table)) { AppendLangSys(scriptTag, (uint)OpenTypeTags.dflt, scriptTable.GetDefaultLangSysTable(table), featureList, table, featureTagsList, lookupBits, ref complexLanguages, ref complexLanguageCount ); } for(ushort langSysIndex = 0; langSysIndex < langSysCount; langSysIndex++) { uint langSysTag = scriptTable.GetLangSysTag(table, langSysIndex); AppendLangSys(scriptTag, langSysTag, scriptTable.GetLangSysTable(table, langSysIndex), featureList, table, featureTagsList, lookupBits, ref complexLanguages, ref complexLanguageCount ); } } } ////// Critical - The method reads into raw font table bits. /// [SecurityCritical] private static void AppendLangSys( uint scriptTag, uint langSysTag, LangSysTable langSysTable, FeatureList featureList, FontTable table, uint[] featureTagsList, uint[] lookupBits, ref WritingSystem[] complexLanguages, ref int complexLanguageCount ) { ushort featureCount = langSysTable.FeatureCount(table); bool complexFeatureFound = false; // for(ushort i = 0; !complexFeatureFound && i < featureCount; i++) { ushort featureIndex = langSysTable.GetFeatureIndex(table, i); uint featureTag = featureList.FeatureTag(table,featureIndex); bool tagFound = false; for(int j = 0; !complexFeatureFound && j < featureTagsList.Length; j++) { if (featureTagsList[j] == featureTag) { tagFound = true; break; } } if (tagFound) { // We should check if any of lookups is complex FeatureTable featureTable = featureList.FeatureTable(table, featureIndex); ushort featureLookupCount = featureTable.LookupCount(table); for(ushort j = 0; j < featureLookupCount; j++) { ushort lookupIndex = featureTable.LookupIndex(table, j); if ((lookupBits[lookupIndex>>5] & (1 << (lookupIndex%32))) != 0) { complexFeatureFound = true; break; } } } } if (complexFeatureFound) { if (complexLanguages.Length == complexLanguageCount) { WritingSystem[] newComplexLanguages = new WritingSystem[complexLanguages.Length * 3 /2]; for(int i = 0; i < complexLanguages.Length; i++) { newComplexLanguages[i] = complexLanguages[i]; } complexLanguages = newComplexLanguages; } complexLanguages[complexLanguageCount].scriptTag = scriptTag; complexLanguages[complexLanguageCount].langSysTag = langSysTag; complexLanguageCount++; } } } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct GSUBHeader { private const int offsetScriptList = 4; private const int offsetFeatureList = 6; private const int offsetLookupList = 8; public ScriptList GetScriptList(FontTable Table) { return new ScriptList(offset+Table.GetOffset(offset+offsetScriptList)); } public FeatureList GetFeatureList(FontTable Table) { return new FeatureList(offset+Table.GetOffset(offset+offsetFeatureList)); } public LookupList GetLookupList(FontTable Table) { return new LookupList(offset+Table.GetOffset(offset+offsetLookupList)); } public GSUBHeader(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct GPOSHeader { private const int offsetScriptList = 4; private const int offsetFeatureList = 6; private const int offsetLookupList = 8; public ScriptList GetScriptList(FontTable Table) { return new ScriptList(offset+Table.GetOffset(offset+offsetScriptList)); } public FeatureList GetFeatureList(FontTable Table) { return new FeatureList(offset+Table.GetOffset(offset+offsetFeatureList)); } public LookupList GetLookupList(FontTable Table) { return new LookupList(offset+Table.GetOffset(offset+offsetLookupList)); } public GPOSHeader(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct GDEFHeader { private const int offsetGlyphClassDef = 4; private const int offsetGlyphAttachList = 6; private const int offsetLigaCaretList = 8; private const int offsetMarkAttachClassDef = 10; public ClassDefTable GetGlyphClassDef(FontTable Table) { //There is only one place where GDEF classdef is retireved - // UpdateGlyphFlags. We will check if GDEF is present there Invariant.Assert(Table.IsPresent); return new ClassDefTable(offset + Table.GetOffset(offset + offsetGlyphClassDef)); } // ///// /// Under construction // /// Glyph attachment points list // /// // /// // /////public GlyphAttachList GetGlyphAttachList(FontTable Table) //{ // return new GlyphAttachList(offset + Table.GetOffset(offset + offsetGlyphAttachList)); //} // /// // /// Under construction // /// Ligature caret positioning data // /// // /// // ///// //public LigatureCaretList GetLigatureCaretlist(FontTable Table) // //{ // // return new LigatureCaretList(offset + Table.GetOffset(offset + offsetLigaCaretList)); // //} public ClassDefTable GetMarkAttachClassDef(FontTable Table) { //There is only one place where GDEF classdef is retireved - // GetNextGlyphInLokup We will check if GDEF is present there. Invariant.Assert(Table.IsPresent); return new ClassDefTable(offset+Table.GetOffset(offset+offsetMarkAttachClassDef)); } public GDEFHeader(int Offset) { offset = Offset; } private int offset; } /// /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ScriptList { private const int offsetScriptCount = 0; private const int offsetScriptRecordArray = 2; private const int sizeScriptRecord = 6; private const int offsetScriptRecordTag = 0; private const int offsetScriptRecordOffset = 4; public ScriptTable FindScript(FontTable Table, uint Tag) { for(ushort i=0;i/// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ScriptTable { private const int offsetDefaultLangSys = 0; private const int offsetLangSysCount = 2; private const int offsetLangSysRecordArray = 4; private const int sizeLangSysRecord = 6; private const int offsetLangSysRecordTag = 0; private const int offsetLangSysRecordOffset = 4; public LangSysTable FindLangSys(FontTable Table, uint Tag) { if (IsNull) { return new LangSysTable(FontTable.InvalidOffset); } if ((OpenTypeTags)Tag==OpenTypeTags.dflt) { if (IsDefaultLangSysExists(Table)) return new LangSysTable(offset + Table.GetOffset(offset + offsetDefaultLangSys)); return new LangSysTable(FontTable.InvalidOffset); } for(ushort i=0;i /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct LangSysTable { private const int offsetRequiredFeature = 2; private const int offsetFeatureCount = 4; private const int offsetFeatureIndexArray = 6; private const int sizeFeatureIndex = 2; public FeatureTable FindFeature(FontTable Table, FeatureList Features, uint FeatureTag) { ushort featureCount = FeatureCount(Table); for(ushort i=0;i /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct FeatureList { private const int offsetFeatureCount = 0; private const int offsetFeatureRecordArray = 2; private const int sizeFeatureRecord = 6; private const int offsetFeatureRecordTag = 0; private const int offsetFeatureRecordOffset = 4; public ushort FeatureCount(FontTable Table) { return Table.GetUShort(offset + offsetFeatureCount); } public uint FeatureTag(FontTable Table,ushort Index) { return Table.GetUInt(offset + offsetFeatureRecordArray + Index * sizeFeatureRecord + offsetFeatureRecordTag); } public FeatureTable FeatureTable(FontTable Table,ushort Index) { return new FeatureTable(offset+Table.GetUShort(offset + offsetFeatureRecordArray + Index * sizeFeatureRecord + offsetFeatureRecordOffset)); } public FeatureList(int Offset) { offset = Offset; } private int offset; } /// /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct FeatureTable { private const int offsetLookupCount = 2; private const int offsetLookupIndexArray = 4; private const int sizeLookupIndex = 2; public ushort LookupCount(FontTable Table) { return Table.GetUShort(offset + offsetLookupCount); } public ushort LookupIndex(FontTable Table,ushort Index) { return Table.GetUShort(offset + offsetLookupIndexArray + Index*sizeLookupIndex); } public FeatureTable(int Offset){ offset = Offset; } public bool IsNull { get{ return (offset==FontTable.InvalidOffset); } } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct LookupList { private const int offsetLookupCount = 0; private const int LookupOffsetArray = 2; private const int sizeLookupOffset = 2; public ushort LookupCount(FontTable Table) { return Table.GetUShort(offset + offsetLookupCount); } public LookupTable Lookup(FontTable Table, ushort Index) { return new LookupTable(Table, offset + Table.GetUShort(offset + LookupOffsetArray + Index * sizeLookupOffset)); } public LookupList(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct LookupTable { private const int offsetLookupType = 0; private const int offsetLookupFlags = 2; private const int offsetSubtableCount = 4; private const int offsetSubtableArray = 6; private const int sizeSubtableOffset = 2; public ushort LookupType() { return lookupType; } public ushort LookupFlags() { return lookupFlags; } public ushort SubTableCount() { return subtableCount; } public int SubtableOffset(FontTable Table, ushort Index) { Debug.Assert(Index < SubTableCount()); return offset+Table.GetOffset(offset + offsetSubtableArray + Index*sizeSubtableOffset); } public LookupTable(FontTable table, int Offset) { offset = Offset; lookupType = table.GetUShort(offset + offsetLookupType); lookupFlags = table.GetUShort(offset + offsetLookupFlags); subtableCount = table.GetUShort(offset + offsetSubtableCount); } private int offset; private ushort lookupType; private ushort lookupFlags; private ushort subtableCount; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct CoverageTable { private const int offsetFormat = 0; private const int offsetFormat1GlyphCount = 2; private const int offsetFormat1GlyphArray = 4; private const int sizeFormat1GlyphId = 2; private const int offsetFormat2RangeCount = 2; private const int offsetFormat2RangeRecordArray = 4; private const int sizeFormat2RangeRecord = 6; private const int offsetFormat2RangeRecordStart = 0; private const int offsetFormat2RangeRecordEnd = 2; private const int offsetFormat2RangeRecordStartIndex = 4; public ushort Format(FontTable Table) { return Table.GetUShort(offset + offsetFormat); } public ushort Format1GlyphCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat1GlyphCount); } public ushort Format1Glyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat1GlyphArray + Index*sizeFormat1GlyphId); } public ushort Format2RangeCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat2RangeCount); } public ushort Format2RangeStartGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordStart); } public ushort Format2RangeEndGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordEnd); } public ushort Format2RangeStartCoverageIndex(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordStartIndex); } public int GetGlyphIndex(FontTable Table, ushort glyph) { switch (Format(Table)) { case 1: // Coverage array { ushort lowIndex = 0; ushort highIndex = Format1GlyphCount(Table); while(lowIndex < highIndex) { ushort middleIndex = (ushort)((lowIndex + highIndex) >> 1); ushort middleGlyph = Format1Glyph(Table,middleIndex); if (glyph < middleGlyph) { highIndex = middleIndex; } else if (glyph > middleGlyph) { lowIndex = (ushort)(middleIndex+1); } else { return middleIndex; } } return -1; } case 2: //Glyph Ranges { ushort lowIndex = 0; ushort highIndex = Format2RangeCount(Table); while(lowIndex < highIndex) { ushort middleIndex = (ushort)((lowIndex + highIndex) >> 1); if (glyph < Format2RangeStartGlyph(Table, middleIndex)) { highIndex = middleIndex; } else if (glyph > Format2RangeEndGlyph(Table, middleIndex)) { lowIndex = (ushort)(middleIndex + 1); } else { return (glyph - Format2RangeStartGlyph(Table,middleIndex)) + Format2RangeStartCoverageIndex(Table,middleIndex); } } return -1; } default: //unknown format. Return NoMatch. return -1; } } public bool IsAnyGlyphCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { switch (Format(table)) { case 1: // Coverage array { ushort glyphCount = Format1GlyphCount(table); if (glyphCount == 0) return false; ushort firstGlyphId = Format1Glyph(table, 0); ushort lastGlyphId = Format1Glyph(table, (ushort)(glyphCount - 1)); if (maxGlyphId < firstGlyphId || minGlyphId > lastGlyphId) return false; for(ushort i = 0; i < glyphCount; i++) { ushort glyphId = Format1Glyph(table, i); if (glyphId <= maxGlyphId && glyphId >= minGlyphId && (glyphBits[glyphId >> 5] & (1 << (glyphId % 32))) != 0 ) { return true; } } return false; } case 2: //Glyph Ranges { ushort rangeCount = Format2RangeCount(table); if (rangeCount == 0) return false; ushort firstGlyphId = Format2RangeStartGlyph(table,0); ushort lastGlyphId = Format2RangeEndGlyph(table, (ushort)(rangeCount - 1)); if (maxGlyphId < firstGlyphId || minGlyphId > lastGlyphId) return false; for (ushort rangeIndex = 0; rangeIndex < rangeCount; rangeIndex++) { ushort startGlyphId = Format2RangeStartGlyph(table,rangeIndex); ushort endGlyphId = Format2RangeEndGlyph(table, rangeIndex); for (ushort glyphId = startGlyphId; glyphId <= endGlyphId; glyphId++) { if (glyphId <= maxGlyphId && glyphId >= minGlyphId && (glyphBits[glyphId >> 5] & (1 << (glyphId % 32))) != 0 ) { return true; } } } return false; } default: //unknown format. Return true. return true; } } public static CoverageTable InvalidCoverage { get { return new CoverageTable(-1); } } public bool IsInvalid { get { return offset == -1; } } public CoverageTable(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ClassDefTable { private const int offsetFormat = 0; private const int offsetFormat1StartGlyph = 2; private const int offsetFormat1GlyphCount = 4; private const int offsetFormat1ClassValueArray = 6; private const int sizeFormat1ClassValue = 2; private const int offsetFormat2RangeCount = 2; private const int offsetFormat2RangeRecordArray = 4; private const int sizeFormat2RangeRecord = 6; private const int offsetFormat2RangeRecordStart = 0; private const int offsetFormat2RangeRecordEnd = 2; private const int offsetFormat2RangeRecordClass = 4; private ushort Format(FontTable Table) { return Table.GetUShort(offset + offsetFormat); } private ushort Format1StartGlyph(FontTable Table) { return Table.GetUShort(offset + offsetFormat1StartGlyph); } private ushort Format1GlyphCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat1GlyphCount); } private ushort Format1ClassValue(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat1ClassValueArray + Index*sizeFormat1ClassValue); } private ushort Format2RangeCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat2RangeCount); } private ushort Format2RangeStartGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordStart); } private ushort Format2RangeEndGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordEnd); } private ushort Format2RangeClassValue(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordClass); } public ushort GetClass(FontTable Table, ushort glyph) { //PERF: binary search!!! switch (Format(Table)) { case 1: // ClassDef array { ushort startGlyph = Format1StartGlyph(Table); ushort glyphCount = Format1GlyphCount(Table); if (glyph >= startGlyph && (glyph - startGlyph) < glyphCount) return Format1ClassValue(Table,(ushort)(glyph - startGlyph)); else return 0; } case 2: //ClassDef Ranges { ushort lowIndex = 0; ushort highIndex = Format2RangeCount(Table); while(lowIndex < highIndex) { ushort middleIndex = (ushort)((lowIndex + highIndex) >> 1); if (glyph < Format2RangeStartGlyph(Table, middleIndex)) { highIndex = middleIndex; } else if (glyph > Format2RangeEndGlyph(Table, middleIndex)) { lowIndex = (ushort)(middleIndex + 1); } else { return Format2RangeClassValue(Table,middleIndex); } } return 0; } default: //unknown format. Return default: 0 return 0; } } public static ClassDefTable InvalidClassDef { get { return new ClassDefTable(-1); } } public bool IsInvalid { get { return offset == -1; } } public ClassDefTable(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ExtensionLookupTable { private const int offsetFormat = 0; private const int offsetLookupType = 2; private const int offsetExtensionOffset = 4; internal ushort LookupType(FontTable Table) { return Table.GetUShort(offset + offsetLookupType); } internal int LookupSubtableOffset(FontTable Table) { return offset + (int)Table.GetUInt(offset + offsetExtensionOffset); } public ExtensionLookupTable(int Offset) { offset = Offset; } private int offset; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //+------------------------------------------------------------------------ // // Microsoft Windows Client Platform // Copyright (C) Microsoft Corporation, 2002 // // File: Common.cs // // Contents: OpentTypeLayout higher level internal engine and structures // // contact: sergeym // // History: 2002-03-23 Created (sergeym) // //----------------------------------------------------------------------- using System.Diagnostics; using System.Security; using System.Security.Permissions; using System; using System.IO; using MS.Internal; namespace MS.Internal.Shaping { ////// Provide core layout functionality /// internal static unsafe class LayoutEngine { ////// Main layout loop. Execute lookups in right order for enabled ranges /// /// In: Font access interface /// In: Layout engine wokrspace /// In: Layout table tag (GSUB or GPOS) /// In: Layout Table (GSUB or GPOS) /// In: Layout metrics /// In: Language system table /// In: FeatureList /// In: Lookup list /// In: List of feature to apply /// In: Actual number of features in/// In: offset of input characters inside FeatureSet /// In: Characters count (i.e. Charmap.Length) /// InOut: Character to glyph method /// InOut: List of Glyph Information structures /// InOut: Glyph advances (used only for positioning) /// InOut: Glyph offsets (used only for positioning) /// /// Critical - calls critical code, accepts pointer parameters, unsafe code /// [SecurityCritical] public static void ApplyFeatures( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, OpenTypeTags TableTag, FontTable Table, LayoutMetrics Metrics, LangSysTable LangSys, FeatureList Features, LookupList Lookups, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList GlyphInfo, int* Advances, LayoutOffset* Offsets ) { UpdateGlyphFlags(Font, GlyphInfo, 0, GlyphInfo.Length, false, GlyphFlags.NotChanged); // if client did not supply us with workspace // we will create our own (temporarily) if (workspace == null) { workspace = new OpenTypeLayoutWorkspace(); } ushort lookupCount=Lookups.LookupCount(Table); //Compile feature set CompileFeatureSet( FeatureSet, featureCount, featureSetOffset, CharCount, Table, LangSys, Features, lookupCount, workspace ); OpenTypeLayoutCache.InitCache(Font, TableTag, GlyphInfo, workspace); for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } int firstChar=0, afterLastChar=0, firstGlyph=0, afterLastGlyph=0; OpenTypeLayoutCache.FindNextLookup(workspace, GlyphInfo, lookupIndex, out lookupIndex, out firstGlyph); // We need to check this again, because FindNextLookup will change lookupIndex if (lookupIndex >= lookupCount) { break; } if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } LookupTable lookup = Lookups.Lookup(Table, lookupIndex); uint parameter=0; bool isLookupReversal = IsLookupReversal(TableTag, lookup.LookupType()); while(firstGlyph < GlyphInfo.Length) // While we have ranges to work on { if (!OpenTypeLayoutCache.FindNextGlyphInLookup(workspace, lookupIndex, isLookupReversal, ref firstGlyph, ref afterLastGlyph)) { firstGlyph = afterLastGlyph; } if (firstGlyph < afterLastGlyph) // Apply lookup while in one range { int nextGlyph; int oldLength = GlyphInfo.Length; int glyphsAfterLastChar = oldLength - afterLastGlyph; bool match = ApplyLookup( Font, // In: Font access interface TableTag, // Layout table tag (GSUB or GPOS) Table, // Layout table (GSUB or GPOS) Metrics, // In: LayoutMetrics lookup, // Lookup definition structure CharCount, Charmap, // In: Char to glyph mapping GlyphInfo, // In/out: List of GlyphInfo structs Advances, // In/out: Glyph adv.widths Offsets, // In/out: Glyph offsets firstGlyph, // where to apply it afterLastGlyph, // how long is a context we can use parameter, // lookup parameter 0, // Nesting level for contextual lookups out nextGlyph // out: next glyph index // !!!: for reversal lookup, should // return new afterLastGlyph ); if (match) { //Adjust range end if length changed, // for reversal changes happens beyond afterLast, no change needed if (!isLookupReversal) { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, firstGlyph, nextGlyph); afterLastGlyph = GlyphInfo.Length - glyphsAfterLastChar; firstGlyph = nextGlyph; } else { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, nextGlyph, afterLastGlyph); afterLastGlyph = nextGlyph; } } else { if (isLookupReversal) afterLastGlyph = nextGlyph; else firstGlyph = nextGlyph; } } else // End of range. Get next { GetNextEnabledGlyphRange( FeatureSet, featureCount, featureSetOffset, Table, workspace, LangSys, Features, lookupIndex, CharCount, Charmap, afterLastChar, afterLastGlyph, GlyphInfo.Length, out firstChar, out afterLastChar, out firstGlyph, out afterLastGlyph, out parameter); } } } } ////// Critical - calls critical code (GetNextGlyphInLookup), /// accepts pointer parameters, unsafe code /// [SecurityCritical] internal static bool ApplyLookup( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics LookupTable Lookup, // List definition structure int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Char to glyph mapping GlyphInfoList GlyphInfo, // List of GlyphInfo structs int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use uint Parameter, // lookup parameter int nestingLevel, // Contextual lookup nesting level out int NextGlyph // out: next glyph index ) { Debug.Assert(TableTag==OpenTypeTags.GSUB || TableTag==OpenTypeTags.GPOS); Debug.Assert(FirstGlyph=AfterLastGlyph) return match; ushort originalLookupType = lookupType; // We need it to recover, if extension lookup updated lookupFormat for(ushort subtableIndex=0; !match && subtableIndex < subtableCount; subtableIndex++) { lookupType = originalLookupType; int subtableOffset = Lookup.SubtableOffset(Table, subtableIndex); switch (TableTag) { case OpenTypeTags.GSUB: { if (lookupType == 7) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(Table); subtableOffset = extension.LookupSubtableOffset(Table); } switch (lookupType) { case 1: //SingleSubst SingleSubstitutionSubtable singleSub = new SingleSubstitutionSubtable(subtableOffset); match = singleSub.Apply( Table, GlyphInfo, FirstGlyph, out NextGlyph ); break; case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); match = multipleSub.Apply( Font, Table, CharCount, Charmap, GlyphInfo, lookupFlags, FirstGlyph, AfterLastGlyph, out NextGlyph ); break; case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); match = alternateSub.Apply( Table, GlyphInfo, Parameter, FirstGlyph, out NextGlyph ); break; case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); match = ligaSub.Apply( Font, Table, CharCount, Charmap, GlyphInfo, lookupFlags, FirstGlyph, AfterLastGlyph, out NextGlyph ); break; case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); match = contextSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); match = chainingSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 7: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it. // Just skip it (do nothing); NextGlyph = FirstGlyph + 1; break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); match = reverseChainingSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, out NextGlyph ); break; default: // Unknown format NextGlyph = FirstGlyph+1; break; } if (match) { if (!IsLookupReversal(TableTag,lookupType)) { UpdateGlyphFlags(Font,GlyphInfo,FirstGlyph,NextGlyph,true,GlyphFlags.Substituted); } else { UpdateGlyphFlags(Font,GlyphInfo,NextGlyph,AfterLastGlyph,true,GlyphFlags.Substituted); } } break; } case OpenTypeTags.GPOS: { if (lookupType == 9) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(Table); subtableOffset = extension.LookupSubtableOffset(Table); } switch (lookupType) { case 1: //SinglePos SinglePositioningSubtable singlePos = new SinglePositioningSubtable(subtableOffset); match = singlePos.Apply(Table, Metrics, GlyphInfo, Advances, Offsets, FirstGlyph, AfterLastGlyph, out NextGlyph ); break; case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); match = pairPos.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 3: // CursivePos // Under construction CursivePositioningSubtable cursivePositioningSubtable = new CursivePositioningSubtable(subtableOffset); cursivePositioningSubtable.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); match = markToBasePos.Apply(Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); match = markToLigaPos.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups CharCount, // Characters count (i.e. Charmap.Length); Charmap, // Char to glyph mapping Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); match = markToMarkPos.Apply( Font, Table, Metrics, // LayoutMetrics GlyphInfo, // List of GlyphInfo structs lookupFlags, // Lookup flags for glyph lookups Advances, // Glyph adv.widths Offsets, // Glyph offsets FirstGlyph, // where to apply lookup AfterLastGlyph, // how long is a context we can use out NextGlyph // Next glyph to process ); break; case 7: // Contextual ContextSubtable contextSub = new ContextSubtable(subtableOffset); match = contextSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 8: // Chaining ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); match = chainingSub.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, lookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); break; case 9: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it. // Just skip it (do nothing); NextGlyph = FirstGlyph + 1; break; default: // Unknown format NextGlyph = FirstGlyph + 1; break; } if (match) { UpdateGlyphFlags(Font,GlyphInfo,FirstGlyph,NextGlyph,false,GlyphFlags.Positioned); } break; } default: Debug.Assert(false,"Unknown OpenType layout table!"); break; } } return match; } private static bool IsLookupReversal(OpenTypeTags TableTag, ushort LookupType) { return (TableTag == OpenTypeTags.GSUB && LookupType == 8); } /// /// Critical - The method reads into raw font table bits. /// [SecurityCritical] private static void CompileFeatureSet( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual number of features in FeatureSet int featureSetOffset, //In: Offset of character input sequence inside feature set int charCount, // In: number of characters in the input string FontTable Table, // In: Layout table (GSUB or GPOS) LangSysTable LangSys, // In: Language system FeatureList Features, // In: List of Features in layout table int lookupCount, // In: number of lookup in layout table OpenTypeLayoutWorkspace workspace // In: workspace with compiled feature set ) { workspace.InitLookupUsageFlags(lookupCount, featureCount); //Set lookup uasge flags for required feature FeatureTable requiredFeatureTable = LangSys.RequiredFeature(Table, Features); if (!requiredFeatureTable.IsNull) { int featureLookupCount = requiredFeatureTable.LookupCount(Table); for(ushort lookup = 0; lookup < featureLookupCount; lookup++) { workspace.SetRequiredFeatureFlag(requiredFeatureTable.LookupIndex(Table,lookup)); } } //Set lookup usage flags for each feature in the FeatureSet for(int feature = 0; feature < featureCount; feature++) { Feature featureDescription = FeatureSet[feature]; //Filter out features which: // Not enabled or applied completely before or after input characters if (featureDescription.Parameter == 0 || featureDescription.StartIndex >= (featureSetOffset + charCount) || (featureDescription.StartIndex+featureDescription.Length) <= featureSetOffset ) { continue; } FeatureTable featureTable = LangSys.FindFeature( Table, Features, featureDescription.Tag); if (featureTable.IsNull) { continue; } int featureLookupCount = featureTable.LookupCount(Table); for(ushort lookup = 0; lookup < featureLookupCount; lookup++) { workspace.SetFeatureFlag(featureTable.LookupIndex(Table,lookup), feature); } } } ////// Critical - This method reads into unsafe cluster map. /// [SecurityCritical] private static void GetNextEnabledGlyphRange( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual nubmer of features in FeatureSet int featureSetOffset, // In: offset of input chars inside feature set FontTable Table, // Layout table (GSUB or GPOS) OpenTypeLayoutWorkspace workspace, // workspace with compiled feature set LangSysTable LangSys, // Language system FeatureList Features, // List of Features in layout table ushort lookupIndex, // List of lokups definitions in layout table int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Character to glyph mapping int StartChar, int StartGlyph, int GlyphRunLength, out int FirstChar, // First char in enabled range out int AfterLastChar, // next char after enabled range out int FirstGlyph, // First char in enabled range out int AfterLastGlyph, // next char after enabled range out uint Parameter // applied feature parameter ) { FirstChar = int.MaxValue; AfterLastChar = int.MaxValue; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; Parameter = 0; if (workspace.IsRequiredFeatureFlagSet(lookupIndex)) { FirstChar = StartChar; AfterLastChar = CharCount; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; return; } for(int feature=0; feature < featureCount; feature++) { if (!workspace.IsFeatureFlagSet(lookupIndex,feature)) { continue; } Feature featureDescription = FeatureSet[feature]; // Shift values from the feature by specified offset and // work with these values from here int featureStart = featureDescription.StartIndex - featureSetOffset; if (featureStart < 0) { featureStart = 0; } int featureAfterEnd = featureDescription.StartIndex + featureDescription.Length - featureSetOffset; if (featureAfterEnd > CharCount) { featureAfterEnd = CharCount; } //If feature is disabled there should not be any flag set Debug.Assert(featureDescription.Parameter != 0); if (featureAfterEnd <= StartChar) { continue; } if (featureStart < FirstChar || ( featureStart == FirstChar && featureAfterEnd >= AfterLastChar ) ) { FirstChar = featureStart; AfterLastChar = featureAfterEnd; Parameter = featureDescription.Parameter; continue; } } //No ranges found if (FirstChar == int.MaxValue) { FirstGlyph = GlyphRunLength; AfterLastGlyph = GlyphRunLength; } else { if (StartGlyph > Charmap[FirstChar]) FirstGlyph = StartGlyph; else FirstGlyph = Charmap[FirstChar]; if (AfterLastChar < CharCount) AfterLastGlyph = Charmap[AfterLastChar]; else AfterLastGlyph = GlyphRunLength; } } ////// Critical - access protected font information (FontTable) /// [SecurityCritical] private static void UpdateGlyphFlags( IOpenTypeFont Font, GlyphInfoList GlyphInfo, int FirstGlyph, int AfterLastGlyph, bool DoAll, GlyphFlags FlagToSet ) { Debug.Assert( FlagToSet==GlyphFlags.NotChanged || FlagToSet==GlyphFlags.Substituted || FlagToSet==GlyphFlags.Positioned); ushort typemask = (ushort)GlyphFlags.GlyphTypeMask; FontTable gdefTable = Font.GetFontTable(OpenTypeTags.GDEF); if (!gdefTable.IsPresent) { //GDEF(i.e. class def in it) is not present. //Assign unassigned to all glyphs for(int i=FirstGlyph;i/// Critical - Access protected font information (FontTable). /// [SecurityCritical] internal static int GetNextGlyphInLookup( IOpenTypeFont Font, // GlyphInfoList GlyphInfo, // Glyph run int FirstGlyph, // Current glyph index ushort LookupFlags, // Lookup flags to use int Direction // Search direction (forward/back) ) { FontTable gdefTable; ClassDefTable markAttachClassDef; //assign them only to avoid error: using unassigned variable gdefTable = null; markAttachClassDef = ClassDefTable.InvalidClassDef; if (LookupFlags==0) return FirstGlyph; //we will mark classes only if mark filter is set if ((LookupFlags&(ushort)LookupFlagMarkAttachmentTypeMask)!=0) { gdefTable = Font.GetFontTable(OpenTypeTags.GDEF); if (gdefTable.IsPresent) { markAttachClassDef = (new GDEFHeader(0)).GetMarkAttachClassDef(gdefTable); } } UshortList glyphFlags = GlyphInfo.GlyphFlags; ushort attachClass = (ushort)((LookupFlags&LookupFlagMarkAttachmentTypeMask)>>8); int glyph; int glyphRunLength = GlyphInfo.Length; for(glyph=FirstGlyph; glyph =0; glyph+=Direction) { if ( (LookupFlags&LookupFlagIgnoreBases)!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Base ) continue; if ( (LookupFlags&LookupFlagIgnoreMarks)!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Mark ) continue; if ( (LookupFlags&LookupFlagIgnoreLigatures)!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Ligature ) continue; if (attachClass!=0 && (glyphFlags[glyph]&(ushort)GlyphFlags.GlyphTypeMask)==(ushort)GlyphFlags.Mark && !markAttachClassDef.IsInvalid && attachClass!=markAttachClassDef.GetClass(gdefTable,GlyphInfo.Glyphs[glyph]) ) continue; return glyph; } return glyph; } /// /// Returns list of the languages, that can not be optimized for simple shaping /// ////// Critical - The method reads into raw font table bits /// [SecurityCritical] internal static void GetComplexLanguageList( OpenTypeTags tableTag, FontTable table, uint[] featureTagsList, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId, out WritingSystem[] complexLanguages, out int complexLanguageCount ) { ScriptList scriptList = new ScriptList(0); FeatureList featureList = new FeatureList(0); LookupList lookupList = new LookupList(0); Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); switch (tableTag) { case OpenTypeTags.GSUB: GSUBHeader gsubHeader = new GSUBHeader(0); scriptList = gsubHeader.GetScriptList(table); featureList = gsubHeader.GetFeatureList(table); lookupList = gsubHeader.GetLookupList(table); break; case OpenTypeTags.GPOS: GPOSHeader gposHeader = new GPOSHeader(0); scriptList = gposHeader.GetScriptList(table); featureList = gposHeader.GetFeatureList(table); lookupList = gposHeader.GetLookupList(table); break; } int scriptCount = scriptList.GetScriptCount(table); int featureCount = featureList.FeatureCount(table); int lookupCount = lookupList.LookupCount(table); // We will mark lookups that should be tested. // At the end, we will have only complex ones marked uint[] lookupBits = new uint[(lookupCount+31)>>5]; for(int i = 0; i < (lookupCount+31)>>5; i++) { lookupBits[i] = 0; } // Iterate through list of featuers in the table for(ushort featureIndex = 0; featureIndex < featureCount; featureIndex++) { uint featureTag = (uint)featureList.FeatureTag(table, featureIndex); bool tagFound = false; //Search for tag in the list of features in question for(int j = 0; j < featureTagsList.Length; j++) { if (featureTagsList[j] == featureTag) { tagFound = true; break; } } if (tagFound) { // We should mark all lookup mapped to this feature FeatureTable featureTable = featureList.FeatureTable(table, featureIndex); ushort featureLookupCount = featureTable.LookupCount(table); for(ushort j = 0; j < featureLookupCount; j++) { ushort lookupIndex = featureTable.LookupIndex(table, j); if (lookupIndex >= lookupCount) { //This should be invalid font. Lookup associated with the feature is not in lookup array. throw new FileFormatException(); } lookupBits[lookupIndex>>5] |= (uint)(1 << (lookupIndex%32)); } } } // // //Now test all marked lookups for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if ((lookupBits[lookupIndex>>5] & (1 << (lookupIndex%32))) == 0) { continue; } LookupTable lookup = lookupList.Lookup(table,lookupIndex); ushort lookupType = lookup.LookupType(); ushort subtableCount = lookup.SubTableCount(); bool lookupIsCovered = false; ushort originalLookupType = lookupType; // We need it to recover, // if extension lookup updated lookupFormat for(ushort subtableIndex = 0; !lookupIsCovered && subtableIndex < subtableCount; subtableIndex++) { lookupType = originalLookupType; int subtableOffset = lookup.SubtableOffset(table, subtableIndex); switch (tableTag) { case OpenTypeTags.GSUB: { if (lookupType == 7) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SingleSubst SingleSubstitutionSubtable singleSub = new SingleSubstitutionSubtable(subtableOffset); lookupIsCovered = singleSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); lookupIsCovered = multipleSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); lookupIsCovered = alternateSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); lookupIsCovered = ligaSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); lookupIsCovered = contextSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); lookupIsCovered = chainingSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 7: //Extension lookup Debug.Assert(false,"Ext.Lookup processed earlier!"); break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); lookupIsCovered = reverseChainingSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; default: // Unknown format lookupIsCovered = true; break; } break; } case OpenTypeTags.GPOS: { if (lookupType == 9) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SinglePos SinglePositioningSubtable singlePos = new SinglePositioningSubtable(subtableOffset); lookupIsCovered = singlePos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); lookupIsCovered = pairPos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 3: // CursivePos // Under construction CursivePositioningSubtable cursivePositioningSubtable = new CursivePositioningSubtable(subtableOffset); lookupIsCovered = cursivePositioningSubtable.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); lookupIsCovered = markToBasePos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); lookupIsCovered = markToLigaPos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); lookupIsCovered = markToMarkPos.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 7: // Contextual ContextSubtable contextSub = new ContextSubtable(subtableOffset); lookupIsCovered = contextSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 8: // Chaining ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); lookupIsCovered = chainingSub.IsLookupCovered(table,glyphBits,minGlyphId,maxGlyphId); break; case 9: //Extension lookup Debug.Assert(false,"Ext.Lookup processed earlier!"); break; default: // Unknown format lookupIsCovered = true; break; } break; } default: Debug.Assert(false,"Unknown OpenType layout table!"); break; } } if (!lookupIsCovered) { // Clean the flag lookupBits[lookupIndex>>5] &= ~(uint)(1 << (lookupIndex%32)); } } // Check if we have any lookup left bool complexLookupFound = false; for(int i = 0; i < (lookupCount+31)>>5; i++) { if (lookupBits[i] != 0) { complexLookupFound = true; break; } } if (!complexLookupFound) { // There are no complex lookups complexLanguages = null; complexLanguageCount = 0; return; } // Now go through all langauages and fill the list complexLanguages = new WritingSystem[10]; complexLanguageCount = 0; for(ushort scriptIndex = 0; scriptIndex < scriptCount; scriptIndex++) { ScriptTable scriptTable = scriptList.GetScriptTable(table, scriptIndex); uint scriptTag = scriptList.GetScriptTag(table, scriptIndex); ushort langSysCount = scriptTable.GetLangSysCount(table); if (scriptTable.IsDefaultLangSysExists(table)) { AppendLangSys(scriptTag, (uint)OpenTypeTags.dflt, scriptTable.GetDefaultLangSysTable(table), featureList, table, featureTagsList, lookupBits, ref complexLanguages, ref complexLanguageCount ); } for(ushort langSysIndex = 0; langSysIndex < langSysCount; langSysIndex++) { uint langSysTag = scriptTable.GetLangSysTag(table, langSysIndex); AppendLangSys(scriptTag, langSysTag, scriptTable.GetLangSysTable(table, langSysIndex), featureList, table, featureTagsList, lookupBits, ref complexLanguages, ref complexLanguageCount ); } } } ////// Critical - The method reads into raw font table bits. /// [SecurityCritical] private static void AppendLangSys( uint scriptTag, uint langSysTag, LangSysTable langSysTable, FeatureList featureList, FontTable table, uint[] featureTagsList, uint[] lookupBits, ref WritingSystem[] complexLanguages, ref int complexLanguageCount ) { ushort featureCount = langSysTable.FeatureCount(table); bool complexFeatureFound = false; // for(ushort i = 0; !complexFeatureFound && i < featureCount; i++) { ushort featureIndex = langSysTable.GetFeatureIndex(table, i); uint featureTag = featureList.FeatureTag(table,featureIndex); bool tagFound = false; for(int j = 0; !complexFeatureFound && j < featureTagsList.Length; j++) { if (featureTagsList[j] == featureTag) { tagFound = true; break; } } if (tagFound) { // We should check if any of lookups is complex FeatureTable featureTable = featureList.FeatureTable(table, featureIndex); ushort featureLookupCount = featureTable.LookupCount(table); for(ushort j = 0; j < featureLookupCount; j++) { ushort lookupIndex = featureTable.LookupIndex(table, j); if ((lookupBits[lookupIndex>>5] & (1 << (lookupIndex%32))) != 0) { complexFeatureFound = true; break; } } } } if (complexFeatureFound) { if (complexLanguages.Length == complexLanguageCount) { WritingSystem[] newComplexLanguages = new WritingSystem[complexLanguages.Length * 3 /2]; for(int i = 0; i < complexLanguages.Length; i++) { newComplexLanguages[i] = complexLanguages[i]; } complexLanguages = newComplexLanguages; } complexLanguages[complexLanguageCount].scriptTag = scriptTag; complexLanguages[complexLanguageCount].langSysTag = langSysTag; complexLanguageCount++; } } } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct GSUBHeader { private const int offsetScriptList = 4; private const int offsetFeatureList = 6; private const int offsetLookupList = 8; public ScriptList GetScriptList(FontTable Table) { return new ScriptList(offset+Table.GetOffset(offset+offsetScriptList)); } public FeatureList GetFeatureList(FontTable Table) { return new FeatureList(offset+Table.GetOffset(offset+offsetFeatureList)); } public LookupList GetLookupList(FontTable Table) { return new LookupList(offset+Table.GetOffset(offset+offsetLookupList)); } public GSUBHeader(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct GPOSHeader { private const int offsetScriptList = 4; private const int offsetFeatureList = 6; private const int offsetLookupList = 8; public ScriptList GetScriptList(FontTable Table) { return new ScriptList(offset+Table.GetOffset(offset+offsetScriptList)); } public FeatureList GetFeatureList(FontTable Table) { return new FeatureList(offset+Table.GetOffset(offset+offsetFeatureList)); } public LookupList GetLookupList(FontTable Table) { return new LookupList(offset+Table.GetOffset(offset+offsetLookupList)); } public GPOSHeader(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct GDEFHeader { private const int offsetGlyphClassDef = 4; private const int offsetGlyphAttachList = 6; private const int offsetLigaCaretList = 8; private const int offsetMarkAttachClassDef = 10; public ClassDefTable GetGlyphClassDef(FontTable Table) { //There is only one place where GDEF classdef is retireved - // UpdateGlyphFlags. We will check if GDEF is present there Invariant.Assert(Table.IsPresent); return new ClassDefTable(offset + Table.GetOffset(offset + offsetGlyphClassDef)); } // ///// /// Under construction // /// Glyph attachment points list // /// // /// // /////public GlyphAttachList GetGlyphAttachList(FontTable Table) //{ // return new GlyphAttachList(offset + Table.GetOffset(offset + offsetGlyphAttachList)); //} // /// // /// Under construction // /// Ligature caret positioning data // /// // /// // ///// //public LigatureCaretList GetLigatureCaretlist(FontTable Table) // //{ // // return new LigatureCaretList(offset + Table.GetOffset(offset + offsetLigaCaretList)); // //} public ClassDefTable GetMarkAttachClassDef(FontTable Table) { //There is only one place where GDEF classdef is retireved - // GetNextGlyphInLokup We will check if GDEF is present there. Invariant.Assert(Table.IsPresent); return new ClassDefTable(offset+Table.GetOffset(offset+offsetMarkAttachClassDef)); } public GDEFHeader(int Offset) { offset = Offset; } private int offset; } /// /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ScriptList { private const int offsetScriptCount = 0; private const int offsetScriptRecordArray = 2; private const int sizeScriptRecord = 6; private const int offsetScriptRecordTag = 0; private const int offsetScriptRecordOffset = 4; public ScriptTable FindScript(FontTable Table, uint Tag) { for(ushort i=0;i/// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ScriptTable { private const int offsetDefaultLangSys = 0; private const int offsetLangSysCount = 2; private const int offsetLangSysRecordArray = 4; private const int sizeLangSysRecord = 6; private const int offsetLangSysRecordTag = 0; private const int offsetLangSysRecordOffset = 4; public LangSysTable FindLangSys(FontTable Table, uint Tag) { if (IsNull) { return new LangSysTable(FontTable.InvalidOffset); } if ((OpenTypeTags)Tag==OpenTypeTags.dflt) { if (IsDefaultLangSysExists(Table)) return new LangSysTable(offset + Table.GetOffset(offset + offsetDefaultLangSys)); return new LangSysTable(FontTable.InvalidOffset); } for(ushort i=0;i /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct LangSysTable { private const int offsetRequiredFeature = 2; private const int offsetFeatureCount = 4; private const int offsetFeatureIndexArray = 6; private const int sizeFeatureIndex = 2; public FeatureTable FindFeature(FontTable Table, FeatureList Features, uint FeatureTag) { ushort featureCount = FeatureCount(Table); for(ushort i=0;i /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct FeatureList { private const int offsetFeatureCount = 0; private const int offsetFeatureRecordArray = 2; private const int sizeFeatureRecord = 6; private const int offsetFeatureRecordTag = 0; private const int offsetFeatureRecordOffset = 4; public ushort FeatureCount(FontTable Table) { return Table.GetUShort(offset + offsetFeatureCount); } public uint FeatureTag(FontTable Table,ushort Index) { return Table.GetUInt(offset + offsetFeatureRecordArray + Index * sizeFeatureRecord + offsetFeatureRecordTag); } public FeatureTable FeatureTable(FontTable Table,ushort Index) { return new FeatureTable(offset+Table.GetUShort(offset + offsetFeatureRecordArray + Index * sizeFeatureRecord + offsetFeatureRecordOffset)); } public FeatureList(int Offset) { offset = Offset; } private int offset; } /// /// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct FeatureTable { private const int offsetLookupCount = 2; private const int offsetLookupIndexArray = 4; private const int sizeLookupIndex = 2; public ushort LookupCount(FontTable Table) { return Table.GetUShort(offset + offsetLookupCount); } public ushort LookupIndex(FontTable Table,ushort Index) { return Table.GetUShort(offset + offsetLookupIndexArray + Index*sizeLookupIndex); } public FeatureTable(int Offset){ offset = Offset; } public bool IsNull { get{ return (offset==FontTable.InvalidOffset); } } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct LookupList { private const int offsetLookupCount = 0; private const int LookupOffsetArray = 2; private const int sizeLookupOffset = 2; public ushort LookupCount(FontTable Table) { return Table.GetUShort(offset + offsetLookupCount); } public LookupTable Lookup(FontTable Table, ushort Index) { return new LookupTable(Table, offset + Table.GetUShort(offset + LookupOffsetArray + Index * sizeLookupOffset)); } public LookupList(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct LookupTable { private const int offsetLookupType = 0; private const int offsetLookupFlags = 2; private const int offsetSubtableCount = 4; private const int offsetSubtableArray = 6; private const int sizeSubtableOffset = 2; public ushort LookupType() { return lookupType; } public ushort LookupFlags() { return lookupFlags; } public ushort SubTableCount() { return subtableCount; } public int SubtableOffset(FontTable Table, ushort Index) { Debug.Assert(Index < SubTableCount()); return offset+Table.GetOffset(offset + offsetSubtableArray + Index*sizeSubtableOffset); } public LookupTable(FontTable table, int Offset) { offset = Offset; lookupType = table.GetUShort(offset + offsetLookupType); lookupFlags = table.GetUShort(offset + offsetLookupFlags); subtableCount = table.GetUShort(offset + offsetSubtableCount); } private int offset; private ushort lookupType; private ushort lookupFlags; private ushort subtableCount; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct CoverageTable { private const int offsetFormat = 0; private const int offsetFormat1GlyphCount = 2; private const int offsetFormat1GlyphArray = 4; private const int sizeFormat1GlyphId = 2; private const int offsetFormat2RangeCount = 2; private const int offsetFormat2RangeRecordArray = 4; private const int sizeFormat2RangeRecord = 6; private const int offsetFormat2RangeRecordStart = 0; private const int offsetFormat2RangeRecordEnd = 2; private const int offsetFormat2RangeRecordStartIndex = 4; public ushort Format(FontTable Table) { return Table.GetUShort(offset + offsetFormat); } public ushort Format1GlyphCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat1GlyphCount); } public ushort Format1Glyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat1GlyphArray + Index*sizeFormat1GlyphId); } public ushort Format2RangeCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat2RangeCount); } public ushort Format2RangeStartGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordStart); } public ushort Format2RangeEndGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordEnd); } public ushort Format2RangeStartCoverageIndex(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordStartIndex); } public int GetGlyphIndex(FontTable Table, ushort glyph) { switch (Format(Table)) { case 1: // Coverage array { ushort lowIndex = 0; ushort highIndex = Format1GlyphCount(Table); while(lowIndex < highIndex) { ushort middleIndex = (ushort)((lowIndex + highIndex) >> 1); ushort middleGlyph = Format1Glyph(Table,middleIndex); if (glyph < middleGlyph) { highIndex = middleIndex; } else if (glyph > middleGlyph) { lowIndex = (ushort)(middleIndex+1); } else { return middleIndex; } } return -1; } case 2: //Glyph Ranges { ushort lowIndex = 0; ushort highIndex = Format2RangeCount(Table); while(lowIndex < highIndex) { ushort middleIndex = (ushort)((lowIndex + highIndex) >> 1); if (glyph < Format2RangeStartGlyph(Table, middleIndex)) { highIndex = middleIndex; } else if (glyph > Format2RangeEndGlyph(Table, middleIndex)) { lowIndex = (ushort)(middleIndex + 1); } else { return (glyph - Format2RangeStartGlyph(Table,middleIndex)) + Format2RangeStartCoverageIndex(Table,middleIndex); } } return -1; } default: //unknown format. Return NoMatch. return -1; } } public bool IsAnyGlyphCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { switch (Format(table)) { case 1: // Coverage array { ushort glyphCount = Format1GlyphCount(table); if (glyphCount == 0) return false; ushort firstGlyphId = Format1Glyph(table, 0); ushort lastGlyphId = Format1Glyph(table, (ushort)(glyphCount - 1)); if (maxGlyphId < firstGlyphId || minGlyphId > lastGlyphId) return false; for(ushort i = 0; i < glyphCount; i++) { ushort glyphId = Format1Glyph(table, i); if (glyphId <= maxGlyphId && glyphId >= minGlyphId && (glyphBits[glyphId >> 5] & (1 << (glyphId % 32))) != 0 ) { return true; } } return false; } case 2: //Glyph Ranges { ushort rangeCount = Format2RangeCount(table); if (rangeCount == 0) return false; ushort firstGlyphId = Format2RangeStartGlyph(table,0); ushort lastGlyphId = Format2RangeEndGlyph(table, (ushort)(rangeCount - 1)); if (maxGlyphId < firstGlyphId || minGlyphId > lastGlyphId) return false; for (ushort rangeIndex = 0; rangeIndex < rangeCount; rangeIndex++) { ushort startGlyphId = Format2RangeStartGlyph(table,rangeIndex); ushort endGlyphId = Format2RangeEndGlyph(table, rangeIndex); for (ushort glyphId = startGlyphId; glyphId <= endGlyphId; glyphId++) { if (glyphId <= maxGlyphId && glyphId >= minGlyphId && (glyphBits[glyphId >> 5] & (1 << (glyphId % 32))) != 0 ) { return true; } } } return false; } default: //unknown format. Return true. return true; } } public static CoverageTable InvalidCoverage { get { return new CoverageTable(-1); } } public bool IsInvalid { get { return offset == -1; } } public CoverageTable(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ClassDefTable { private const int offsetFormat = 0; private const int offsetFormat1StartGlyph = 2; private const int offsetFormat1GlyphCount = 4; private const int offsetFormat1ClassValueArray = 6; private const int sizeFormat1ClassValue = 2; private const int offsetFormat2RangeCount = 2; private const int offsetFormat2RangeRecordArray = 4; private const int sizeFormat2RangeRecord = 6; private const int offsetFormat2RangeRecordStart = 0; private const int offsetFormat2RangeRecordEnd = 2; private const int offsetFormat2RangeRecordClass = 4; private ushort Format(FontTable Table) { return Table.GetUShort(offset + offsetFormat); } private ushort Format1StartGlyph(FontTable Table) { return Table.GetUShort(offset + offsetFormat1StartGlyph); } private ushort Format1GlyphCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat1GlyphCount); } private ushort Format1ClassValue(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat1ClassValueArray + Index*sizeFormat1ClassValue); } private ushort Format2RangeCount(FontTable Table) { return Table.GetUShort(offset + offsetFormat2RangeCount); } private ushort Format2RangeStartGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordStart); } private ushort Format2RangeEndGlyph(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordEnd); } private ushort Format2RangeClassValue(FontTable Table, ushort Index) { return Table.GetUShort(offset + offsetFormat2RangeRecordArray + Index*sizeFormat2RangeRecord + offsetFormat2RangeRecordClass); } public ushort GetClass(FontTable Table, ushort glyph) { //PERF: binary search!!! switch (Format(Table)) { case 1: // ClassDef array { ushort startGlyph = Format1StartGlyph(Table); ushort glyphCount = Format1GlyphCount(Table); if (glyph >= startGlyph && (glyph - startGlyph) < glyphCount) return Format1ClassValue(Table,(ushort)(glyph - startGlyph)); else return 0; } case 2: //ClassDef Ranges { ushort lowIndex = 0; ushort highIndex = Format2RangeCount(Table); while(lowIndex < highIndex) { ushort middleIndex = (ushort)((lowIndex + highIndex) >> 1); if (glyph < Format2RangeStartGlyph(Table, middleIndex)) { highIndex = middleIndex; } else if (glyph > Format2RangeEndGlyph(Table, middleIndex)) { lowIndex = (ushort)(middleIndex + 1); } else { return Format2RangeClassValue(Table,middleIndex); } } return 0; } default: //unknown format. Return default: 0 return 0; } } public static ClassDefTable InvalidClassDef { get { return new ClassDefTable(-1); } } public bool IsInvalid { get { return offset == -1; } } public ClassDefTable(int Offset) { offset = Offset; } private int offset; } ////// Critical - Everything in this struct is considered critical /// because they either operate on raw font table bits or unsafe pointers. /// [SecurityCritical(SecurityCriticalScope.Everything)] internal struct ExtensionLookupTable { private const int offsetFormat = 0; private const int offsetLookupType = 2; private const int offsetExtensionOffset = 4; internal ushort LookupType(FontTable Table) { return Table.GetUShort(offset + offsetLookupType); } internal int LookupSubtableOffset(FontTable Table) { return offset + (int)Table.GetUInt(offset + offsetExtensionOffset); } public ExtensionLookupTable(int Offset) { offset = Offset; } private int offset; } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- NGCPageContentCollectionSerializerAsync.cs
- XPathBinder.cs
- ListViewVirtualItemsSelectionRangeChangedEvent.cs
- UMPAttributes.cs
- OledbConnectionStringbuilder.cs
- RMPublishingDialog.cs
- ListSortDescription.cs
- ProxyWebPart.cs
- TdsEnums.cs
- SapiRecoInterop.cs
- COM2PropertyBuilderUITypeEditor.cs
- PrinterResolution.cs
- DataViewManager.cs
- CalloutQueueItem.cs
- VersionConverter.cs
- RegistryDataKey.cs
- ConnectivityStatus.cs
- PersonalizationProvider.cs
- SafeHandles.cs
- CodeTypeOfExpression.cs
- DependencyObjectProvider.cs
- RubberbandSelector.cs
- DelegatingChannelListener.cs
- _IPv4Address.cs
- ListBase.cs
- MobilePage.cs
- SQLChars.cs
- counter.cs
- TCPClient.cs
- Margins.cs
- InvalidPrinterException.cs
- Camera.cs
- FrameSecurityDescriptor.cs
- TiffBitmapEncoder.cs
- x509store.cs
- PropertyContainer.cs
- BitVec.cs
- ViewManager.cs
- EditingMode.cs
- ConstraintCollection.cs
- TableLayoutPanelCellPosition.cs
- XmlKeywords.cs
- MethodBody.cs
- DifferencingCollection.cs
- BufferedWebEventProvider.cs
- TextBoxDesigner.cs
- BulletedList.cs
- XmlNamespaceDeclarationsAttribute.cs
- LateBoundChannelParameterCollection.cs
- WebWorkflowRole.cs
- OutOfProcStateClientManager.cs
- WebPartEventArgs.cs
- RuntimeConfigLKG.cs
- HttpSysSettings.cs
- FullTextBreakpoint.cs
- CodeEventReferenceExpression.cs
- RootProfilePropertySettingsCollection.cs
- smtppermission.cs
- EmissiveMaterial.cs
- DrawingContextDrawingContextWalker.cs
- ReadWriteControlDesigner.cs
- Cursor.cs
- LoginView.cs
- UnitySerializationHolder.cs
- AuthorizationRule.cs
- ClientScriptManagerWrapper.cs
- NumericExpr.cs
- LinkedDataMemberFieldEditor.cs
- _AutoWebProxyScriptHelper.cs
- _NegoStream.cs
- DataSourceSelectArguments.cs
- OleDbPropertySetGuid.cs
- ConnectionStringSettings.cs
- FixedSOMTableCell.cs
- PerformanceCounter.cs
- AssemblyHash.cs
- XmlWrappingReader.cs
- ModifierKeysValueSerializer.cs
- XmlElement.cs
- BindingWorker.cs
- CharacterMetricsDictionary.cs
- HeaderPanel.cs
- FileDetails.cs
- LineBreakRecord.cs
- UserControl.cs
- XmlILIndex.cs
- ProjectionCamera.cs
- FullTextBreakpoint.cs
- PropertyItem.cs
- CompilationLock.cs
- SqlBooleanMismatchVisitor.cs
- smtppermission.cs
- MetadataCache.cs
- LocalServiceSecuritySettingsElement.cs
- WindowsIdentity.cs
- EDesignUtil.cs
- DataGridViewColumn.cs
- ButtonColumn.cs
- NullableBoolConverter.cs
- InstanceNotReadyException.cs