internal GlyphInfoList(int capacity, int leap, bool justify) { _glyphs = new UshortList(capacity, leap); _glyphFlags = new UshortList(capacity, leap); _firstChars = new UshortList(capacity, leap); _ligatureCounts = new UshortList(capacity, leap); }
/// <summary> /// ShaperBuffers - constructor /// </summary> /// <SecurityNote> /// Critical: This code accepts checked pointers and extracts /// unsafe pointers. /// </SecurityNote> public ShaperBuffers(ushort charCount, ushort glyphCount) { // the charCount is used to provide an initial size for the // various buffers used by the shaper(s). These may // grow over the shaper's life _glyphInfoList = new GlyphInfoList((glyphCount > charCount ? glyphCount : charCount), 16, false); _charMap = new UshortList(charCount, 16); _layoutWorkspace = new OpenTypeLayoutWorkspace(); _charMap.SetRange(0, charCount); if (glyphCount > 0) { _glyphInfoList.SetRange(0, glyphCount); } }
/// <summary> /// ShaperBuffers - constructor /// </summary> /// <SecurityNote> /// Critical: This code accepts checked pointers and extracts /// unsafe pointers. /// </SecurityNote> public ShaperBuffers(ushort charCount, ushort glyphCount) { // the charCount is used to provide an initial size for the // various buffers used by the shaper(s). These may // grow over the shaper's life _glyphInfoList = new GlyphInfoList((glyphCount > charCount ? glyphCount : charCount), 16, false); _charMap = new UshortList(charCount, 16); _layoutWorkspace = new OpenTypeLayoutWorkspace(); _charMap.SetRange(0,charCount); if (glyphCount > 0) { _glyphInfoList.SetRange(0,glyphCount); } }
public unsafe void ApplyContextualLookups( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Char to glyph mapping GlyphInfoList GlyphInfo, // List of GlyphInfo structs int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets ushort LookupFlags, // Lookup table flags int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use uint Parameter, // lookup parameter int nestingLevel, // Contextual lookup nesting level out int nextGlyph // out: next glyph index ) { // Limit nesting level for contextual lookups to // prevent infinite loops from corrupt fonts if (nestingLevel >= MaximumContextualLookupNestingLevel) { nextGlyph = AfterLastGlyph; return; } LookupList lookupList; if (TableTag == OpenTypeTags.GSUB) { lookupList = (new GSUBHeader(0)).GetLookupList(Table); } else { lookupList = (new GPOSHeader(0)).GetLookupList(Table); } int prevLookupIndex = -1; int prevSequenceIndex = -1; while (true) { ushort lookupIndex = ushort.MaxValue; ushort sequenceIndex = ushort.MaxValue; for(ushort i = 0; i < recordCount; i++) { ushort recordLookupIndex = LookupIndex(Table,i); ushort recordSequenceIndex = SequenceIndex(Table,i); if (recordLookupIndex < prevLookupIndex || (recordLookupIndex == prevLookupIndex && recordSequenceIndex <= prevSequenceIndex ) ) { // This record we already should have been processed continue; } // Among not proccessed record, find next one if ( recordLookupIndex < lookupIndex || (recordLookupIndex == lookupIndex && recordSequenceIndex < sequenceIndex ) ) { lookupIndex = recordLookupIndex; sequenceIndex = recordSequenceIndex; } } if (lookupIndex == ushort.MaxValue) { // All records processed (or we had duplicate records, which we skipped automatically) break; } //remember for the next iteration prevLookupIndex = lookupIndex; prevSequenceIndex = sequenceIndex; // Now find actual glyph where to apply lookup (may depend on lookup flags) int recordFirstGlyph = FirstGlyph; for (int i = 0; i < sequenceIndex && recordFirstGlyph < AfterLastGlyph; i++) { recordFirstGlyph = LayoutEngine.GetNextGlyphInLookup(Font, GlyphInfo, recordFirstGlyph + 1, LookupFlags, LayoutEngine.LookForward ); } if (recordFirstGlyph >= AfterLastGlyph) { // Requested position is outside of input sequence, do nothing. continue; } // And finally apply lookup int prevLength = GlyphInfo.Length; int dummyNextGlyph; LayoutEngine.ApplyLookup( Font, TableTag, Table, Metrics, lookupList.Lookup(Table, lookupIndex), CharCount, Charmap, GlyphInfo, Advances, Offsets, recordFirstGlyph, AfterLastGlyph, Parameter, nestingLevel + 1, out dummyNextGlyph // we don't need it here ); //We need to adjust afterLastGlyph, in case non-single substitution happened AfterLastGlyph += GlyphInfo.Length - prevLength; } nextGlyph = AfterLastGlyph; }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Char to glyph mapping GlyphInfoList GlyphInfo, // List of GlyphInfo structs int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets ushort LookupFlags, // Lookup table flags int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use uint Parameter, // lookup parameter int nestingLevel, // Contextual lookup nesting level out int NextGlyph // out: next glyph index ) { Invariant.Assert(Format(Table)==1); NextGlyph = FirstGlyph + 1; //in case we don't match int glyphCount = GlyphInfo.Length; int glyphIndex = FirstGlyph; ushort glyphId = GlyphInfo.Glyphs[glyphIndex]; int coverageIndex = Coverage(Table).GetGlyphIndex(Table,glyphId); if (coverageIndex < 0) return false; SubRuleSet subRuleSet= RuleSet(Table, coverageIndex); ushort ruleCount = subRuleSet.RuleCount(Table); bool match = false; for(ushort i=0; !match && i<ruleCount; i++) { match = subRuleSet.Rule(Table,i).Apply(Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); } return match; }
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; }
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, 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; }
/// <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 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, 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); }
private static void GetNextEnabledGlyphRange( Feature[] FeatureSet, // In: List of features to apply int featureCount, // In: Actual nubmer of features in FeatureSet int featureSetOffset, // In: offset of input chars inside feature set FontTable Table, // Layout table (GSUB or GPOS) OpenTypeLayoutWorkspace workspace, // workspace with compiled feature set LangSysTable LangSys, // Language system FeatureList Features, // List of Features in layout table ushort lookupIndex, // List of lokups definitions in layout table int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Character to glyph mapping int StartChar, int StartGlyph, int GlyphRunLength, out int FirstChar, // First char in enabled range out int AfterLastChar, // next char after enabled range out int FirstGlyph, // First char in enabled range out int AfterLastGlyph, // next char after enabled range out uint Parameter // applied feature parameter ) { FirstChar = int.MaxValue; AfterLastChar = int.MaxValue; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; Parameter = 0; if (workspace.IsRequiredFeatureFlagSet(lookupIndex)) { FirstChar = StartChar; AfterLastChar = CharCount; FirstGlyph = StartGlyph; AfterLastGlyph = GlyphRunLength; return; } for(int feature=0; feature < featureCount; feature++) { if (!workspace.IsFeatureFlagSet(lookupIndex,feature)) { continue; } Feature featureDescription = FeatureSet[feature]; // Shift values from the feature by specified offset and // work with these values from here int featureStart = featureDescription.StartIndex - featureSetOffset; if (featureStart < 0) { featureStart = 0; } int featureAfterEnd = featureDescription.StartIndex + featureDescription.Length - featureSetOffset; if (featureAfterEnd > CharCount) { featureAfterEnd = CharCount; } //If feature is disabled there should not be any flag set Debug.Assert(featureDescription.Parameter != 0); if (featureAfterEnd <= StartChar) { continue; } if (featureStart < FirstChar || ( featureStart == FirstChar && featureAfterEnd >= AfterLastChar ) ) { FirstChar = featureStart; AfterLastChar = featureAfterEnd; Parameter = featureDescription.Parameter; continue; } } //No ranges found if (FirstChar == int.MaxValue) { FirstGlyph = GlyphRunLength; AfterLastGlyph = GlyphRunLength; } else { if (StartGlyph > Charmap[FirstChar]) FirstGlyph = StartGlyph; else FirstGlyph = Charmap[FirstChar]; if (AfterLastChar < CharCount) AfterLastGlyph = Charmap[AfterLastChar]; else AfterLastGlyph = GlyphRunLength; } }
public static void ApplyFeatures( IOpenTypeFont Font, OpenTypeLayoutWorkspace workspace, OpenTypeTags TableTag, FontTable Table, LayoutMetrics Metrics, LangSysTable LangSys, FeatureList Features, LookupList Lookups, Feature[] FeatureSet, int featureCount, int featureSetOffset, int CharCount, UshortList Charmap, GlyphInfoList GlyphInfo, int* Advances, LayoutOffset* Offsets ) { UpdateGlyphFlags(Font, GlyphInfo, 0, GlyphInfo.Length, false, GlyphFlags.NotChanged); // if client did not supply us with workspace // we will create our own (temporarily) if (workspace == null) { workspace = new OpenTypeLayoutWorkspace(); } ushort lookupCount=Lookups.LookupCount(Table); //Compile feature set CompileFeatureSet( FeatureSet, featureCount, featureSetOffset, CharCount, Table, LangSys, Features, lookupCount, workspace ); OpenTypeLayoutCache.InitCache(Font, TableTag, GlyphInfo, workspace); for(ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } int firstChar=0, afterLastChar=0, firstGlyph=0, afterLastGlyph=0; OpenTypeLayoutCache.FindNextLookup(workspace, GlyphInfo, lookupIndex, out lookupIndex, out firstGlyph); // We need to check this again, because FindNextLookup will change lookupIndex if (lookupIndex >= lookupCount) { break; } if (!workspace.IsAggregatedFlagSet(lookupIndex)) { continue; } LookupTable lookup = Lookups.Lookup(Table, lookupIndex); uint parameter=0; bool isLookupReversal = IsLookupReversal(TableTag, lookup.LookupType()); while(firstGlyph < GlyphInfo.Length) // While we have ranges to work on { if (!OpenTypeLayoutCache.FindNextGlyphInLookup(workspace, lookupIndex, isLookupReversal, ref firstGlyph, ref afterLastGlyph)) { firstGlyph = afterLastGlyph; } if (firstGlyph < afterLastGlyph) // Apply lookup while in one range { int nextGlyph; int oldLength = GlyphInfo.Length; int glyphsAfterLastChar = oldLength - afterLastGlyph; bool match = ApplyLookup( Font, // In: Font access interface TableTag, // Layout table tag (GSUB or GPOS) Table, // Layout table (GSUB or GPOS) Metrics, // In: LayoutMetrics lookup, // Lookup definition structure CharCount, Charmap, // In: Char to glyph mapping GlyphInfo, // In/out: List of GlyphInfo structs Advances, // In/out: Glyph adv.widths Offsets, // In/out: Glyph offsets firstGlyph, // where to apply it afterLastGlyph, // how long is a context we can use parameter, // lookup parameter 0, // Nesting level for contextual lookups out nextGlyph // out: next glyph index // !!!: for reversal lookup, should // return new afterLastGlyph ); if (match) { //Adjust range end if length changed, // for reversal changes happens beyond afterLast, no change needed if (!isLookupReversal) { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, firstGlyph, nextGlyph); afterLastGlyph = GlyphInfo.Length - glyphsAfterLastChar; firstGlyph = nextGlyph; } else { OpenTypeLayoutCache.OnGlyphsChanged(workspace, GlyphInfo, oldLength, nextGlyph, afterLastGlyph); afterLastGlyph = nextGlyph; } } else { if (isLookupReversal) afterLastGlyph = nextGlyph; else firstGlyph = nextGlyph; } } else // End of range. Get next { GetNextEnabledGlyphRange( FeatureSet, featureCount, featureSetOffset, Table, workspace, LangSys, Features, lookupIndex, CharCount, Charmap, afterLastChar, afterLastGlyph, GlyphInfo.Length, out firstChar, out afterLastChar, out firstGlyph, out afterLastGlyph, out parameter); } } } }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics 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; }
// Find base ligature and component corresponding to the mark private unsafe void FindBaseLigature ( int CharCount, UshortList Charmap, GlyphInfoList GlyphInfo, int markGlyph, out ushort component, out int ligatureGlyph ) { int ligatureChar = 0; ligatureGlyph = -1; component = 0; bool FoundBase = false; for (int ch = GlyphInfo.FirstChars[markGlyph]; ch >= 0 && !FoundBase; ch--) { ushort glyph = Charmap[ch]; if ((GlyphInfo.GlyphFlags[glyph] & (ushort)GlyphFlags.GlyphTypeMask) != (ushort)GlyphFlags.Mark) { ligatureChar = ch; ligatureGlyph = glyph; FoundBase = true; } } if (!FoundBase) return; ushort comp = 0; for(ushort ch = GlyphInfo.FirstChars[ligatureGlyph]; ch<CharCount; ch++) { if (ch == ligatureChar) break; if (Charmap[ch]==ligatureGlyph) comp++; } component = comp; }
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; }
/* 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); }
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, 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; }
public unsafe bool Apply( IOpenTypeFont Font, // Font access interface OpenTypeTags TableTag, // Layout table tag (GSUB or GPOS) FontTable Table, // Layout table (GSUB or GPOS) LayoutMetrics Metrics, // LayoutMetrics int CharCount, // Characters count (i.e. Charmap.Length); UshortList Charmap, // Char to glyph mapping GlyphInfoList GlyphInfo, // List of GlyphInfo structs int* Advances, // Glyph adv.widths LayoutOffset* Offsets, // Glyph offsets ushort LookupFlags, // Lookup table flags int FirstGlyph, // where to apply it int AfterLastGlyph, // how long is a context we can use uint Parameter, // lookup parameter int nestingLevel, // Contextual lookup nesting level out int NextGlyph // out: next glyph index ) { switch (Format(Table)) { case 1: GlyphContextSubtable glyphContextSubtable = new GlyphContextSubtable(offset); return glyphContextSubtable.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); case 2: ClassContextSubtable classContextSubtable = new ClassContextSubtable(offset); return classContextSubtable.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); case 3: CoverageContextSubtable coverageContextSubtable = new CoverageContextSubtable(offset); return coverageContextSubtable.Apply( Font, TableTag, Table, Metrics, CharCount, Charmap, GlyphInfo, Advances, Offsets, LookupFlags, FirstGlyph, AfterLastGlyph, Parameter, nestingLevel, out NextGlyph ); default: //Unknown format NextGlyph = FirstGlyph+1; //don't match return false; } }
public 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; }
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; }