public bool Apply( FontTable Table, GlyphInfoList GlyphInfo, // List of GlyphInfo structs int FirstGlyph, // where to apply it out int NextGlyph // Next glyph to process ) { Invariant.Assert(FirstGlyph >= 0); NextGlyph = FirstGlyph + 1; //In case we don't match; ushort GlyphId = GlyphInfo.Glyphs[FirstGlyph]; int CoverageIndex = Coverage(Table).GetGlyphIndex(Table,GlyphId); if (CoverageIndex == -1) return false; switch(Format(Table)) { case 1: GlyphInfo.Glyphs[FirstGlyph] = (ushort)(GlyphId + Format1DeltaGlyphId(Table)); GlyphInfo.GlyphFlags[FirstGlyph] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); NextGlyph = FirstGlyph + 1; return true; case 2: GlyphInfo.Glyphs[FirstGlyph] = Format2SubstituteGlyphId(Table,(ushort)CoverageIndex); GlyphInfo.GlyphFlags[FirstGlyph] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); NextGlyph = FirstGlyph + 1; return true; default: NextGlyph = FirstGlyph+1; return false; } }
public unsafe bool Apply( FontTable Table, GlyphInfoList GlyphInfo, // List of GlyphInfo uint FeatureParam, // For this lookup - index of glyph alternate int FirstGlyph, // where to apply it out int NextGlyph // Next glyph to process ) { NextGlyph = FirstGlyph + 1; // always move one glyph forward, // doesn't matter whether we matched context if (Format(Table) != 1) return false; //Unknown format int oldGlyphCount=GlyphInfo.Length; int coverageIndex = Coverage(Table). GetGlyphIndex(Table,GlyphInfo.Glyphs[FirstGlyph]); if (coverageIndex==-1) return false; AlternateSetTable alternateSet = AlternateSet(Table,coverageIndex); ushort alternateGlyph = alternateSet.Alternate(Table, FeatureParam); if (alternateGlyph != InvalidAlternateGlyph) { GlyphInfo.Glyphs[FirstGlyph] = alternateGlyph; GlyphInfo.GlyphFlags[FirstGlyph] = (ushort)(GlyphFlags.Unresolved | GlyphFlags.Substituted); return true; } return false; }
// Not used. This value should be equal to glyph count in Coverage. // Keeping it for future reference //private ushort AlternateSetCount(FontTable Table) //{ // return Table.GetUShort(offset + offsetAlternateSetCount); //} private AlternateSetTable AlternateSet(FontTable Table, int index) { return(new AlternateSetTable(offset + Table.GetUShort(offset + offsetAlternateSets + index * sizeAlternateSetOffset) )); }
// Not used. This value should be equal to glyph count in Coverage. // Keeping it for future reference //private ushort SequenceCount(FontTable Table) //{ // return Table.GetUShort(offset + offsetSequenceCount); //} private MultipleSubstitutionSequenceTable Sequence(FontTable Table, int Index) { return(new MultipleSubstitutionSequenceTable( offset + Table.GetUShort(offset + offsetSequenceArray + Index * sizeSequenceOffset) )); }
public bool IsLookupCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { return(Coverage(table).IsAnyGlyphCovered(table, glyphBits, minGlyphId, maxGlyphId )); }
public bool IsLookupCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { if (!Coverage(table).IsAnyGlyphCovered(table, glyphBits, minGlyphId, maxGlyphId ) ) { return(false); } ushort ligatureSetCount = LigatureSetCount(table); for (ushort setIndex = 0; setIndex < ligatureSetCount; setIndex++) { LigatureSetTable ligatureSet = LigatureSet(table, setIndex); ushort ligaCount = ligatureSet.LigatureCount(table); for (ushort liga = 0; liga < ligaCount; liga++) { LigatureTable ligature = ligatureSet.Ligature(table, liga); ushort compCount = ligature.ComponentCount(table); bool ligatureIsComplex = true; for (ushort compIndex = 1; compIndex < compCount; compIndex++) { ushort glyphId = ligature.Component(table, compIndex); if (glyphId > maxGlyphId || glyphId < minGlyphId || (glyphBits[glyphId >> 5] & (1 << (glyphId % 32))) == 0 ) { ligatureIsComplex = false; break; } } if (ligatureIsComplex) { return(true); } } } return(false); }
private static bool AppendCoverageGlyphRecords( FontTable table, ushort lookupIndex, CoverageTable coverage, GlyphLookupRecord[] records, ref int recordCount, ref int maxLookupGlyph ) { switch (coverage.Format(table)) { case 1: ushort glyphCount = coverage.Format1GlyphCount(table); for (ushort i = 0; i < glyphCount; i++) { ushort glyph = coverage.Format1Glyph(table, i); if (!AppendGlyphRecord(glyph, lookupIndex, records, ref recordCount, ref maxLookupGlyph)) { // We've failed to add another record. return(false); } } break; case 2: ushort rangeCount = coverage.Format2RangeCount(table); for (ushort i = 0; i < rangeCount; i++) { ushort firstGlyph = coverage.Format2RangeStartGlyph(table, i); ushort lastGlyph = coverage.Format2RangeEndGlyph(table, i); for (int glyph = firstGlyph; glyph <= lastGlyph; glyph++) { if (!AppendGlyphRecord((ushort)glyph, lookupIndex, records, ref recordCount, ref maxLookupGlyph)) { // We've failed to add another record. return(false); } } } break; } return(true); }
public ushort Alternate(FontTable Table, uint FeatureParam) { Invariant.Assert(FeatureParam > 0); // Parameter 0 means feautre is disabled. //Should be filtered out in GetNextEnabledGlyphRange // Off by one - alternate number 1 is stored under index 0 uint index = FeatureParam - 1; if (index >= GlyphCount(Table)) { return(AlternateSubstitutionSubtable.InvalidAlternateGlyph); } return(Table.GetUShort(offset + offsetGlyphs + (ushort)index * sizeGlyph)); }
/// <summary> /// /// </summary> /// <param name="Font">Font</param> /// <param name="ScriptTag">Script to search in</param> /// <param name="LangSysTag">LangGys to search for</param> /// <returns>TagInfoFlags, if script not present == None</returns> internal static TagInfoFlags FindLangSys( IOpenTypeFont Font, uint ScriptTag, uint LangSysTag ) { TagInfoFlags flags = TagInfoFlags.None; try { FontTable gsubTable = Font.GetFontTable(OpenTypeTags.GSUB); if (gsubTable.IsPresent) { GSUBHeader gsubHeader = new GSUBHeader(0); ScriptTable gsubScript = gsubHeader.GetScriptList(gsubTable).FindScript(gsubTable, ScriptTag); if (!gsubScript.IsNull && !gsubScript.FindLangSys(gsubTable, LangSysTag).IsNull) { flags |= TagInfoFlags.Substitution; } } } catch (FileFormatException) { return(TagInfoFlags.None); } try { FontTable gposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (gposTable.IsPresent) { GPOSHeader gposHeader = new GPOSHeader(0); ScriptTable gposScript = gposHeader.GetScriptList(gposTable).FindScript(gposTable, ScriptTag); if (!gposScript.IsNull && !gposScript.FindLangSys(gposTable, LangSysTag).IsNull) { flags |= TagInfoFlags.Positioning; } } } catch (FileFormatException) { return(TagInfoFlags.None); } return(flags); }
public ushort ComponentCount(FontTable Table) { return Table.GetUShort(offset + offsetComponentCount); }
private LigatureSetTable LigatureSet(FontTable Table, ushort Index) { return new LigatureSetTable(offset+Table.GetUShort(offset+ offsetLigatureSetArray + Index * sizeLigatureSet)); }
public LigatureTable Ligature(FontTable Table, ushort Index) { return new LigatureTable(offset + Table.GetUShort(offset + offsetLigatureArray + Index * sizeLigatureOffset)); }
public ushort ComponentCount(FontTable Table) { return(Table.GetUShort(offset + offsetComponentCount)); }
/// <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); }
// Not used. This value should be equal to glyph count in Coverage. // Keeping it for future reference //private ushort SequenceCount(FontTable Table) //{ // return Table.GetUShort(offset + offsetSequenceCount); //} private MultipleSubstitutionSequenceTable Sequence(FontTable Table, int Index) { return new MultipleSubstitutionSequenceTable( offset + Table.GetUShort(offset + offsetSequenceArray + Index * sizeSequenceOffset) ); }
// Not used. This value should be equal to glyph count in Coverage. // Keeping it for future reference //private ushort Foramt2GlyphCount(FontTable Table) //{ // Debug.Assert(Format(Table)==2); // return Table.GetUShort(offset + offsetFormat2GlyphCount); //} private ushort Format2SubstituteGlyphId(FontTable Table,ushort Index) { Invariant.Assert(Format(Table)==2); return Table.GetUShort(offset + offsetFormat2SubstitutehArray + Index * sizeFormat2SubstituteSize); }
private CoverageTable Coverage(FontTable Table) { return(new CoverageTable(offset + Table.GetUShort(offset + offsetCoverage))); }
public LigatureTable Ligature(FontTable Table, ushort Index) { return(new LigatureTable(offset + Table.GetUShort(offset + offsetLigatureArray + Index * sizeLigatureOffset))); }
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 ushort Format(FontTable Table) { return(Table.GetUShort(offset + offsetFormat)); }
// Not used. This value should be equal to glyph count in Coverage. // Keeping it for future reference //private ushort Foramt2GlyphCount(FontTable Table) //{ // Debug.Assert(Format(Table)==2); // return Table.GetUShort(offset + offsetFormat2GlyphCount); //} private ushort Format2SubstituteGlyphId(FontTable Table, ushort Index) { Invariant.Assert(Format(Table) == 2); return(Table.GetUShort(offset + offsetFormat2SubstitutehArray + Index * sizeFormat2SubstituteSize)); }
public ushort LigatureGlyph(FontTable Table) { return(Table.GetUShort(offset + offsetLigatureGlyph)); }
public ushort Glyph(FontTable Table, ushort index) { return(Table.GetUShort(offset + offsetGlyphArray + index * sizeGlyphId)); }
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 ushort GlyphCount(FontTable Table) { return(Table.GetUShort(offset + offsetGlyphCount)); }
public ushort Glyph(FontTable Table, ushort index) { return Table.GetUShort(offset + offsetGlyphArray + index * sizeGlyphId); }
public ushort Format(FontTable Table) { return Table.GetUShort(offset + offsetFormat); }
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 ushort GlyphCount(FontTable Table) { return Table.GetUShort(offset + offsetGlyphCount); }
private short Format1DeltaGlyphId(FontTable Table) { Invariant.Assert(Format(Table) == 1); return(Table.GetShort(offset + offsetFormat1DeltaGlyphId)); }
public bool IsLookupCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { return Coverage(table).IsAnyGlyphCovered(table, glyphBits, minGlyphId, maxGlyphId ); }
public CoverageTable GetPrimaryCoverage(FontTable table) { return(Coverage(table)); }
/* 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); }
public ushort LigatureCount(FontTable Table) { return(Table.GetUShort(offset + offsetLigatureCount)); }
private ushort LigatureSetCount(FontTable Table) { return Table.GetUShort(offset + offsetLigatureSetCount); }
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 ushort LigatureCount(FontTable Table) { return Table.GetUShort(offset + offsetLigatureCount); }
public bool IsLookupCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { return true; }
public ushort LigatureGlyph(FontTable Table) { return Table.GetUShort(offset + offsetLigatureGlyph); }
public CoverageTable BacktrackCoverage(FontTable Table, ushort Index) { return new CoverageTable(offset + Table.GetUShort(offset+ offsetBacktrackGlyphCount + sizeGlyphCount + Index*sizeCoverageOffset) ); }
public ushort Component(FontTable Table, ushort Index) { //LigaTable includes comps from 1 to N. So, (Index-1) return Table.GetUShort(offset + offsetComponentArray + (Index-1) * sizeComponent); }
public CoverageTable LookaheadCoverage(FontTable Table, ushort Index) { return new CoverageTable(offset + Table.GetUShort(offset+ offsetLookaheadGlyphCount + sizeGlyphCount + Index*sizeCoverageOffset) ); }
public bool IsLookupCovered( FontTable table, uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId) { if (!Coverage(table).IsAnyGlyphCovered(table, glyphBits, minGlyphId, maxGlyphId ) ) return false; ushort ligatureSetCount = LigatureSetCount(table); for(ushort setIndex = 0; setIndex < ligatureSetCount; setIndex++) { LigatureSetTable ligatureSet = LigatureSet(table, setIndex); ushort ligaCount = ligatureSet.LigatureCount(table); for (ushort liga = 0; liga < ligaCount; liga++) { LigatureTable ligature = ligatureSet.Ligature(table, liga); ushort compCount = ligature.ComponentCount(table); bool ligatureIsComplex = true; for(ushort compIndex = 1; compIndex < compCount; compIndex++) { ushort glyphId = ligature.Component(table,compIndex); if (glyphId > maxGlyphId || glyphId < minGlyphId || (glyphBits[glyphId >> 5] & (1 << (glyphId % 32))) == 0 ) { ligatureIsComplex = false; break; } } if (ligatureIsComplex) return true; } } return false; }
public ContextualLookupRecords ContextualLookups(FontTable Table) { int recordCountOffset = offset + offsetLookaheadGlyphCount + sizeGlyphCount + LookaheadGlyphCount(Table) * sizeCoverageOffset; return new ContextualLookupRecords(recordCountOffset+sizeGlyphCount, Table.GetUShort(recordCountOffset)); }
private short Format1DeltaGlyphId(FontTable Table) { Invariant.Assert(Format(Table)==1); return Table.GetShort(offset + offsetFormat1DeltaGlyphId); }
public CoverageChainingSubtable(FontTable Table, int Offset) { offset = Offset; offsetInputGlyphCount = offsetBacktrackGlyphCount + sizeGlyphCount + Table.GetUShort(offset+offsetBacktrackGlyphCount) * sizeCoverageOffset; offsetLookaheadGlyphCount = offsetInputGlyphCount + sizeGlyphCount + Table.GetUShort(offset+offsetInputGlyphCount) * sizeCoverageOffset; }
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; }
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; }
private CoverageTable Coverage(FontTable Table) { return new CoverageTable(offset + Table.GetUShort(offset + offsetCoverage)); }
private LigatureSetTable LigatureSet(FontTable Table, ushort Index) { return(new LigatureSetTable(offset + Table.GetUShort(offset + offsetLigatureSetArray + Index * sizeLigatureSet))); }
// Not used. This value should be equal to glyph count in Coverage. // Keeping it for future reference //private ushort AlternateSetCount(FontTable Table) //{ // return Table.GetUShort(offset + offsetAlternateSetCount); //} private AlternateSetTable AlternateSet(FontTable Table, int index) { return new AlternateSetTable(offset + Table.GetUShort(offset + offsetAlternateSets + index * sizeAlternateSetOffset) ); }
private static void ComputeTableCache( IOpenTypeFont font, OpenTypeTags tableTag, int maxCacheSize, ref int cacheSize, ref GlyphLookupRecord[] records, ref int recordCount, ref int glyphCount, ref int lastLookupAdded ) { FontTable table = font.GetFontTable(tableTag); if (!table.IsPresent) { return; } FeatureList featureList; LookupList lookupList; Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); switch (tableTag) { case OpenTypeTags.GSUB: { GSUBHeader header = new GSUBHeader(); featureList = header.GetFeatureList(table); lookupList = header.GetLookupList(table); break; } case OpenTypeTags.GPOS: { GPOSHeader header = new GPOSHeader(); featureList = header.GetFeatureList(table); lookupList = header.GetLookupList(table); break; } default: { Debug.Assert(false); featureList = new FeatureList(0); lookupList = new LookupList(0); break; } } // Estimate number of records that can fit into cache using ratio of approximately // 4 bytes of cache per actual record. Most of fonts will fit into this value, except // some tiny caches and big EA font that can have ratio of around 5 (theoretical maximum is 8). // // If actual ratio for particluar font will be larger than 4, we will remove records // from the end to fit into cache. // // If ratio is less than 4 we actually can fit more lookups, but for the speed and because most fonts // will fit into cache completely anyway we do not do anything about this here. int maxRecordCount = maxCacheSize / 4; // For now, we will just allocate array of maximum size. // Given heuristics above, it wont be greater than max cache size. // records = new GlyphLookupRecord[maxRecordCount]; // // Now iterate through lookups and subtables, filling in lookup-glyph pairs list // int lookupCount = lookupList.LookupCount(table); int recordCountAfterLastLookup = 0; // // Not all lookups can be invoked from feature directly, // they are actions from contextual lookups. // We are not interested in those, because they will // never work from high level, so do not bother adding them to the cache. // // Filling array of lookup usage bits, to skip those not mapped to any lookup // BitArray lookupUsage = new BitArray(lookupCount); for (ushort feature = 0; feature < featureList.FeatureCount(table); feature++) { FeatureTable featureTable = featureList.FeatureTable(table, feature); for (ushort lookup = 0; lookup < featureTable.LookupCount(table); lookup++) { ushort lookupIndex = featureTable.LookupIndex(table, lookup); if (lookupIndex >= lookupCount) { // This must be an invalid font. Just igonoring this lookup here. continue; } lookupUsage[lookupIndex] = true; } } // Done with lookup usage bits for (ushort lookupIndex = 0; lookupIndex < lookupCount; lookupIndex++) { if (!lookupUsage[lookupIndex]) { continue; } int firstLookupRecord = recordCount; int maxLookupGlyph = -1; bool cacheIsFull = false; LookupTable lookup = lookupList.Lookup(table, lookupIndex); ushort lookupType = lookup.LookupType(); ushort subtableCount = lookup.SubTableCount(); for (ushort subtableIndex = 0; subtableIndex < subtableCount; subtableIndex++) { int subtableOffset = lookup.SubtableOffset(table, subtableIndex); CoverageTable coverage = GetSubtablePrincipalCoverage(table, tableTag, lookupType, subtableOffset); if (coverage.IsInvalid) { continue; } cacheIsFull = !AppendCoverageGlyphRecords(table, lookupIndex, coverage, records, ref recordCount, ref maxLookupGlyph); if (cacheIsFull) { break; } } if (cacheIsFull) { break; } lastLookupAdded = lookupIndex; recordCountAfterLastLookup = recordCount; } // We may hit storage overflow in the middle of lookup. Throw this partial lookup away recordCount = recordCountAfterLastLookup; if (lastLookupAdded == -1) { // We did not succeed adding even single lookup. return; } // We now have glyph records for (may be not all) lookups in the table. // Cache structures should be sorted by glyph, then by lookup index. Array.Sort(records, 0, recordCount); cacheSize = -1; glyphCount = -1; // It may happen, that records do not fit into cache, even using our heuristics. // We will remove lookups one by one from the end until it fits. while (recordCount > 0) { CalculateCacheSize(records, recordCount, out cacheSize, out glyphCount); if (cacheSize <= maxCacheSize) { // Fine, we now fit into max cache size break; } else { // Find last lookup index int lastLookup = -1; for (int i = 0; i < recordCount; i++) { int lookup = records[i].Lookup; if (lastLookup < lookup) { lastLookup = lookup; } } Debug.Assert(lastLookup >= 0); // There are lookups, so there was an index // Remove it int currentRecord = 0; for (int i = 0; i < recordCount; i++) { if (records[i].Lookup == lastLookup) { continue; } if (currentRecord == i) { continue; } records[currentRecord] = records[i]; currentRecord++; } recordCount = currentRecord; // Do not forget update lastLookupAdded variable lastLookupAdded = lastLookup - 1; } } if (recordCount == 0) { // We can't fit even single lookup into the cache return; } Debug.Assert(cacheSize > 0); // We've calcucalted them at least ones, and Debug.Assert(glyphCount > 0); // if there is no records, we already should've exited }
public ushort Alternate(FontTable Table, uint FeatureParam) { Invariant.Assert(FeatureParam > 0); // Parameter 0 means feautre is disabled. //Should be filtered out in GetNextEnabledGlyphRange // Off by one - alternate number 1 is stored under index 0 uint index = FeatureParam - 1; if (index >= GlyphCount(Table)) { return AlternateSubstitutionSubtable.InvalidAlternateGlyph; } return Table.GetUShort(offset + offsetGlyphs + (ushort)index*sizeGlyph); }
private static CoverageTable GetSubtablePrincipalCoverage( FontTable table, OpenTypeTags tableTag, ushort lookupType, int subtableOffset ) { Debug.Assert(tableTag == OpenTypeTags.GSUB || tableTag == OpenTypeTags.GPOS); CoverageTable coverage = CoverageTable.InvalidCoverage; switch (tableTag) { case OpenTypeTags.GSUB: if (lookupType == 7) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SingleSubst SingleSubstitutionSubtable singleSubst = new SingleSubstitutionSubtable(subtableOffset); return(singleSubst.GetPrimaryCoverage(table)); case 2: //MultipleSubst MultipleSubstitutionSubtable multipleSub = new MultipleSubstitutionSubtable(subtableOffset); return(multipleSub.GetPrimaryCoverage(table)); case 3: //AlternateSubst AlternateSubstitutionSubtable alternateSub = new AlternateSubstitutionSubtable(subtableOffset); return(alternateSub.GetPrimaryCoverage(table)); case 4: //Ligature subst LigatureSubstitutionSubtable ligaSub = new LigatureSubstitutionSubtable(subtableOffset); return(ligaSub.GetPrimaryCoverage(table)); case 5: //ContextualSubst ContextSubtable contextSub = new ContextSubtable(subtableOffset); return(contextSub.GetPrimaryCoverage(table)); case 6: //ChainingSubst ChainingSubtable chainingSub = new ChainingSubtable(subtableOffset); return(chainingSub.GetPrimaryCoverage(table)); case 7: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it break; case 8: //ReverseCahiningSubst ReverseChainingSubtable reverseChainingSub = new ReverseChainingSubtable(subtableOffset); return(reverseChainingSub.GetPrimaryCoverage(table)); } break; case OpenTypeTags.GPOS: if (lookupType == 9) { ExtensionLookupTable extension = new ExtensionLookupTable(subtableOffset); lookupType = extension.LookupType(table); subtableOffset = extension.LookupSubtableOffset(table); } switch (lookupType) { case 1: //SinglePos SinglePositioningSubtable singlePos = new SinglePositioningSubtable(subtableOffset); return(singlePos.GetPrimaryCoverage(table)); case 2: //PairPos PairPositioningSubtable pairPos = new PairPositioningSubtable(subtableOffset); return(pairPos.GetPrimaryCoverage(table)); case 3: // CursivePos CursivePositioningSubtable cursivePos = new CursivePositioningSubtable(subtableOffset); return(cursivePos.GetPrimaryCoverage(table)); case 4: //MarkToBasePos MarkToBasePositioningSubtable markToBasePos = new MarkToBasePositioningSubtable(subtableOffset); return(markToBasePos.GetPrimaryCoverage(table)); case 5: //MarkToLigaturePos // Under construction MarkToLigaturePositioningSubtable markToLigaPos = new MarkToLigaturePositioningSubtable(subtableOffset); return(markToLigaPos.GetPrimaryCoverage(table)); case 6: //MarkToMarkPos MarkToMarkPositioningSubtable markToMarkPos = new MarkToMarkPositioningSubtable(subtableOffset); return(markToMarkPos.GetPrimaryCoverage(table)); case 7: // Contextual ContextSubtable contextPos = new ContextSubtable(subtableOffset); return(contextPos.GetPrimaryCoverage(table)); case 8: // Chaining ChainingSubtable chainingPos = new ChainingSubtable(subtableOffset); return(chainingPos.GetPrimaryCoverage(table)); case 9: //Extension lookup // Ext.Lookup processed earlier. It can't contain another ext.lookups in it break; } break; } return(CoverageTable.InvalidCoverage); }
private ushort LigatureSetCount(FontTable Table) { return(Table.GetUShort(offset + offsetLigatureSetCount)); }
///<summary> /// Internal method to test layout tables if they are uitable for fast path. /// Returns list of script-langauge pairs that are not optimizable. ///</summary> internal static OpenTypeLayoutResult GetComplexLanguageList( IOpenTypeFont Font, //In: Font access interface uint[] featureList, //In: Feature to look in uint[] glyphBits, ushort minGlyphId, ushort maxGlyphId, out WritingSystem[] complexLanguages // Out: List of script/langauge pair // that are not optimizable ) { try { WritingSystem[] gsubComplexLanguages = null; WritingSystem[] gposComplexLanguages = null; int gsubComplexLanguagesCount = 0; int gposComplexLanguagesCount = 0; FontTable GsubTable = Font.GetFontTable(OpenTypeTags.GSUB); FontTable GposTable = Font.GetFontTable(OpenTypeTags.GPOS); if (GsubTable.IsPresent) { LayoutEngine.GetComplexLanguageList( OpenTypeTags.GSUB, GsubTable, featureList, glyphBits, minGlyphId, maxGlyphId, out gsubComplexLanguages, out gsubComplexLanguagesCount ); } if (GposTable.IsPresent) { LayoutEngine.GetComplexLanguageList( OpenTypeTags.GPOS, GposTable, featureList, glyphBits, minGlyphId, maxGlyphId, out gposComplexLanguages, out gposComplexLanguagesCount ); } if (gsubComplexLanguages == null && gposComplexLanguages == null) { complexLanguages = null; return(OpenTypeLayoutResult.Success); } // Both tables have complex scrips, merge results // Count gpos unique Languages // and pack them at the same time // so we do not research them again. int gposNewLanguages = 0, i, j; for (i = 0; i < gposComplexLanguagesCount; i++) { bool foundInGsub = false; for (j = 0; j < gsubComplexLanguagesCount; j++) { if (gsubComplexLanguages[j].scriptTag == gposComplexLanguages[i].scriptTag && gsubComplexLanguages[j].langSysTag == gposComplexLanguages[i].langSysTag ) { foundInGsub = true; break; } ; } if (!foundInGsub) { if (gposNewLanguages < i) { gposComplexLanguages[gposNewLanguages] = gposComplexLanguages[i]; } gposNewLanguages++; } } //realloc array for merged results, merge both arrays complexLanguages = new WritingSystem[gsubComplexLanguagesCount + gposNewLanguages]; for (i = 0; i < gsubComplexLanguagesCount; i++) { complexLanguages[i] = gsubComplexLanguages[i]; } for (i = 0; i < gposNewLanguages; i++) { complexLanguages[gsubComplexLanguagesCount + i] = gposComplexLanguages[i]; } return(OpenTypeLayoutResult.Success); } catch (FileFormatException) { complexLanguages = null; return(OpenTypeLayoutResult.BadFontTable); } }
public CoverageTable GetPrimaryCoverage(FontTable table) { return Coverage(table); }
public ushort Component(FontTable Table, ushort Index) { //LigaTable includes comps from 1 to N. So, (Index-1) return(Table.GetUShort(offset + offsetComponentArray + (Index - 1) * sizeComponent)); }