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 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, // 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 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; } }
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; }
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 LayoutOffset AnchorCoordinates( FontTable Table, LayoutMetrics Metrics, LayoutOffset ContourPoint ) { LayoutOffset Point = new LayoutOffset(); switch (format) { case 1: //Simple coordinates Point.dx = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth,XCoordinate(Table)); Point.dy = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmHeight,YCoordinate(Table)); break; case 2: //Coordinates + anchor point if (ContourPoint.dx==int.MinValue) { Point.dx = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth,XCoordinate(Table)); Point.dy = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmHeight,YCoordinate(Table)); } else { Point.dx = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth,ContourPoint.dx); Point.dy = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth,ContourPoint.dy); } break; case 3: //Coordinates + Device table Point.dx = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth,XCoordinate(Table))+ Format3XDeviceTable(Table).Value(Table,Metrics.PixelsEmWidth); Point.dy = Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmHeight,YCoordinate(Table))+ Format3YDeviceTable(Table).Value(Table,Metrics.PixelsEmHeight); break; default: //Unknown format Point.dx = 0; Point.dx = 0; break; } return Point; }
public unsafe bool Apply( FontTable Table, LayoutMetrics Metrics, // LayoutMetrics GlyphInfoList GlyphInfo, // List of GlyphInfo structs 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; //In case we don't match; int glyphCount=GlyphInfo.Length; ushort glyphId = GlyphInfo.Glyphs[FirstGlyph]; int coverageIndex = Coverage(Table).GetGlyphIndex(Table,glyphId); if (coverageIndex == -1) return false; ValueRecordTable valueRecord; switch (Format(Table)) { case 1: valueRecord = Format1ValueRecord(Table); break; case 2: valueRecord = Format2ValueRecord(Table,(ushort)coverageIndex); break; default: return false; } valueRecord.AdjustPos(Table, Metrics, ref Offsets[FirstGlyph], ref Advances[FirstGlyph]); return true; }
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; }
public void AdjustPos( FontTable Table, LayoutMetrics Metrics, ref LayoutOffset GlyphOffset, ref int GlyphAdvance ) { int curOffset=offset; if ((format&XPlacmentFlag)!=0) { GlyphOffset.dx += Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth, Table.GetShort(curOffset)); curOffset+=2; } if ((format&YPlacmentFlag)!=0) { GlyphOffset.dy += Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmHeight, Table.GetShort(curOffset)); curOffset+=2; } if ((format&XAdvanceFlag)!=0) { GlyphAdvance += Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmWidth, Table.GetShort(curOffset)); curOffset+=2; } if ((format&YAdvanceFlag)!=0) { GlyphAdvance += Positioning.DesignToPixels(Metrics.DesignEmHeight,Metrics.PixelsEmHeight, Table.GetShort(curOffset)); curOffset+=2; } if ((format&XPlacementDeviceFlag)!=0) { int deviceTableOffset = Table.GetOffset(curOffset); if (deviceTableOffset != FontTable.NullOffset) { DeviceTable deviceTable = new DeviceTable(baseTableOffset+deviceTableOffset); GlyphOffset.dx += deviceTable.Value(Table,Metrics.PixelsEmWidth); } curOffset+=2; } if ((format&YPlacementDeviceFlag)!=0) { int deviceTableOffset = Table.GetOffset(curOffset); if (deviceTableOffset != FontTable.NullOffset) { DeviceTable deviceTable = new DeviceTable(baseTableOffset+deviceTableOffset); GlyphOffset.dy += deviceTable.Value(Table,Metrics.PixelsEmHeight); } curOffset+=2; } if ((format&XAdvanceDeviceFlag)!=0) { if (Metrics.Direction==TextFlowDirection.LTR || Metrics.Direction==TextFlowDirection.RTL) { int deviceTableOffset = Table.GetOffset(curOffset); if (deviceTableOffset != FontTable.NullOffset) { DeviceTable deviceTable = new DeviceTable(baseTableOffset+deviceTableOffset); GlyphAdvance += deviceTable.Value(Table,Metrics.PixelsEmWidth); } } curOffset+=2; } if ((format&YAdvanceDeviceFlag)!=0) { if (Metrics.Direction==TextFlowDirection.TTB || Metrics.Direction==TextFlowDirection.BTT) { int deviceTableOffset = Table.GetOffset(curOffset); if (deviceTableOffset != FontTable.NullOffset) { DeviceTable deviceTable = new DeviceTable(baseTableOffset+deviceTableOffset); GlyphAdvance += deviceTable.Value(Table,Metrics.PixelsEmHeight); } } curOffset+=2; } }
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, 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 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; }
/// <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 } }
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* 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; }
/// <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); }
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; }