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> /// ///</summary> internal static OpenTypeLayoutResult CreateLayoutCache( IOpenTypeFont font, // In: Font access interface int maxCacheSize // In: Maximum cache size allowed ) { OpenTypeLayoutCache.CreateCache(font, maxCacheSize); return(OpenTypeLayoutResult.Success); }
/// <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); }
/// <summary> /// /// </summary> /// <param name="Font">Font</param> /// <param name="ScriptTag">Script to search in</param> /// <param name="LangSysTag">LangGys to search for</param> /// <returns>TagInfoFlags, if script not present == None</returns> 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); }
internal static void CreateCache(IOpenTypeFont font, int maxCacheSize) { if (maxCacheSize > ushort.MaxValue) { // Data structures do not support cache sizes more than 64K. maxCacheSize = ushort.MaxValue; } int tableCacheSize; int totalSize = 0; CreateTableCache(font, OpenTypeTags.GSUB, maxCacheSize - totalSize, out tableCacheSize); totalSize += tableCacheSize; Debug.Assert(totalSize <= maxCacheSize); CreateTableCache(font, OpenTypeTags.GPOS, maxCacheSize - totalSize, out tableCacheSize); totalSize += tableCacheSize; Debug.Assert(totalSize <= maxCacheSize); }
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 ); } }
internal static OpenTypeLayoutResult CreateLayoutCache ( IOpenTypeFont font, // In: Font access interface int maxCacheSize // In: Maximum cache size allowed ) { OpenTypeLayoutCache.CreateCache(font, maxCacheSize); return OpenTypeLayoutResult.Success; }
public unsafe bool Apply( IOpenTypeFont Font, FontTable Table, int CharCount, UshortList Charmap, // Character to glyph map GlyphInfoList GlyphInfo, // List of GlyphInfo ushort LookupFlags, // Lookup flags for glyph lookups int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph>=0); Invariant.Assert(AfterLastGlyph<=GlyphInfo.Length); NextGlyph = FirstGlyph + 1; //In case we don't match; if (Format(Table) != 1) return false; // Unknown format int glyphCount=GlyphInfo.Length; ushort glyphId = GlyphInfo.Glyphs[FirstGlyph]; int CoverageIndex = Coverage(Table).GetGlyphIndex(Table,glyphId); if (CoverageIndex==-1) return false; int curGlyph; ushort ligatureGlyph=0; bool match = false; ushort compCount=0; LigatureSetTable ligatureSet = LigatureSet(Table,(ushort)CoverageIndex); ushort ligaCount = ligatureSet.LigatureCount(Table); for(ushort liga=0; liga<ligaCount; liga++) { LigatureTable ligature = ligatureSet.Ligature(Table,liga); compCount = ligature.ComponentCount(Table); if (compCount == 0) { throw new FileFormatException(); } curGlyph=FirstGlyph; ushort comp=1; for(comp=1;comp<compCount;comp++) { curGlyph = LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo,curGlyph+1,LookupFlags,LayoutEngine.LookForward); if (curGlyph>=AfterLastGlyph) break; if (GlyphInfo.Glyphs[curGlyph]!=ligature.Component(Table,comp)) break; } if (comp==compCount) //liga matched { match=true; ligatureGlyph = ligature.LigatureGlyph(Table); break; //Liga found } } //If no ligature found, match will remain false after last iteration if (match) { //Fix character and glyph Mapping //PERF: localize ligature character range //Calculate Ligature CharCount int totalLigaCharCount=0; int firstLigaChar=int.MaxValue; curGlyph=FirstGlyph; for(ushort comp=0;comp<compCount;comp++) { Invariant.Assert(curGlyph<AfterLastGlyph); int curFirstChar = GlyphInfo.FirstChars[curGlyph]; int curLigaCount = GlyphInfo.LigatureCounts[curGlyph]; totalLigaCharCount += curLigaCount; if (curFirstChar<firstLigaChar) firstLigaChar=curFirstChar; curGlyph = LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo,curGlyph+1,LookupFlags,LayoutEngine.LookForward); } curGlyph=FirstGlyph; int prevGlyph=FirstGlyph; ushort shift=0; for(ushort comp=1;comp<=compCount;comp++) { prevGlyph=curGlyph; if (comp<compCount) { curGlyph = LayoutEngine. GetNextGlyphInLookup(Font,GlyphInfo, curGlyph+1, LookupFlags, LayoutEngine.LookForward); } else curGlyph = GlyphInfo.Length; // to the end from last component // Set charmap for ligature component for(int curChar=0; curChar<CharCount; curChar++) { if (Charmap[curChar]==prevGlyph) { Charmap[curChar] = (ushort)FirstGlyph; } } //Shift glyphInfo if (shift>0) { for(int glyph=prevGlyph+1; glyph<curGlyph; glyph++) { GlyphInfo.Glyphs[glyph-shift] = GlyphInfo.Glyphs[glyph]; GlyphInfo.GlyphFlags[glyph-shift] = GlyphInfo.GlyphFlags[glyph]; GlyphInfo.FirstChars[glyph-shift] = GlyphInfo.FirstChars[glyph]; GlyphInfo.LigatureCounts[glyph-shift] = GlyphInfo.LigatureCounts[glyph]; } if (curGlyph-prevGlyph>1) //do fixing only if have glyphs in between { for(int curChar=0; curChar<CharCount; curChar++) { ushort curCharmap = Charmap[curChar]; if (curCharmap>prevGlyph && curCharmap<curGlyph) { Charmap[curChar] -= shift; } } } } ++shift; } //Place new glyph into position of first ligature glyph GlyphInfo.Glyphs[FirstGlyph] = ligatureGlyph; GlyphInfo.GlyphFlags[FirstGlyph] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); GlyphInfo.FirstChars[FirstGlyph] = (ushort)firstLigaChar; GlyphInfo.LigatureCounts[FirstGlyph] = (ushort)totalLigaCharCount; //remove empty space if (compCount > 1) { GlyphInfo.Remove(GlyphInfo.Length-compCount+1,compCount-1); } NextGlyph=prevGlyph-(compCount-1)+1; } return match; }
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; }
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 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 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 bool Apply( IOpenTypeFont Font, FontTable Table, LayoutMetrics Metrics, // LayoutMetrics GlyphInfoList GlyphInfo, // List of GlyphInfo structs ushort LookupFlags, // Lookup flags for glyph lookups int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets int FirstGlyph, // where to apply lookup int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph>=0); Invariant.Assert(AfterLastGlyph<=GlyphInfo.Length); NextGlyph = FirstGlyph+1; //Always move to the next glyph, whether matched or not if (Format(Table) != 1) return false; //unknown format int glyphCount=GlyphInfo.Length; int mark1Glyph=FirstGlyph; //Lookup works with marks only if ((GlyphInfo.GlyphFlags[mark1Glyph]&(ushort)GlyphFlags.GlyphTypeMask)!=(ushort)GlyphFlags.Mark) return false; int mark1CoverageIndex = Mark1Coverage(Table).GetGlyphIndex(Table,GlyphInfo.Glyphs[mark1Glyph]); if (mark1CoverageIndex==-1) return false; //Find preceeding mark according mark from specified class int mark2Glyph = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, FirstGlyph-1, (ushort)(LookupFlags & 0xFF00), //Clear Ignore... flags LayoutEngine.LookBackward); if (mark2Glyph<0) return false; int mark2CoverageIndex = Mark2Coverage(Table).GetGlyphIndex(Table,GlyphInfo.Glyphs[mark2Glyph]); if (mark2CoverageIndex==-1) return false; ushort classCount = Mark1ClassCount(Table); MarkArray mark1Array = Mark1Array(Table); ushort mark1Class = mark1Array.Class(Table,(ushort)mark1CoverageIndex); if (mark1Class>=classCount) return false; //Invalid mark class AnchorTable mark1Anchor = mark1Array.MarkAnchor(Table,(ushort)mark1CoverageIndex); if (mark1Anchor.IsNull()) { return false; } AnchorTable mark2Anchor = Marks2(Table).Anchor(Table,(ushort)mark2CoverageIndex,classCount,mark1Class); if (mark2Anchor.IsNull()) { return false; } Positioning.AlignAnchors(Font,Table,Metrics,GlyphInfo,Advances,Offsets, mark2Glyph,mark1Glyph,mark2Anchor,mark1Anchor,false); return true; }
public unsafe bool Apply( IOpenTypeFont Font, FontTable Table, int CharCount, UshortList Charmap, // Character to glyph map GlyphInfoList GlyphInfo, // List of GlyphInfo ushort LookupFlags, // Lookup flags for glyph lookups int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { NextGlyph = FirstGlyph + 1; // in case we don't match if (Format(Table) != 1) { return(false); //unknown format } int oldGlyphCount = GlyphInfo.Length; ushort glyphId = GlyphInfo.Glyphs[FirstGlyph]; int coverageIndex = Coverage(Table).GetGlyphIndex(Table, glyphId); if (coverageIndex == -1) { return(false); } MultipleSubstitutionSequenceTable sequence = Sequence(Table, coverageIndex); ushort sequenceLength = sequence.GlyphCount(Table); int lengthDelta = sequenceLength - 1; if (sequenceLength == 0) { // This is illegal, because mapping will be broken - // corresponding char will be lost. Just leave it as it is. // (char will be attached to the following glyph). GlyphInfo.Remove(FirstGlyph, 1); } else { ushort firstChar = GlyphInfo.FirstChars[FirstGlyph]; ushort ligatureCount = GlyphInfo.LigatureCounts[FirstGlyph]; if (lengthDelta > 0) { GlyphInfo.Insert(FirstGlyph, lengthDelta); } //put glyphs in place for (ushort gl = 0; gl < sequenceLength; gl++) { GlyphInfo.Glyphs[FirstGlyph + gl] = sequence.Glyph(Table, gl); GlyphInfo.GlyphFlags[FirstGlyph + gl] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); GlyphInfo.FirstChars[FirstGlyph + gl] = firstChar; GlyphInfo.LigatureCounts[FirstGlyph + gl] = ligatureCount; } } // Fix char mapping - very simple for now. // Works only for arabic base+mark -> base and marks decomposition // Needs work for full mapping for (int ch = 0; ch < CharCount; ch++) { if (Charmap[ch] > FirstGlyph) { Charmap[ch] = (ushort)(Charmap[ch] + lengthDelta); } } NextGlyph = FirstGlyph + lengthDelta + 1; return(true); }
public unsafe bool Apply( IOpenTypeFont Font, FontTable Table, int CharCount, UshortList Charmap, // Character to glyph map GlyphInfoList GlyphInfo, // List of GlyphInfo ushort LookupFlags, // Lookup flags for glyph lookups int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph >= 0); Invariant.Assert(AfterLastGlyph <= GlyphInfo.Length); NextGlyph = FirstGlyph + 1; //In case we don't match; if (Format(Table) != 1) { return(false); // Unknown format } int glyphCount = GlyphInfo.Length; ushort glyphId = GlyphInfo.Glyphs[FirstGlyph]; int CoverageIndex = Coverage(Table).GetGlyphIndex(Table, glyphId); if (CoverageIndex == -1) { return(false); } int curGlyph; ushort ligatureGlyph = 0; bool match = false; ushort compCount = 0; LigatureSetTable ligatureSet = LigatureSet(Table, (ushort)CoverageIndex); ushort ligaCount = ligatureSet.LigatureCount(Table); for (ushort liga = 0; liga < ligaCount; liga++) { LigatureTable ligature = ligatureSet.Ligature(Table, liga); compCount = ligature.ComponentCount(Table); if (compCount == 0) { throw new FileFormatException(); } curGlyph = FirstGlyph; ushort comp = 1; for (comp = 1; comp < compCount; comp++) { curGlyph = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, curGlyph + 1, LookupFlags, LayoutEngine.LookForward); if (curGlyph >= AfterLastGlyph) { break; } if (GlyphInfo.Glyphs[curGlyph] != ligature.Component(Table, comp)) { break; } } if (comp == compCount) //liga matched { match = true; ligatureGlyph = ligature.LigatureGlyph(Table); break; //Liga found } } //If no ligature found, match will remain false after last iteration if (match) { //Fix character and glyph Mapping //PERF: localize ligature character range //Calculate Ligature CharCount int totalLigaCharCount = 0; int firstLigaChar = int.MaxValue; curGlyph = FirstGlyph; for (ushort comp = 0; comp < compCount; comp++) { Invariant.Assert(curGlyph < AfterLastGlyph); int curFirstChar = GlyphInfo.FirstChars[curGlyph]; int curLigaCount = GlyphInfo.LigatureCounts[curGlyph]; totalLigaCharCount += curLigaCount; if (curFirstChar < firstLigaChar) { firstLigaChar = curFirstChar; } curGlyph = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, curGlyph + 1, LookupFlags, LayoutEngine.LookForward); } curGlyph = FirstGlyph; int prevGlyph = FirstGlyph; ushort shift = 0; for (ushort comp = 1; comp <= compCount; comp++) { prevGlyph = curGlyph; if (comp < compCount) { curGlyph = LayoutEngine. GetNextGlyphInLookup(Font, GlyphInfo, curGlyph + 1, LookupFlags, LayoutEngine.LookForward); } else { curGlyph = GlyphInfo.Length; // to the end from last component } // Set charmap for ligature component for (int curChar = 0; curChar < CharCount; curChar++) { if (Charmap[curChar] == prevGlyph) { Charmap[curChar] = (ushort)FirstGlyph; } } //Shift glyphInfo if (shift > 0) { for (int glyph = prevGlyph + 1; glyph < curGlyph; glyph++) { GlyphInfo.Glyphs[glyph - shift] = GlyphInfo.Glyphs[glyph]; GlyphInfo.GlyphFlags[glyph - shift] = GlyphInfo.GlyphFlags[glyph]; GlyphInfo.FirstChars[glyph - shift] = GlyphInfo.FirstChars[glyph]; GlyphInfo.LigatureCounts[glyph - shift] = GlyphInfo.LigatureCounts[glyph]; } if (curGlyph - prevGlyph > 1) //do fixing only if have glyphs in between { for (int curChar = 0; curChar < CharCount; curChar++) { ushort curCharmap = Charmap[curChar]; if (curCharmap > prevGlyph && curCharmap < curGlyph) { Charmap[curChar] -= shift; } } } } ++shift; } //Place new glyph into position of first ligature glyph GlyphInfo.Glyphs[FirstGlyph] = ligatureGlyph; GlyphInfo.GlyphFlags[FirstGlyph] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); GlyphInfo.FirstChars[FirstGlyph] = (ushort)firstLigaChar; GlyphInfo.LigatureCounts[FirstGlyph] = (ushort)totalLigaCharCount; //remove empty space if (compCount > 1) { GlyphInfo.Remove(GlyphInfo.Length - compCount + 1, compCount - 1); } NextGlyph = prevGlyph - (compCount - 1) + 1; } return(match); }
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<glyphRunLength && 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; }
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<AfterLastGlyph;i++) { ushort flags = (ushort)( (GlyphInfo.GlyphFlags[i] & (ushort)~typemask) | (ushort)GlyphFlags.Unassigned | (ushort)FlagToSet); } return; } GDEFHeader gdefHeader = new GDEFHeader(0); ClassDefTable GlyphClassDef = gdefHeader.GetGlyphClassDef(gdefTable); for(int i=FirstGlyph;i<AfterLastGlyph;i++) { ushort flags = (ushort)(GlyphInfo.GlyphFlags[i] | (ushort)FlagToSet); if ((flags & typemask) == (ushort)GlyphFlags.Unresolved || FlagToSet!=GlyphFlags.NotChanged) { ushort glyph = GlyphInfo.Glyphs[i]; flags &= (ushort)~typemask; int glyphClass = GlyphClassDef.GetClass(gdefTable,glyph); GlyphInfo.GlyphFlags[i] = (ushort)(flags| ((glyphClass==-1)? (ushort)GlyphFlags.Unassigned: (ushort)glyphClass ) ); } } }
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); } } } }
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 ) { 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; }
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, FontTable Table, LayoutMetrics Metrics, // LayoutMetrics GlyphInfoList GlyphInfo, // List of GlyphInfo structs ushort LookupFlags, // Lookup flags for glyph lookups int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets int FirstGlyph, // where to apply lookup int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph>=0); Invariant.Assert(AfterLastGlyph<=GlyphInfo.Length); NextGlyph = FirstGlyph+1; if (Format(Table) != 1) return false; // Unknown format bool RTL = (LookupFlags & LayoutEngine.LookupFlagRightToLeft) != 0; ushort cursiveBit = (ushort)GlyphFlags.CursiveConnected; // int glyphIndex, prevGlyphIndex, coverageIndex, prevCoverageIndex; glyphIndex = LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo, FirstGlyph,LookupFlags, LayoutEngine.LookForward ); //clear "CursiveConected" bit, //we will set it only if there is a connection to previous glyph if (RTL) { GlyphInfo.GlyphFlags[glyphIndex] &= (ushort)~cursiveBit; } if ( glyphIndex >= AfterLastGlyph ) { return false; } prevGlyphIndex = LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo, FirstGlyph-1,LookupFlags, LayoutEngine.LookBackward ); if ( prevGlyphIndex < 0 ) { return false; } CoverageTable coverage = Coverage(Table); coverageIndex = coverage.GetGlyphIndex(Table,GlyphInfo.Glyphs[glyphIndex]); if (coverageIndex == -1) { return false; } prevCoverageIndex = coverage. GetGlyphIndex(Table,GlyphInfo.Glyphs[prevGlyphIndex]); if (prevCoverageIndex == -1) { return false; } AnchorTable prevExitAnchor, entryAnchor; prevExitAnchor = ExitAnchor(Table, prevCoverageIndex); if (prevExitAnchor.IsNull()) { return false; } entryAnchor = EntryAnchor(Table,coverageIndex); if (entryAnchor.IsNull()) { return false; } Positioning.AlignAnchors(Font, Table, Metrics, GlyphInfo, Advances, Offsets, prevGlyphIndex, glyphIndex, prevExitAnchor, entryAnchor, true); if (RTL) { UshortList glyphFlags = GlyphInfo.GlyphFlags; int index; //set "cursive" bit for everything up to prevGlyphIndex for(index = glyphIndex; index>prevGlyphIndex; index--) { glyphFlags[index] |= cursiveBit; } //fix cursive dependencies int yCorrection = Offsets[glyphIndex].dy; for(index = glyphIndex; (glyphFlags[index] & cursiveBit) != 0 ; index-- ) { Offsets[index].dy -= yCorrection; } Invariant.Assert(glyphIndex>=0); //First glyph should not have bit set Offsets[index].dy -= yCorrection; } return true; }
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, FontTable Table, LayoutMetrics Metrics, // LayoutMetrics GlyphInfoList GlyphInfo, // List of GlyphInfo structs ushort LookupFlags, // Lookup flags for glyph lookups int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Char to glyph mapping int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets int FirstGlyph, // where to apply lookup int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph>=0); Invariant.Assert(AfterLastGlyph<=GlyphInfo.Length); NextGlyph = FirstGlyph+1; //Always move to the next glyph, whether matched or not if (Format(Table) != 1) return false; //unknown format int glyphCount=GlyphInfo.Length; int markGlyph=FirstGlyph; //Lookup works with marks only if ((GlyphInfo.GlyphFlags[markGlyph]&(ushort)GlyphFlags.GlyphTypeMask)!=(ushort)GlyphFlags.Mark) return false; int markCoverageIndex = MarkCoverage(Table).GetGlyphIndex(Table,GlyphInfo.Glyphs[markGlyph]); if (markCoverageIndex==-1) return false; int baseGlyph; ushort component; FindBaseLigature(CharCount, Charmap,GlyphInfo,markGlyph, out component, out baseGlyph); if (baseGlyph<0) return false; int baseCoverageIndex = LigatureCoverage(Table). GetGlyphIndex(Table,GlyphInfo.Glyphs[baseGlyph]); if (baseCoverageIndex == -1) return false; ushort classCount = ClassCount(Table); MarkArray marks = Marks(Table); ushort markClass = marks.Class(Table,(ushort)markCoverageIndex); if (markClass>=classCount) return false; //Invalid mark class AnchorTable baseAnchor = Ligatures(Table,baseCoverageIndex, classCount). LigatureAnchor(Table,component,markClass); if (baseAnchor.IsNull()) { return false; } AnchorTable markAnchor = marks.MarkAnchor(Table,(ushort)markCoverageIndex); if (markAnchor.IsNull()) { return false; } Positioning.AlignAnchors(Font,Table,Metrics,GlyphInfo,Advances,Offsets, baseGlyph,markGlyph,baseAnchor,markAnchor,false); return true; }
/// <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; }
internal static OpenTypeLayoutResult GetComplexLanguageList ( IOpenTypeFont Font, //In: Font access interface uint[] featureList, //In: Feature to look in uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId, out WritingSystem[] complexLanguages // Out: List of script/langauge pair // that are not optimizable ) { try { WritingSystem[] gsubComplexLanguages = null; WritingSystem[] gposComplexLanguages = null; int gsubComplexLanguagesCount = 0; int gposComplexLanguagesCount = 0; FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (GsubTable.IsPresent) { LayoutEngine.GetComplexLanguageList( OpenTypeTags.GSUB, GsubTable, featureList, glyphBits, minGlyphId, maxGlyphId, out gsubComplexLanguages, out gsubComplexLanguagesCount ); } if (GposTable.IsPresent) { LayoutEngine.GetComplexLanguageList( OpenTypeTags.GPOS, GposTable, featureList, glyphBits, minGlyphId, maxGlyphId, out gposComplexLanguages, out gposComplexLanguagesCount ); } if (gsubComplexLanguages == null && gposComplexLanguages == null) { complexLanguages = null; return OpenTypeLayoutResult.Success; } // Both tables have complex scrips, merge results // Count gpos unique Languages // and pack them at the same time // so we do not research them again. int gposNewLanguages=0, i, j; for(i = 0; i < gposComplexLanguagesCount ;i++) { bool foundInGsub = false; for(j = 0; j < gsubComplexLanguagesCount ;j++) { if (gsubComplexLanguages[j].scriptTag == gposComplexLanguages[i].scriptTag && gsubComplexLanguages[j].langSysTag == gposComplexLanguages[i].langSysTag ) { foundInGsub = true; break; }; } if (!foundInGsub) { if (gposNewLanguages < i) { gposComplexLanguages[gposNewLanguages] = gposComplexLanguages[i]; } gposNewLanguages++; } } //realloc array for merged results, merge both arrays complexLanguages = new WritingSystem[gsubComplexLanguagesCount + gposNewLanguages]; for(i = 0; i < gsubComplexLanguagesCount; i++) { complexLanguages[i] = gsubComplexLanguages[i]; } for(i = 0; i < gposNewLanguages; i++) { complexLanguages[gsubComplexLanguagesCount + i] = gposComplexLanguages[i]; } return OpenTypeLayoutResult.Success; } catch (FileFormatException) { complexLanguages = null; return OpenTypeLayoutResult.BadFontTable; } }
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 }
/* This is unused code, but will be used later so it is just commented out for now. * * /// <summary> * /// Enumerates scripts in a font * /// </summary> * internal static OpenTypeLayoutResult GetScriptList ( * IOpenTypeFont Font, // In: Font access interface * out TagInfo[] Scripts // Out: Array of scripts supported * ) * { * ushort i; * ushort GposNewTags; * * Scripts=null; // Assignment required, because of out attribute. * // This value should be owerwritten later. * * try * { * FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); * FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); * * GSUBHeader GsubHeader = new GSUBHeader(0); * GPOSHeader GposHeader = new GPOSHeader(0); * * ScriptList GsubScriptList; * ScriptList GposScriptList; * ushort GsubScriptCount; * ushort GposScriptCount; * * if (GsubTable.IsNotPresent && GposTable.IsNotPresent) * { * Scripts = Array.Empty<TagInfo>(); * return OpenTypeLayoutResult.Success; * } * * if (GsubTable.IsPresent) * { * GsubScriptList = GsubHeader.GetScriptList(GsubTable); * GsubScriptCount = GsubScriptList.GetScriptCount(GsubTable); * } * else * { * GsubScriptList = new ScriptList(FontTable.InvalidOffset); * GsubScriptCount = 0; * } * * if (GposTable.IsPresent) * { * GposScriptList = GposHeader.GetScriptList(GposTable); * GposScriptCount = GposScriptList.GetScriptCount(GposTable); * } * else * { * GposScriptList = new ScriptList(FontTable.InvalidOffset); * GposScriptCount = 0; * } * * //This is true in most cases that there is no new tags in GPOS. * //So, we allocate this array then check GPOS for new tags * Scripts = new TagInfo[GsubScriptCount]; * * for(i=0; i<GsubScriptCount; i++) * { * Scripts[i].Tag = GsubScriptList.GetScriptTag(GsubTable,i); * Scripts[i].TagFlags = TagInfoFlags.Substitution; * } * * //Check GPOS for tags that is not in GSUB * GposNewTags=0; * * for(i=0;i<GposScriptCount;i++) * { * uint GposTag = GsubScriptList.GetScriptTag(GposTable,i); * if (TagInfo.IsNewTag(Scripts,GposTag)) GposNewTags++; * } * * //append new tags to ScriptTags if any exists * if (GposNewTags>0) * { * int CurrentScriptIndex=GposScriptCount; * * //Allocate new array to fit all tags * TagInfo[] tmp = Scripts; * Scripts = new TagInfo[GsubScriptCount+GposNewTags]; * Array.Copy(tmp,0,Scripts,0,tmp.Length); * * for(i=0;i<GposScriptCount;i++) * { * uint GposTag = GsubScriptList.GetScriptTag(GposTable,i); * if (TagInfo.IsNewTag(Scripts,GposTag)) * { * Scripts[CurrentScriptIndex].Tag=GposTag; * Scripts[CurrentScriptIndex].TagFlags * = TagInfoFlags.Positioning; ++CurrentScriptIndex; * } * else * { * int ScriptIndex = TagInfo.GetTagIndex(Scripts,GposTag); * Scripts[ScriptIndex].TagFlags |= TagInfoFlags.Positioning; * } * } * * Debug.Assert(CurrentScriptIndex==Scripts.Length); * } * } * catch (FileFormatException) * { * return OpenTypeLayoutResult.BadFontTable; * } * * return OpenTypeLayoutResult.Success; * } * * * ///<summary> * /// Enumerates language systems for script * /// </summary> * internal static OpenTypeLayoutResult GetLangSysList ( * IOpenTypeFont Font, // In: Font access interface * uint ScriptTag, // In: Script tag * out TagInfo[] LangSystems // Out: Array of LangSystems for Script * ) * { * ushort i; * ushort GposNewTags; * * LangSystems=null; // Assignment required, because of out attribute. * // This value should be owerwritten later. * * try * { * FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); * FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); * * GSUBHeader GsubHeader = new GSUBHeader(0); * GPOSHeader GposHeader = new GPOSHeader(0); * * ScriptList GsubScriptList; * ScriptList GposScriptList; * ScriptTable GsubScript; * ScriptTable GposScript; * ushort GsubLangSysCount; * ushort GposLangSysCount; * * if (GsubTable.IsNotPresent && GposTable.IsNotPresent) * { * return OpenTypeLayoutResult.ScriptNotFound; * } * * if (GsubTable.IsPresent) * { * GsubScriptList = GsubHeader.GetScriptList(GsubTable); * GsubScript = GsubScriptList.FindScript(GsubTable,ScriptTag); * } * else * { * GsubScript = new ScriptTable(FontTable.InvalidOffset); * } * * if (GposTable.IsPresent) * { * GposScriptList = GposHeader.GetScriptList(GposTable); * GposScript = GposScriptList.FindScript(GposTable,ScriptTag); * } * else * { * GposScript = new ScriptTable(FontTable.InvalidOffset); * } * * if (GsubScript.IsNull && GposScript.IsNull) * { * return OpenTypeLayoutResult.ScriptNotFound; * } * * if (!GsubScript.IsNull) * { * GsubLangSysCount = GsubScript.GetLangSysCount(GsubTable); * } * else * { * GsubLangSysCount = 0; * } * * if (!GposScript.IsNull) * { * GposLangSysCount = GposScript.GetLangSysCount(GposTable); * } * else * { * GposLangSysCount = 0; * } * * //This is true in most cases that there is no new tags in GPOS. * //So, we allocate this array then check GPOS for new tags * ushort CurrentLangSysIndex; * * if (GsubScript.IsDefaultLangSysExists(GsubTable)) * { * LangSystems = new TagInfo[GsubLangSysCount+1]; * LangSystems[0].Tag = (uint)OpenTypeTags.dflt; * LangSystems[0].TagFlags = TagInfoFlags.Substitution; * CurrentLangSysIndex = 1; * } * else * { * LangSystems = new TagInfo[GsubLangSysCount]; * CurrentLangSysIndex = 0; * } * * for(i=0; i<GsubLangSysCount; i++) * { * LangSystems[CurrentLangSysIndex].Tag = GsubScript.GetLangSysTag(GsubTable,i); * LangSystems[CurrentLangSysIndex].TagFlags = TagInfoFlags.Substitution; ++CurrentLangSysIndex; * } * * //Check GPOS for tags that is not in GSUB * GposNewTags=0; * * if (!GposScript.IsNull) * { * if (GposScript.IsDefaultLangSysExists(GposTable) && * TagInfo.IsNewTag(LangSystems,(uint)OpenTypeTags.dflt)) * { ++GposNewTags; * } * * for(i=0;i<GposLangSysCount;i++) * { * uint GposTag = GsubScript.GetLangSysTag(GposTable,i); * if (TagInfo.IsNewTag(LangSystems,GposTag)) * { ++GposNewTags; * } * } * } * * Debug.Assert(CurrentLangSysIndex==LangSystems.Length); * * //append new tags to ScriptTags if any exists * if (GposNewTags>0) * { * //Allocate new array to fit all tags * TagInfo[] tmp = LangSystems; * LangSystems = new TagInfo[GsubLangSysCount+GposNewTags]; * Array.Copy(tmp,0,LangSystems,0,tmp.Length); * * if (GposScript.IsDefaultLangSysExists(GposTable)) * { * if (TagInfo.IsNewTag(LangSystems,(uint)OpenTypeTags.dflt)) * { * LangSystems[CurrentLangSysIndex].Tag = (uint)OpenTypeTags.dflt; * LangSystems[CurrentLangSysIndex].TagFlags = TagInfoFlags.Positioning; ++CurrentLangSysIndex; * } * else * { * int LangSysIndex = TagInfo.GetTagIndex(LangSystems,(uint)OpenTypeTags.dflt); * LangSystems[LangSysIndex].TagFlags |= TagInfoFlags.Positioning; * } * } * * for(i=0;i<GposLangSysCount;i++) * { * uint GposTag = GposScript.GetLangSysTag(GposTable,i); * * if (TagInfo.IsNewTag(LangSystems,GposTag)) * { * LangSystems[CurrentLangSysIndex].Tag = GposTag; * LangSystems[CurrentLangSysIndex].TagFlags = TagInfoFlags.Positioning; ++CurrentLangSysIndex; * } * else * { * int LangSysIndex = TagInfo.GetTagIndex(LangSystems,GposTag); * LangSystems[LangSysIndex].TagFlags |= TagInfoFlags.Positioning; * } * } * * Debug.Assert(CurrentLangSysIndex==LangSystems.Length); * } * } * catch (FileFormatException) * { * return OpenTypeLayoutResult.BadFontTable; * } * * return OpenTypeLayoutResult.Success; * } * * * /// <summary> * /// Enumerates features in a language system * /// </summary> * internal static OpenTypeLayoutResult GetFeatureList ( * IOpenTypeFont Font, // In: Font access interface * uint ScriptTag, // In: Script tag * uint LangSysTag, // In: LangSys tag * out TagInfo[] Features // Out: Array of features * ) * { * ushort i; * ushort GposNewTags; * * Features=null; // Assignment required, because of out attribute. * // This value should be owerwritten later. * * try * { * FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); * FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); * * GSUBHeader GsubHeader = new GSUBHeader(0); * GPOSHeader GposHeader = new GPOSHeader(0); * * ScriptList GsubScriptList; * ScriptList GposScriptList; * ScriptTable GsubScript; * ScriptTable GposScript; * LangSysTable GsubLangSys; * LangSysTable GposLangSys; * ushort GsubFeatureCount; * ushort GposFeatureCount; * FeatureList GsubFeatureList; * FeatureList GposFeatureList; * * * if (GsubTable.IsNotPresent && GposTable.IsNotPresent) * { * return OpenTypeLayoutResult.ScriptNotFound; * } * * if (GsubTable.IsPresent) * { * GsubScriptList = GsubHeader.GetScriptList(GsubTable); * GsubScript = GsubScriptList.FindScript(GsubTable,ScriptTag); * GsubLangSys = GsubScript.FindLangSys(GsubTable,LangSysTag); * GsubFeatureList = GsubHeader.GetFeatureList(GsubTable); * } * else * { * GsubScript = new ScriptTable(FontTable.InvalidOffset); * GsubLangSys = new LangSysTable(FontTable.InvalidOffset); * GsubFeatureList = new FeatureList(FontTable.InvalidOffset); * } * * if (GposTable.IsPresent) * { * GposScriptList = GposHeader.GetScriptList(GposTable); * GposScript = GposScriptList.FindScript(GposTable,ScriptTag); * GposLangSys = GposScript.FindLangSys(GposTable,LangSysTag); * GposFeatureList = GposHeader.GetFeatureList(GposTable); * } * else * { * GposScript = new ScriptTable(FontTable.InvalidOffset); * GposLangSys = new LangSysTable(FontTable.InvalidOffset); * GposFeatureList = new FeatureList(FontTable.InvalidOffset); * } * * if (GsubScript.IsNull && GposScript.IsNull) * { * return OpenTypeLayoutResult.ScriptNotFound; * } * * if (GsubLangSys.IsNull && GposLangSys.IsNull) * { * return OpenTypeLayoutResult.LangSysNotFound; * } * * if (!GsubLangSys.IsNull) * { * GsubFeatureCount = GsubLangSys.FeatureCount(GsubTable); * } * else * { * GsubFeatureCount = 0; * } * * if (!GposLangSys.IsNull) * { * GposFeatureCount = GposLangSys.FeatureCount(GposTable); * } * else * { * GposFeatureCount = 0; * } * * Features = new TagInfo[GsubFeatureCount]; * int CurrentFeatureIndex = 0; * * for(i=0; i<GsubFeatureCount; i++) * { * ushort FeatureIndex = GsubLangSys.GetFeatureIndex(GsubTable,i); * Features[CurrentFeatureIndex].Tag = GsubFeatureList.FeatureTag(GsubTable,FeatureIndex); * Features[CurrentFeatureIndex].TagFlags = TagInfoFlags.Substitution; ++CurrentFeatureIndex; * } * * Debug.Assert(CurrentFeatureIndex==Features.Length); * * //Check GPOS for tags that is not in GSUB * GposNewTags=0; * if (!GposLangSys.IsNull) * { * for(i=0;i<GposFeatureCount;i++) * { * ushort FeatureIndex = GposLangSys.GetFeatureIndex(GposTable,i); * uint GposTag = GposFeatureList.FeatureTag(GposTable,FeatureIndex); * if (TagInfo.IsNewTag(Features,GposTag)) * { ++GposNewTags; * } * } * } * * //append new tags to ScriptTags if any exists * if (GposNewTags>0) * { * //Allocate new array to fit all tags * TagInfo[] tmp = Features; * Features = new TagInfo[GsubFeatureCount+GposNewTags]; * Array.Copy(tmp,0,Features,0,tmp.Length); * * for(i=0;i<GposFeatureCount;i++) * { * ushort FeatureIndex = GposLangSys.GetFeatureIndex(GposTable,i); * uint GposTag = GposFeatureList.FeatureTag(GposTable,FeatureIndex); * * if (TagInfo.IsNewTag(Features,GposTag)) * { * Features[CurrentFeatureIndex].Tag = GposTag; * Features[CurrentFeatureIndex].TagFlags = TagInfoFlags.Positioning; ++CurrentFeatureIndex; * } * else * { * int Index = TagInfo.GetTagIndex(Features,GposTag); * Features[Index].TagFlags |= TagInfoFlags.Positioning; * } * } * * Debug.Assert(CurrentFeatureIndex==Features.Length); * } * * * } * catch (FileFormatException) * { * return OpenTypeLayoutResult.BadFontTable; * } * * return OpenTypeLayoutResult.Success; * } */ /// <summary> /// Substitutes glyphs according to features defined in the font. /// </summary> /// <param name="Font">In: Font access interface</param> /// <param name="workspace">In: Workspace for layout engine</param> /// <param name="ScriptTag">In: Script tag</param> /// <param name="LangSysTag">In: LangSys tag</param> /// <param name="FeatureSet">In: List of features to apply</param> /// <param name="featureCount">In: Actual number of features in <paramref name="FeatureSet"/></param> /// <param name="featureSetOffset">In: offset of input characters inside FeatureSet</param> /// <param name="CharCount">In: Characters count (i.e. <paramref name="Charmap"/>.Length);</param> /// <param name="Charmap">In/out: Char to glyph mapping</param> /// <param name="Glyphs">In/out: List of GlyphInfo structs</param> /// <returns>Substitution result</returns> 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); }
/// <summary> /// Align to anchors between two glyphs (e.g. mark and base) /// by changing adv.width and offsets for both of them /// </summary> /// <param name="Font"></param> /// <param name="Table"></param> /// <param name="Metrics"></param> /// <param name="GlyphInfo"></param> /// <param name="Advances"></param> /// <param name="Offsets"></param> /// <param name="StaticGlyph"></param> /// <param name="MobileGlyph"></param> /// <param name="StaticAnchor"></param> /// <param name="MobileAnchor"></param> /// <param name="UseAdvances"></param> /// <returns></returns> public static unsafe void AlignAnchors( IOpenTypeFont Font, FontTable Table, LayoutMetrics Metrics, GlyphInfoList GlyphInfo, int* Advances, LayoutOffset* Offsets, int StaticGlyph, int MobileGlyph, AnchorTable StaticAnchor, AnchorTable MobileAnchor, bool UseAdvances ) { Invariant.Assert(StaticGlyph>=0 && StaticGlyph<GlyphInfo.Length); Invariant.Assert(MobileGlyph>=0 && MobileGlyph<GlyphInfo.Length); Invariant.Assert(!StaticAnchor.IsNull()); Invariant.Assert(!MobileAnchor.IsNull()); LayoutOffset ContourPoint = new LayoutOffset(); if (StaticAnchor.NeedContourPoint(Table)) { ContourPoint = Font.GetGlyphPointCoord(GlyphInfo.Glyphs[MobileGlyph], StaticAnchor.ContourPointIndex(Table)); } LayoutOffset StaticAnchorPoint = StaticAnchor.AnchorCoordinates(Table,Metrics,ContourPoint); if (MobileAnchor.NeedContourPoint(Table)) { ContourPoint = Font.GetGlyphPointCoord(GlyphInfo.Glyphs[MobileGlyph], MobileAnchor.ContourPointIndex(Table)); } LayoutOffset MobileAnchorPoint = MobileAnchor.AnchorCoordinates(Table,Metrics,ContourPoint); int AdvanceInBetween=0; if (StaticGlyph<MobileGlyph) for(int i=StaticGlyph+1;i<MobileGlyph;i++) AdvanceInBetween+=Advances[i]; else for(int i=MobileGlyph+1;i<StaticGlyph;i++) AdvanceInBetween+=Advances[i]; if (Metrics.Direction==TextFlowDirection.LTR || Metrics.Direction==TextFlowDirection.RTL) { Offsets[MobileGlyph].dy = Offsets[StaticGlyph].dy + StaticAnchorPoint.dy - MobileAnchorPoint.dy; if ((Metrics.Direction==TextFlowDirection.LTR)==(StaticGlyph<MobileGlyph)) { // static glyph is on the left(phisically, not logically) to the mobile glyph int dx = Offsets[StaticGlyph].dx - Advances[StaticGlyph] + StaticAnchorPoint.dx - AdvanceInBetween - MobileAnchorPoint.dx; if (UseAdvances) { Advances[StaticGlyph] += dx; } else { Offsets[MobileGlyph].dx = dx; } } else { // static glyph is on the right(phisically, not logically) to the mobile glyph int dx = Offsets[StaticGlyph].dx + Advances[MobileGlyph] + StaticAnchorPoint.dx + AdvanceInBetween - MobileAnchorPoint.dx; if (UseAdvances) { Advances[MobileGlyph] -= dx; } else { Offsets[MobileGlyph].dx = dx; } } } else { //TTB, BTT: //Under construction } }
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; }
///<summary> /// Internal method to test layout tables if they are uitable for fast path. /// Returns list of script-langauge pairs that are not optimizable. ///</summary> internal static OpenTypeLayoutResult GetComplexLanguageList( IOpenTypeFont Font, //In: Font access interface uint[] featureList, //In: Feature to look in uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId, out WritingSystem[] complexLanguages // Out: List of script/langauge pair // that are not optimizable ) { try { WritingSystem[] gsubComplexLanguages = null; WritingSystem[] gposComplexLanguages = null; int gsubComplexLanguagesCount = 0; int gposComplexLanguagesCount = 0; FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (GsubTable.IsPresent) { LayoutEngine.GetComplexLanguageList( OpenTypeTags.GSUB, GsubTable, featureList, glyphBits, minGlyphId, maxGlyphId, out gsubComplexLanguages, out gsubComplexLanguagesCount ); } if (GposTable.IsPresent) { LayoutEngine.GetComplexLanguageList( OpenTypeTags.GPOS, GposTable, featureList, glyphBits, minGlyphId, maxGlyphId, out gposComplexLanguages, out gposComplexLanguagesCount ); } if (gsubComplexLanguages == null && gposComplexLanguages == null) { complexLanguages = null; return(OpenTypeLayoutResult.Success); } // Both tables have complex scrips, merge results // Count gpos unique Languages // and pack them at the same time // so we do not research them again. int gposNewLanguages = 0, i, j; for (i = 0; i < gposComplexLanguagesCount; i++) { bool foundInGsub = false; for (j = 0; j < gsubComplexLanguagesCount; j++) { if (gsubComplexLanguages[j].scriptTag == gposComplexLanguages[i].scriptTag && gsubComplexLanguages[j].langSysTag == gposComplexLanguages[i].langSysTag ) { foundInGsub = true; break; } ; } if (!foundInGsub) { if (gposNewLanguages < i) { gposComplexLanguages[gposNewLanguages] = gposComplexLanguages[i]; } gposNewLanguages++; } } //realloc array for merged results, merge both arrays complexLanguages = new WritingSystem[gsubComplexLanguagesCount + gposNewLanguages]; for (i = 0; i < gsubComplexLanguagesCount; i++) { complexLanguages[i] = gsubComplexLanguages[i]; } for (i = 0; i < gposNewLanguages; i++) { complexLanguages[gsubComplexLanguagesCount + i] = gposComplexLanguages[i]; } return(OpenTypeLayoutResult.Success); } catch (FileFormatException) { complexLanguages = null; return(OpenTypeLayoutResult.BadFontTable); } }
public unsafe bool Apply( IOpenTypeFont Font, FontTable Table, LayoutMetrics Metrics, // LayoutMetrics GlyphInfoList GlyphInfo, // List of GlyphInfo structs ushort LookupFlags, // Lookup flags for glyph lookups int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets int FirstGlyph, // where to apply lookup int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph>=0); Invariant.Assert(AfterLastGlyph<=GlyphInfo.Length); NextGlyph = FirstGlyph+1; //Always move to the next glyph, whether matched or not int glyphCount=GlyphInfo.Length; ushort firstGlyphId = GlyphInfo.Glyphs[FirstGlyph]; int secondGlyph = LayoutEngine.GetNextGlyphInLookup(Font,GlyphInfo,FirstGlyph+1,LookupFlags,LayoutEngine.LookForward); if (secondGlyph>=AfterLastGlyph) return false; ushort secondGlyphId = GlyphInfo.Glyphs[secondGlyph]; ValueRecordTable firstValueRecord, secondValueRecord; switch (Format(Table)) { case 1: { int coverageIndex = Coverage(Table).GetGlyphIndex(Table,firstGlyphId); if (coverageIndex==-1) return false; PairSetTable pairSet = Format1PairSet(Table,(ushort)coverageIndex); int pairValueIndex = pairSet.FindPairValue(Table, secondGlyphId); if (pairValueIndex == -1) return false; firstValueRecord = pairSet.FirstValueRecord(Table,(ushort)pairValueIndex,FirstValueFormat(Table)); secondValueRecord = pairSet.SecondValueRecord(Table,(ushort)pairValueIndex,SecondValueFormat(Table)); break; } case 2: { int coverageIndex = Coverage(Table).GetGlyphIndex(Table,firstGlyphId); if (coverageIndex == -1) return false; ushort firstClassIndex = Format2Class1Table(Table).GetClass(Table,firstGlyphId); if (firstClassIndex >= Format2Class1Count(Table)) return false; //this is invalid font; ushort secondClassIndex = Format2Class2Table(Table).GetClass(Table,secondGlyphId); if (secondClassIndex >= Format2Class2Count(Table)) return false; //this is invalid font; ushort class2Count = Format2Class2Count(Table); firstValueRecord = Format2FirstValueRecord(Table, class2Count, firstClassIndex, secondClassIndex ); secondValueRecord = Format2SecondValueRecord(Table, class2Count, firstClassIndex, secondClassIndex ); break; } default: return false; } //Now adjust positions firstValueRecord.AdjustPos (Table, Metrics, ref Offsets[FirstGlyph], ref Advances[FirstGlyph]); secondValueRecord.AdjustPos(Table, Metrics, ref Offsets[secondGlyph], ref Advances[secondGlyph]); return true; }
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 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; } }
/// <summary> /// Position glyphs according to features defined in the font. /// </summary> /// <param name="Font">In: Font access interface</param> /// <param name="workspace">In: Workspace for layout engine</param> /// <param name="ScriptTag">In: Script tag</param> /// <param name="LangSysTag">In: LangSys tag</param> /// <param name="Metrics">In: LayoutMetrics</param> /// <param name="FeatureSet">In: List of features to apply</param> /// <param name="featureCount">In: Actual number of features in <paramref name="FeatureSet"/></param> /// <param name="featureSetOffset">In: offset of input characters inside FeatureSet</param> /// <param name="CharCount">In: Characters count (i.e. <paramref name="Charmap"/>.Length);</param> /// <param name="Charmap">In: Char to glyph mapping</param> /// <param name="Glyphs">In/out: List of GlyphInfo structs</param> /// <param name="Advances">In/out: Glyphs adv.widths</param> /// <param name="Offsets">In/out: Glyph offsets</param> /// <returns>Substitution result</returns> internal static OpenTypeLayoutResult PositionGlyphs( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, uint ScriptTag, uint LangSysTag, LayoutMetrics Metrics, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList Glyphs, int *Advances, LayoutOffset *Offsets ) { try { FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (!GposTable.IsPresent) { return(OpenTypeLayoutResult.ScriptNotFound); } GPOSHeader GposHeader = new GPOSHeader(0); ScriptList ScriptList = GposHeader.GetScriptList(GposTable); ScriptTable Script = ScriptList.FindScript(GposTable, ScriptTag); if (Script.IsNull) { return(OpenTypeLayoutResult.ScriptNotFound); } LangSysTable LangSys = Script.FindLangSys(GposTable, LangSysTag); if (LangSys.IsNull) { return(OpenTypeLayoutResult.LangSysNotFound); } FeatureList FeatureList = GposHeader.GetFeatureList(GposTable); LookupList LookupList = GposHeader.GetLookupList(GposTable); LayoutEngine.ApplyFeatures( Font, workspace, OpenTypeTags.GPOS, GposTable, Metrics, LangSys, FeatureList, LookupList, FeatureSet, featureCount, featureSetOffset, CharCount, Charmap, Glyphs, Advances, Offsets ); } catch (FileFormatException) { return(OpenTypeLayoutResult.BadFontTable); } return(OpenTypeLayoutResult.Success); }
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; }
public unsafe bool Apply( IOpenTypeFont Font, FontTable Table, int CharCount, UshortList Charmap, // Character to glyph map GlyphInfoList GlyphInfo, // List of GlyphInfo ushort LookupFlags, // Lookup flags for glyph lookups int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use out int NextGlyph // Next glyph to process ) { NextGlyph = FirstGlyph + 1; // in case we don't match if (Format(Table) != 1) return false; //unknown format int oldGlyphCount=GlyphInfo.Length; ushort glyphId = GlyphInfo.Glyphs[FirstGlyph]; int coverageIndex = Coverage(Table).GetGlyphIndex(Table,glyphId); if (coverageIndex==-1) return false; MultipleSubstitutionSequenceTable sequence = Sequence(Table,coverageIndex); ushort sequenceLength = sequence.GlyphCount(Table); int lengthDelta = sequenceLength - 1; if (sequenceLength==0) { // This is illegal, because mapping will be broken - // corresponding char will be lost. Just leave it as it is. // (char will be attached to the following glyph). GlyphInfo.Remove(FirstGlyph,1); } else { ushort firstChar = GlyphInfo.FirstChars[FirstGlyph]; ushort ligatureCount = GlyphInfo.LigatureCounts[FirstGlyph]; if (lengthDelta > 0) { GlyphInfo.Insert(FirstGlyph,lengthDelta); } //put glyphs in place for(ushort gl=0; gl<sequenceLength; gl++) { GlyphInfo.Glyphs[FirstGlyph + gl] = sequence.Glyph(Table,gl); GlyphInfo.GlyphFlags[FirstGlyph + gl] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); GlyphInfo.FirstChars[FirstGlyph + gl] = firstChar; GlyphInfo.LigatureCounts[FirstGlyph + gl] = ligatureCount; } } //Fix char mapping - very simple for now. // Works only for arabic base+mark -> base and marks decomposition // for(int ch=0;ch<CharCount;ch++) { if (Charmap[ch]>FirstGlyph) Charmap[ch] = (ushort)(Charmap[ch]+lengthDelta); } NextGlyph = FirstGlyph + lengthDelta + 1; return true; }
internal static OpenTypeLayoutResult PositionGlyphs( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, uint ScriptTag, uint LangSysTag, LayoutMetrics Metrics, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList Glyphs, int* Advances, LayoutOffset* Offsets ) { try { FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (!GposTable.IsPresent) {return OpenTypeLayoutResult.ScriptNotFound;} GPOSHeader GposHeader = new GPOSHeader(0); ScriptList ScriptList = GposHeader.GetScriptList(GposTable); ScriptTable Script = ScriptList.FindScript(GposTable,ScriptTag); if (Script.IsNull) {return OpenTypeLayoutResult.ScriptNotFound;} LangSysTable LangSys = Script.FindLangSys(GposTable,LangSysTag); if (LangSys.IsNull) {return OpenTypeLayoutResult.LangSysNotFound;} FeatureList FeatureList = GposHeader.GetFeatureList(GposTable); LookupList LookupList = GposHeader.GetLookupList(GposTable); LayoutEngine.ApplyFeatures( Font, workspace, OpenTypeTags.GPOS, GposTable, Metrics, LangSys, FeatureList, LookupList, FeatureSet, featureCount, featureSetOffset, CharCount, Charmap, Glyphs, Advances, Offsets ); } catch (FileFormatException) { return OpenTypeLayoutResult.BadFontTable; } return OpenTypeLayoutResult.Success; }