internal static OpenTypeLayoutResult SubstituteGlyphs( IOpenTypeFont Font, // In: Font access interface OpenTypeLayoutWorkspace workspace, // In: Workspace for layout engine uint ScriptTag, // In: Script tag uint LangSysTag, // In: LangSys tag Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual number of features in FeatureSet int featureSetOffset, int CharCount, // In: Characters count (i.e. Charmap.Length); UshortList Charmap, // In/out: Char to glyph mapping GlyphInfoList Glyphs // In/out: List of GlyphInfo structs ) { try { FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); if (!GsubTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;} GSUBHeader GsubHeader = new GSUBHeader(0); ScriptList ScriptList = GsubHeader.GetScriptList(GsubTable); ScriptTable Script = ScriptList.FindScript(GsubTable,ScriptTag); if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;} LangSysTable LangSys = Script.FindLangSys(GsubTable,LangSysTag); if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;} FeatureList FeatureList = GsubHeader.GetFeatureList(GsubTable); LookupList LookupList = GsubHeader.GetLookupList(GsubTable); LayoutEngine.ApplyFeatures( Font, workspace, OpenTypeTags.GSUB, GsubTable, new LayoutMetrics(), //it is not needed for substitution LangSys, FeatureList, LookupList, FeatureSet, featureCount, featureSetOffset, CharCount, Charmap, Glyphs, null, null ); } catch (FileFormatException) { return OpenTypeLayoutResult.BadFontTable; } return OpenTypeLayoutResult.Success; }
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 ); } } }
internal static TagInfoFlags FindLangSys( IOpenTypeFont Font, uint ScriptTag, uint LangSysTag ) { TagInfoFlags flags = TagInfoFlags.None; try { FontTable gsubTable = Font.GetFontTable(OpenTypeTags.GSUB); if (gsubTable.IsPresent) { GSUBHeader gsubHeader = new GSUBHeader(0); ScriptTable gsubScript = gsubHeader.GetScriptList(gsubTable).FindScript(gsubTable,ScriptTag); if (!gsubScript.IsNull && !gsubScript.FindLangSys(gsubTable,LangSysTag).IsNull) { flags |= TagInfoFlags.Substitution; } } } catch (FileFormatException) { return TagInfoFlags.None; } try { FontTable gposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (gposTable.IsPresent) { GPOSHeader gposHeader = new GPOSHeader(0); ScriptTable gposScript = gposHeader.GetScriptList(gposTable).FindScript(gposTable,ScriptTag); if (!gposScript.IsNull && !gposScript.FindLangSys(gposTable,LangSysTag).IsNull) { flags |= TagInfoFlags.Positioning; } } } catch (FileFormatException) { return TagInfoFlags.None; } return flags; }