Beispiel #1
0
        /// <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);
        }
Beispiel #2
0
/* 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);
        }
        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
        }