public static void InitCache( IOpenTypeFont font, OpenTypeTags tableTag, GlyphInfoList glyphInfo, OpenTypeLayoutWorkspace workspace ) { Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); byte[] cacheArray = font.GetTableCache(tableTag); unsafe { if (cacheArray == null) { workspace.TableCacheData = null; } else { workspace.TableCacheData = cacheArray; workspace.AllocateCachePointers(glyphInfo.Length); RenewPointers(glyphInfo, workspace, 0, glyphInfo.Length); } } }
/// <summary> /// Reset all structures to the new font/OTTable/script/langsys. /// /// Client need to call it only once per shaping engine call. /// This is client's responsibility to ensure that workspace is /// used for single font/OTTable/script/langsys between Init() calls /// </summary> ///<param name="font">In: Font access interface</param> ///<param name="tableTag">In: Font table tag</param> ///<param name="scriptTag">In: Script tag</param> ///<param name="langSysTag">In: Language System tag</param> ///<returns>Success if workspace is initialized succesfully, specific error if failed</returns> internal OpenTypeLayoutResult Init( IOpenTypeFont font, OpenTypeTags tableTag, uint scriptTag, uint langSysTag ) { // Currently all buffers are per call, // no need to do anything. return(OpenTypeLayoutResult.Success); }
private static void CreateTableCache(IOpenTypeFont font, OpenTypeTags tableTag, int maxCacheSize, out int tableCacheSize) { // Initialize all computed values tableCacheSize = 0; int cacheSize = 0; int recordCount = 0; int glyphCount = 0; int lastLookupAdded = -1; GlyphLookupRecord[] records = null; try { ComputeTableCache( font, tableTag, maxCacheSize, ref cacheSize, ref records, ref recordCount, ref glyphCount, ref lastLookupAdded ); } catch (FileFormatException) { cacheSize = 0; } if (cacheSize > 0) { tableCacheSize = FillTableCache( font, tableTag, cacheSize, records, recordCount, glyphCount, lastLookupAdded ); } }
/// <summary> /// Returns font table data /// </summary> public FontTable GetFontTable(OpenTypeTags TableTag) { switch (TableTag) { case OpenTypeTags.GSUB: { return(_gsubTable); } case OpenTypeTags.GPOS: { return(_gposTable); } default: { throw new NotSupportedException(); } } }
internal byte[] AllocateTableCache(OpenTypeTags tableTag, int size) { switch (tableTag) { case OpenTypeTags.GSUB: { _gsubCache = new byte[size]; return(_gsubCache); } case OpenTypeTags.GPOS: { _gposCache = new byte[size]; return(_gposCache); } default: { throw new NotSupportedException(); } } }
internal byte[] GetTableCache(OpenTypeTags tableTag) { switch (tableTag) { case OpenTypeTags.GSUB: if (Gsub() != null) { return(_gsubCache); } break; case OpenTypeTags.GPOS: if (Gpos() != null) { return(_gposCache); } break; default: throw new NotSupportedException(); } return(null); }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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 ushort LookupFlags, // Lookup table flags 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 ) { Invariant.Assert(Format(Table)==3); NextGlyph = FirstGlyph + 1; //in case we don't match int glyphCount = GlyphInfo.Length; int glyphIndex; ushort backtrackGlyphCount = BacktrackGlyphCount(Table); ushort inputGlyphCount = InputGlyphCount(Table); ushort lookaheadGlyphCount = LookaheadGlyphCount(Table); if (FirstGlyph < backtrackGlyphCount || (FirstGlyph + inputGlyphCount) > AfterLastGlyph) { return false; } bool match = true; //Check backtrack sequence glyphIndex = FirstGlyph; for(ushort backtrackIndex = 0; backtrackIndex < backtrackGlyphCount && match; backtrackIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex-1, LookupFlags, LayoutEngine.LookBackward ); if (glyphIndex<0 || BacktrackCoverage(Table,backtrackIndex) .GetGlyphIndex(Table,GlyphInfo.Glyphs[glyphIndex])<0) { match=false; } } if (!match) return false; glyphIndex = FirstGlyph; for(ushort inputIndex = 0; inputIndex < inputGlyphCount && match; inputIndex++) { if (glyphIndex>=AfterLastGlyph || InputCoverage(Table,inputIndex) .GetGlyphIndex(Table,GlyphInfo.Glyphs[glyphIndex])<0) { match=false; } else { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex + 1, LookupFlags, LayoutEngine.LookForward ); } } if (!match) return false; int afterInputGlyph = glyphIndex; // remember where we were after input seqence for(ushort lookaheadIndex = 0; lookaheadIndex < lookaheadGlyphCount && match; lookaheadIndex++) { if (glyphIndex>=GlyphInfo.Length || LookaheadCoverage(Table,lookaheadIndex) .GetGlyphIndex(Table,GlyphInfo.Glyphs[glyphIndex])<0) { match=false; } else { glyphIndex = LayoutEngine. GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex + 1, LookupFlags, LayoutEngine.LookForward); } } if (match) { ContextualLookups(Table).ApplyContextualLookups( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, afterInputGlyph, //As AfterLastGlyph Parameter, nestingLevel, out NextGlyph ); } return match; }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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 ushort LookupFlags, // Lookup table flags 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 ) { Invariant.Assert(Format(Table)==2); NextGlyph = FirstGlyph + 1; //in case we don't match int glyphCount = GlyphInfo.Length; int glyphIndex = FirstGlyph; ushort glyphId = GlyphInfo.Glyphs[glyphIndex]; if (Coverage(Table).GetGlyphIndex(Table,glyphId) < 0) return false; ClassDefTable inputClassDef = InputClassDef(Table), backtrackClassDef = BacktrackClassDef(Table), lookaheadClassDef = LookaheadClassDef(Table); ushort GlyphClass = inputClassDef.GetClass(Table,glyphId); if (GlyphClass >= ClassSetCount(Table)) return false; //!!! Bad font table SubClassSet subClassSet = ClassSet(Table,GlyphClass); if (subClassSet.IsNull) return false; // There are no rules for this class ushort ruleCount = subClassSet.RuleCount(Table); bool match = false; for(ushort i=0; !match && i<ruleCount; i++) { match = subClassSet.Rule(Table,i).Apply(Font, TableTag, Table, Metrics, inputClassDef,backtrackClassDef,lookaheadClassDef, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); } return match; }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics ClassDefTable inputClassDef, ClassDefTable backtrackClassDef, ClassDefTable lookaheadClassDef, 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 ushort LookupFlags, // Lookup table flags 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 ) { bool match = true; NextGlyph = FirstGlyph + 1; //In case we don't match //We are moving through table. We can pick glyph count or glyph class id. int curOffset = offset; int glyphIndex; // //Check backtrack sequence // int backtrackGlyphCount = GlyphCount(Table,curOffset); curOffset += sizeCount; glyphIndex = FirstGlyph; for(ushort backtrackIndex = 0; backtrackIndex < backtrackGlyphCount && match; backtrackIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex-1, LookupFlags, LayoutEngine.LookBackward ); if (glyphIndex<0) { match = false; } else { ushort classId = ClassId(Table,curOffset); curOffset+=sizeClassId; ushort glyphClass = backtrackClassDef. GetClass(Table,GlyphInfo.Glyphs[glyphIndex]); match = (glyphClass == classId); } } if (!match) return false; // // Check input sequence // int inputGlyphCount = GlyphCount(Table,curOffset); curOffset += sizeCount; glyphIndex = FirstGlyph; for(ushort inputIndex = 1; //go from second glyph in the input inputIndex < inputGlyphCount && match; inputIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex+1, LookupFlags, LayoutEngine.LookForward ); if (glyphIndex >= AfterLastGlyph) { match = false; } else { ushort classId = ClassId(Table,curOffset); curOffset+=sizeClassId; ushort glyphClass = inputClassDef. GetClass(Table,GlyphInfo.Glyphs[glyphIndex]); match = (glyphClass == classId); } } if (!match) return false; int afterInputGlyph = glyphIndex + 1; // remember where we were after input seqence // // Check lookahead sequence // int lookaheadGlyphCount = GlyphCount(Table,curOffset); curOffset += sizeCount; // Lokahead sequence starting right after input, // no need to change current glyphIndex for(ushort lookaheadIndex = 0; lookaheadIndex < lookaheadGlyphCount && match; lookaheadIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex+1, LookupFlags, LayoutEngine.LookForward ); if (glyphIndex >= GlyphInfo.Length) { match = false; } else { ushort classId = ClassId(Table,curOffset); curOffset+=sizeClassId; ushort glyphClass = lookaheadClassDef. GetClass(Table,GlyphInfo.Glyphs[glyphIndex]); match = (glyphClass == classId); } } if (match) { ContextualLookups(Table,curOffset).ApplyContextualLookups( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, afterInputGlyph, //As AfterLastGlyph Parameter, nestingLevel, out NextGlyph ); } return match; }
public unsafe void ApplyContextualLookups( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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 ushort LookupFlags, // Lookup table flags 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 ) { // Limit nesting level for contextual lookups to // prevent infinite loops from corrupt fonts if (nestingLevel >= MaximumContextualLookupNestingLevel) { nextGlyph = AfterLastGlyph; return; } LookupList lookupList; if (TableTag == OpenTypeTags.GSUB) { lookupList = (new GSUBHeader(0)).GetLookupList(Table); } else { lookupList = (new GPOSHeader(0)).GetLookupList(Table); } int prevLookupIndex = -1; int prevSequenceIndex = -1; while (true) { ushort lookupIndex = ushort.MaxValue; ushort sequenceIndex = ushort.MaxValue; for(ushort i = 0; i < recordCount; i++) { ushort recordLookupIndex = LookupIndex(Table,i); ushort recordSequenceIndex = SequenceIndex(Table,i); if (recordLookupIndex < prevLookupIndex || (recordLookupIndex == prevLookupIndex && recordSequenceIndex <= prevSequenceIndex ) ) { // This record we already should have been processed continue; } // Among not proccessed record, find next one if ( recordLookupIndex < lookupIndex || (recordLookupIndex == lookupIndex && recordSequenceIndex < sequenceIndex ) ) { lookupIndex = recordLookupIndex; sequenceIndex = recordSequenceIndex; } } if (lookupIndex == ushort.MaxValue) { // All records processed (or we had duplicate records, which we skipped automatically) break; } //remember for the next iteration prevLookupIndex = lookupIndex; prevSequenceIndex = sequenceIndex; // Now find actual glyph where to apply lookup (may depend on lookup flags) int recordFirstGlyph = FirstGlyph; for (int i = 0; i < sequenceIndex && recordFirstGlyph < AfterLastGlyph; i++) { recordFirstGlyph = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, recordFirstGlyph + 1, LookupFlags, LayoutEngine.LookForward ); } if (recordFirstGlyph >= AfterLastGlyph) { // Requested position is outside of input sequence, do nothing. continue; } // And finally apply lookup int prevLength = GlyphInfo.Length; int dummyNextGlyph; LayoutEngine.ApplyLookup( Font, TableTag, Table, Metrics, lookupList.Lookup(Table, lookupIndex), CharCount, Charmap, GlyphInfo, Advances, Offsets, recordFirstGlyph, AfterLastGlyph, Parameter, nestingLevel + 1, out dummyNextGlyph // we don't need it here ); //We need to adjust afterLastGlyph, in case non-single substitution happened AfterLastGlyph += GlyphInfo.Length - prevLength; } nextGlyph = AfterLastGlyph; }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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 ushort LookupFlags, // Lookup table flags 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 ) { Invariant.Assert(Format(Table)==1); NextGlyph = FirstGlyph + 1; //in case we don't match int glyphCount = GlyphInfo.Length; int glyphIndex = FirstGlyph; ushort glyphId = GlyphInfo.Glyphs[glyphIndex]; int coverageIndex = Coverage(Table).GetGlyphIndex(Table,glyphId); if (coverageIndex < 0) return false; SubRuleSet subRuleSet= RuleSet(Table, coverageIndex); ushort ruleCount = subRuleSet.RuleCount(Table); bool match = false; for(ushort i=0; !match && i<ruleCount; i++) { match = subRuleSet.Rule(Table,i).Apply(Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); } return match; }
private static bool IsLookupReversal(OpenTypeTags TableTag, ushort LookupType) { return (TableTag == OpenTypeTags.GSUB && LookupType == 8); }
private static int FillTableCache( IOpenTypeFont font, OpenTypeTags tableTag, int cacheSize, GlyphLookupRecord[] records, int recordCount, int glyphCount, int lastLookupAdded ) { // Fill the cache. // We are using basically the same code to fill the cache // that had been used to calculate the size. So pList pointer // moving through cache memory should not overrun allocated space. // Asserts are set to chek that at every place where we write to cache // and at the end where we check that we filled exactly the same amount. unsafe { byte[] cache = font.AllocateTableCache(tableTag, cacheSize); if (cache == null) { // We failed to allocate cache of requested size, // exit without created cache. return(0); } fixed(byte *pCacheByte = &cache[0]) { ushort *pCache = (ushort *)pCacheByte; pCache[0] = (ushort)cacheSize; // Cache size pCache[1] = 0xFFFF; // 0xFFFF constants pCache[2] = (ushort)(lastLookupAdded + 1); // Number of lookups that fit into the cache pCache[3] = (ushort)glyphCount; // Glyph count ushort *pGlyphs = pCache + 4; ushort *pList = pGlyphs + glyphCount * 2; ushort *pPrevList = null; int prevListIndex = -1, prevListLength = 0; int curListIndex = 0, curListLength = 1; ushort curGlyph = records[0].Glyph; for (int i = 1; i < recordCount; i++) { if (records[i].Glyph != curGlyph) { // We've found another list. Compare it with previous if (prevListLength != curListLength || // Fast check to avoid full comparison !CompareGlyphRecordLists(records, recordCount, prevListIndex, curListIndex) ) { // New list. Remember position in pPrevList and write list down pPrevList = pList; for (int j = curListIndex; j < i; j++) { Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = records[j].Lookup; pList++; } Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = 0xFFFF; pList++; } // Now pPrevList points at the first element of the correct list. *pGlyphs = curGlyph; // Write down glyph id pGlyphs++; *pGlyphs = (ushort)((pPrevList - pCache) * sizeof(ushort)); // Write down list offset pGlyphs++; prevListIndex = curListIndex; prevListLength = curListLength; curGlyph = records[i].Glyph; curListIndex = i; curListLength = 1; } } // And we need to check the last list we missed in the loop if (prevListLength != curListLength || // Fast check to avoid full comparison !CompareGlyphRecordLists(records, recordCount, prevListIndex, curListIndex) ) { // New list. Remember position in pPrevList and write list down pPrevList = pList; for (int j = curListIndex; j < recordCount; j++) { Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = records[j].Lookup; pList++; } Debug.Assert((pList - pCache) * sizeof(ushort) < cacheSize); *pList = 0xFFFF; pList++; } // Now pPrevList points at the first element of the correct list. *pGlyphs = curGlyph; // Write down glyph id pGlyphs++; *pGlyphs = (ushort)((pPrevList - pCache) * sizeof(ushort)); // Write down list offset pGlyphs++; // We are done with the cache Debug.Assert((pList - pCache) * sizeof(ushort) == cacheSize); // We exactly filled up the cache Debug.Assert((pGlyphs - pCache) * sizeof(ushort) == (4 + glyphCount * 2) * sizeof(ushort)); // Glyphs ended where lists start. } } return(cacheSize); }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics ClassDefTable ClassDef, 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 ushort LookupFlags, // Lookup table flags 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 ) { NextGlyph = FirstGlyph + 1; //In case we don't match // // Check input sequence // bool match = true; int glyphIndex = FirstGlyph; int inputGlyphCount = GlyphCount(Table); for(ushort inputIndex = 1; // go from second glyph in the input inputIndex < inputGlyphCount && match; inputIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex+1, LookupFlags, LayoutEngine.LookForward ); if (glyphIndex>=AfterLastGlyph) { match = false; } else { ushort classId = ClassId(Table,inputIndex); ushort glyphClass = ClassDef.GetClass(Table,GlyphInfo.Glyphs[glyphIndex]); match = (glyphClass == classId); } } if (match) { ContextualLookups(Table).ApplyContextualLookups( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, glyphIndex + 1, //As AfterLastGlyph Parameter, nestingLevel, out NextGlyph ); } return match; }
public byte[] AllocateTableCache(OpenTypeTags tableTag, int size) { return(_layout.AllocateTableCache(tableTag, size)); }
public byte[] GetTableCache(OpenTypeTags tableTag) { return(_layout.GetTableCache(tableTag)); }
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 ); } } }
private static CoverageTable GetSubtablePrincipalCoverage( FontTable table, OpenTypeTags tableTag, ushort lookupType, int subtableOffset ) { Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); CoverageTable coverage = CoverageTable.InvalidCoverage; 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 singleSubst = new SingleSubstitutionSubtable(subtableOffset); return(singleSubst.GetPrimaryCoverage(table)); case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); return(multipleSub.GetPrimaryCoverage(table)); case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); return(alternateSub.GetPrimaryCoverage(table)); case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); return(ligaSub.GetPrimaryCoverage(table)); case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); return(contextSub.GetPrimaryCoverage(table)); case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); return(chainingSub.GetPrimaryCoverage(table)); case 7: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); return(reverseChainingSub.GetPrimaryCoverage(table)); } 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); return(singlePos.GetPrimaryCoverage(table)); case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); return(pairPos.GetPrimaryCoverage(table)); case 3: // CursivePos CursivePositioningSubtable cursivePos = new CursivePositioningSubtable(subtableOffset); return(cursivePos.GetPrimaryCoverage(table)); case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); return(markToBasePos.GetPrimaryCoverage(table)); case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); return(markToLigaPos.GetPrimaryCoverage(table)); case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); return(markToMarkPos.GetPrimaryCoverage(table)); case 7: // Contextual ContextSubtable contextPos = new ContextSubtable(subtableOffset); return(contextPos.GetPrimaryCoverage(table)); case 8: // Chaining ChainingSubtable chainingPos = new ChainingSubtable(subtableOffset); return(chainingPos.GetPrimaryCoverage(table)); case 9: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it break; } break; } return(CoverageTable.InvalidCoverage); }
private static void ComputeTableCache( IOpenTypeFont font, OpenTypeTags tableTag, int maxCacheSize, ref int cacheSize, ref GlyphLookupRecord[] records, ref int recordCount, ref int glyphCount, ref int lastLookupAdded ) { FontTable table = font.GetFontTable(tableTag); if (!table.IsPresent) { return; } FeatureList featureList; LookupList lookupList; Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); switch (tableTag) { case OpenTypeTags.GSUB: { GSUBHeader header = new GSUBHeader(); featureList = header.GetFeatureList(table); lookupList = header.GetLookupList(table); break; } case OpenTypeTags.GPOS: { GPOSHeader header = new GPOSHeader(); featureList = header.GetFeatureList(table); lookupList = header.GetLookupList(table); break; } default: { Debug.Assert(false); featureList = new FeatureList(0); lookupList = new LookupList(0); break; } } // Estimate number of records that can fit into cache using ratio of approximately // 4 bytes of cache per actual record. Most of fonts will fit into this value, except // some tiny caches and big EA font that can have ratio of around 5 (theoretical maximum is 8). // // If actual ratio for particluar font will be larger than 4, we will remove records // from the end to fit into cache. // // If ratio is less than 4 we actually can fit more lookups, but for the speed and because most fonts // will fit into cache completely anyway we do not do anything about this here. int maxRecordCount = maxCacheSize / 4; // For now, we will just allocate array of maximum size. // Given heuristics above, it wont be greater than max cache size. // records = new GlyphLookupRecord[maxRecordCount]; // // Now iterate through lookups and subtables, filling in lookup-glyph pairs list // int lookupCount = lookupList.LookupCount(table); int recordCountAfterLastLookup = 0; // // Not all lookups can be invoked from feature directly, // they are actions from contextual lookups. // We are not interested in those, because they will // never work from high level, so do not bother adding them to the cache. // // Filling array of lookup usage bits, to skip those not mapped to any lookup // BitArray lookupUsage = new BitArray(lookupCount); for (ushort feature = 0; feature < featureList.FeatureCount(table); feature++) { FeatureTable featureTable = featureList.FeatureTable(table, feature); for (ushort lookup = 0; lookup < featureTable.LookupCount(table); lookup++) { ushort lookupIndex = featureTable.LookupIndex(table, lookup); if (lookupIndex >= lookupCount) { // This must be an invalid font. Just igonoring this lookup here. continue; } lookupUsage[lookupIndex] = true; } } // Done with lookup usage bits for (ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!lookupUsage[lookupIndex]) { continue; } int firstLookupRecord = recordCount; int maxLookupGlyph = -1; bool cacheIsFull = false; LookupTable lookup = lookupList.Lookup(table, lookupIndex); ushort lookupType = lookup.LookupType(); ushort subtableCount = lookup.SubTableCount(); for (ushort subtableIndex = 0; subtableIndex < subtableCount; subtableIndex++) { int subtableOffset = lookup.SubtableOffset(table, subtableIndex); CoverageTable coverage = GetSubtablePrincipalCoverage(table, tableTag, lookupType, subtableOffset); if (coverage.IsInvalid) { continue; } cacheIsFull = !AppendCoverageGlyphRecords(table, lookupIndex, coverage, records, ref recordCount, ref maxLookupGlyph); if (cacheIsFull) { break; } } if (cacheIsFull) { break; } lastLookupAdded = lookupIndex; recordCountAfterLastLookup = recordCount; } // We may hit storage overflow in the middle of lookup. Throw this partial lookup away recordCount = recordCountAfterLastLookup; if (lastLookupAdded == -1) { // We did not succeed adding even single lookup. return; } // We now have glyph records for (may be not all) lookups in the table. // Cache structures should be sorted by glyph, then by lookup index. Array.Sort(records, 0, recordCount); cacheSize = -1; glyphCount = -1; // It may happen, that records do not fit into cache, even using our heuristics. // We will remove lookups one by one from the end until it fits. while (recordCount > 0) { CalculateCacheSize(records, recordCount, out cacheSize, out glyphCount); if (cacheSize <= maxCacheSize) { // Fine, we now fit into max cache size break; } else { // Find last lookup index int lastLookup = -1; for (int i = 0; i < recordCount; i++) { int lookup = records[i].Lookup; if (lastLookup < lookup) { lastLookup = lookup; } } Debug.Assert(lastLookup >= 0); // There are lookups, so there was an index // Remove it int currentRecord = 0; for (int i = 0; i < recordCount; i++) { if (records[i].Lookup == lastLookup) { continue; } if (currentRecord == i) { continue; } records[currentRecord] = records[i]; currentRecord++; } recordCount = currentRecord; // Do not forget update lastLookupAdded variable lastLookupAdded = lastLookup - 1; } } if (recordCount == 0) { // We can't fit even single lookup into the cache return; } Debug.Assert(cacheSize > 0); // We've calcucalted them at least ones, and Debug.Assert(glyphCount > 0); // if there is no records, we already should've exited }
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); Debug.Assert(AfterLastGlyph<=GlyphInfo.Length); ushort lookupType = Lookup.LookupType(); ushort lookupFlags = Lookup.LookupFlags(); ushort subtableCount = Lookup.SubTableCount(); bool match=false; NextGlyph=FirstGlyph+1; //Just to avoid compiler error // Find first glyph if (!IsLookupReversal(TableTag,lookupType)) { FirstGlyph=LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo,FirstGlyph, lookupFlags,LayoutEngine.LookForward); } else { AfterLastGlyph = LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo,AfterLastGlyph-1, lookupFlags,LayoutEngine.LookBackward) + 1; } if (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; }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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 ushort LookupFlags, // Lookup table flags 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 ) { switch (Format(Table)) { case 1: GlyphContextSubtable glyphContextSubtable = new GlyphContextSubtable(offset); return glyphContextSubtable.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); case 2: ClassContextSubtable classContextSubtable = new ClassContextSubtable(offset); return classContextSubtable.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); case 3: CoverageContextSubtable coverageContextSubtable = new CoverageContextSubtable(offset); return coverageContextSubtable.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); default: //Unknown format NextGlyph = FirstGlyph+1; //don't match return false; } }
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); } } } }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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 ushort LookupFlags, // Lookup table flags int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use uint Parameter, // lookup parameter out int NextGlyph // out: next glyph index ) { //This will be the next glyph, does not matter sequence matched or not NextGlyph = AfterLastGlyph-1; if (Format(Table)!=1) return false; //Unknown bool match = true; int inputGlyphIndex = AfterLastGlyph - 1; int glyphIndex; //Check input glyph int coverageIndex = InputCoverage(Table).GetGlyphIndex(Table,GlyphInfo.Glyphs[inputGlyphIndex]); if (coverageIndex<0) return false; //we reading data sequenctially from table, moving pointer through it int curOffset = offset + offsetBacktrackGlyphCount; // // Check backtrack sequence // ushort backtrackGlyphCount = GlyphCount(Table,curOffset); curOffset += sizeCount; glyphIndex = inputGlyphIndex; for(ushort backtrackIndex = 0; backtrackIndex < backtrackGlyphCount && match; backtrackIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex-1, LookupFlags, LayoutEngine.LookBackward ); if (glyphIndex<0) { match = false; } else { match = (Coverage(Table,curOffset).GetGlyphIndex(Table,GlyphInfo.Glyphs[glyphIndex]) >= 0); curOffset += sizeOffset; } } ushort lookaheadGlyphCount = GlyphCount(Table,curOffset); curOffset += sizeCount; glyphIndex = inputGlyphIndex; for(ushort lookaheadIndex = 0; lookaheadIndex < lookaheadGlyphCount && match; lookaheadIndex++) { glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, glyphIndex+1, LookupFlags, LayoutEngine.LookForward ); if (glyphIndex>=GlyphInfo.Length) { match = false; } else { match = (Coverage(Table,curOffset).GetGlyphIndex(Table,GlyphInfo.Glyphs[glyphIndex]) >= 0); curOffset += sizeOffset; } } if (match) { curOffset += sizeCount + sizeGlyphId*coverageIndex; GlyphInfo.Glyphs[inputGlyphIndex] = Glyph(Table,curOffset); GlyphInfo.GlyphFlags[inputGlyphIndex] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); } return match; }
/// <summary> /// Reset all structures to the new font/OTTable/script/langsys. /// /// Client need to call it only once per shaping engine call. /// This is client's responsibility to ensure that workspace is /// used for single font/OTTable/script/langsys between Init() calls /// </summary> ///<param name="font">In: Font access interface</param> ///<param name="tableTag">In: Font table tag</param> ///<param name="scriptTag">In: Script tag</param> ///<param name="langSysTag">In: Language System tag</param> ///<returns>Success if workspace is initialized succesfully, specific error if failed</returns> internal OpenTypeLayoutResult Init( IOpenTypeFont font, OpenTypeTags tableTag, uint scriptTag, uint langSysTag ) { // Currently all buffers are per call, // no need to do anything. return OpenTypeLayoutResult.Success; }