public static LookupType3Format1SubTable Load(BigEndianBinaryReader reader, long offset, LookupFlags lookupFlags) { // Cursive Attachment Positioning Format1. // +--------------------+---------------------------------+------------------------------------------------------+ // | Type | Name | Description | // +====================+=================================+======================================================+ // | uint16 | posFormat | Format identifier: format = 1 | // +--------------------+---------------------------------+------------------------------------------------------+ // | Offset16 | coverageOffset | Offset to Coverage table, | // | | | from beginning of CursivePos subtable. | // +--------------------+---------------------------------+------------------------------------------------------+ // | uint16 | entryExitCount | Number of EntryExit records. | // +--------------------+---------------------------------+------------------------------------------------------+ // | EntryExitRecord | entryExitRecord[entryExitCount] | Array of EntryExit records, in Coverage index order. | // +--------------------+---------------------------------+------------------------------------------------------+ ushort coverageOffset = reader.ReadOffset16(); ushort entryExitCount = reader.ReadUInt16(); var entryExitRecords = new EntryExitRecord[entryExitCount]; for (int i = 0; i < entryExitCount; i++) { entryExitRecords[i] = new EntryExitRecord(reader, offset); } var entryExitAnchors = new EntryExitAnchors[entryExitCount]; for (int i = 0; i < entryExitCount; i++) { entryExitAnchors[i] = new EntryExitAnchors(reader, offset, entryExitRecords[i]); } var coverageTable = CoverageTable.Load(reader, offset + coverageOffset); return(new LookupType3Format1SubTable(coverageTable, entryExitAnchors, lookupFlags)); }
public static LookupType4Format1SubTable Load(BigEndianBinaryReader reader, long offset, LookupFlags lookupFlags) { // MarkBasePosFormat1 Subtable. // +--------------------+---------------------------------+------------------------------------------------------+ // | Type | Name | Description | // +====================+=================================+======================================================+ // | uint16 | posFormat | Format identifier: format = 1 | // +--------------------+---------------------------------+------------------------------------------------------+ // | Offset16 | markCoverageOffset | Offset to markCoverage table, | // | | | from beginning of MarkBasePos subtable. | // +--------------------+---------------------------------+------------------------------------------------------+ // | Offset16 | baseCoverageOffset | Offset to baseCoverage table, | // | | | from beginning of MarkBasePos subtable. | // +--------------------+---------------------------------+------------------------------------------------------+ // | uint16 | markClassCount | Number of classes defined for marks. | // +--------------------+---------------------------------+------------------------------------------------------+ // | Offset16 | markArrayOffset | Offset to MarkArray table, | // | | | from beginning of MarkBasePos subtable. | // +--------------------+---------------------------------+------------------------------------------------------+ // | Offset16 | baseArrayOffset | Offset to BaseArray table, | // | | | from beginning of MarkBasePos subtable. | // +--------------------+---------------------------------+------------------------------------------------------+ ushort markCoverageOffset = reader.ReadOffset16(); ushort baseCoverageOffset = reader.ReadOffset16(); ushort markClassCount = reader.ReadUInt16(); ushort markArrayOffset = reader.ReadOffset16(); ushort baseArrayOffset = reader.ReadOffset16(); var markCoverage = CoverageTable.Load(reader, offset + markCoverageOffset); var baseCoverage = CoverageTable.Load(reader, offset + baseCoverageOffset); var markArrayTable = new MarkArrayTable(reader, offset + markArrayOffset); var baseArrayTable = new BaseArrayTable(reader, offset + baseArrayOffset, markClassCount); return(new LookupType4Format1SubTable(markCoverage, baseCoverage, markArrayTable, baseArrayTable, lookupFlags)); }
private LookupType8Format1SubTable( CoverageTable coverageTable, ChainedSequenceRuleSetTable[] seqRuleSetTables, LookupFlags lookupFlags) : base(lookupFlags) { this.coverageTable = coverageTable; this.seqRuleSetTables = seqRuleSetTables; }
public LookupType7Format2SubTable( CoverageTable coverageTable, ClassDefinitionTable classDefinitionTable, ClassSequenceRuleSetTable[] sequenceRuleSetTables, LookupFlags lookupFlags) : base(lookupFlags) { this.coverageTable = coverageTable; this.classDefinitionTable = classDefinitionTable; this.sequenceRuleSetTables = sequenceRuleSetTables; }
public LookupType5Format1SubTable( CoverageTable markCoverage, CoverageTable ligatureCoverage, MarkArrayTable markArrayTable, LigatureArrayTable ligatureArrayTable, LookupFlags lookupFlags) : base(lookupFlags) { this.markCoverage = markCoverage; this.ligatureCoverage = ligatureCoverage; this.markArrayTable = markArrayTable; this.ligatureArrayTable = ligatureArrayTable; }
public LookupType6Format1SubTable( CoverageTable mark1Coverage, CoverageTable mark2Coverage, MarkArrayTable mark1ArrayTable, Mark2ArrayTable mark2ArrayTable, LookupFlags lookupFlags) : base(lookupFlags) { this.mark1Coverage = mark1Coverage; this.mark2Coverage = mark2Coverage; this.mark1ArrayTable = mark1ArrayTable; this.mark2ArrayTable = mark2ArrayTable; }
public LookupType4Format1SubTable( CoverageTable markCoverage, CoverageTable baseCoverage, MarkArrayTable markArrayTable, BaseArrayTable baseArrayTable, LookupFlags lookupFlags) : base(lookupFlags) { this.markCoverage = markCoverage; this.baseCoverage = baseCoverage; this.markArrayTable = markArrayTable; this.baseArrayTable = baseArrayTable; }
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); }
private LookupType8Format2SubTable( ChainedClassSequenceRuleSetTable[] sequenceRuleSetTables, ClassDefinitionTable backtrackClassDefinitionTable, ClassDefinitionTable inputClassDefinitionTable, ClassDefinitionTable lookaheadClassDefinitionTable, CoverageTable coverageTable, LookupFlags lookupFlags) : base(lookupFlags) { this.sequenceRuleSetTables = sequenceRuleSetTables; this.backtrackClassDefinitionTable = backtrackClassDefinitionTable; this.inputClassDefinitionTable = inputClassDefinitionTable; this.lookaheadClassDefinitionTable = lookaheadClassDefinitionTable; this.coverageTable = coverageTable; }
private void ExtractDataFromSingleSubstTableFormat1Table(Dictionary <List <int>, int> glyphSubstitutionMap, LookupTypeSingleSubstFormat1 singleSubstTableFormat1) { CoverageTable coverageTable = singleSubstTableFormat1.CoverageTable; for (int i = 0; i < coverageTable.Size; i++) { int coverageGlyphId = coverageTable.GetGlyphId(i); int substituteGlyphId = coverageGlyphId + singleSubstTableFormat1.DeltaGlyphID; PutNewSubstitutionEntry(glyphSubstitutionMap, substituteGlyphId, new List <int> { coverageGlyphId }); } }
public static LookupType2Format1SubTable Load(BigEndianBinaryReader reader, long offset, LookupFlags lookupFlags) { // Pair Adjustment Positioning Subtable format 1. // +-------------+------------------------------+------------------------------------------------+ // | Type | Name | Description | // +=============+==============================+================================================+ // | uint16 | posFormat | Format identifier: format = 1 | // +-------------+------------------------------+------------------------------------------------+ // | Offset16 | coverageOffset | Offset to Coverage table, from beginning of | // | | | PairPos subtable. | // +-------------+------------------------------+------------------------------------------------+ // | uint16 | valueFormat1 | Defines the types of data in valueRecord1 — | // | | | for the first glyph in the pair (may be zero). | // +-------------+------------------------------+------------------------------------------------+ // | uint16 | valueFormat2 | Defines the types of data in valueRecord2 — | // | | | for the second glyph in the pair (may be zero).| // +-------------+------------------------------+------------------------------------------------+ // | uint16 | pairSetCount | Number of PairSet tables | // +-------------+------------------------------+------------------------------------------------+ // | Offset16 | pairSetOffsets[pairSetCount] | Array of offsets to PairSet tables. | // | | | Offsets are from beginning of PairPos subtable,| // | | | ordered by Coverage Index. | // +-------------+------------------------------+------------------------------------------------+ ushort coverageOffset = reader.ReadOffset16(); ValueFormat valueFormat1 = reader.ReadUInt16 <ValueFormat>(); ValueFormat valueFormat2 = reader.ReadUInt16 <ValueFormat>(); ushort pairSetCount = reader.ReadUInt16(); using Buffer <ushort> pairSetOffsetsBuffer = new(pairSetCount); Span <ushort> pairSetOffsets = pairSetOffsetsBuffer.GetSpan(); reader.ReadUInt16Array(pairSetOffsets); var pairSets = new PairSetTable[pairSetCount]; for (int i = 0; i < pairSetCount; i++) { reader.Seek(offset + pairSetOffsets[i], SeekOrigin.Begin); pairSets[i] = PairSetTable.Load(reader, offset + pairSetOffsets[i], valueFormat1, valueFormat2); } var coverageTable = CoverageTable.Load(reader, offset + coverageOffset); return(new LookupType2Format1SubTable(coverageTable, pairSets, lookupFlags)); }
private void ExtractDataFromSingleSubstTableFormat2Table( Dictionary <List <int>, int> glyphSubstitutionMap, LookupTypeSingleSubstFormat2 singleSubstTableFormat2) { CoverageTable coverageTable = singleSubstTableFormat2.CoverageTable; if (coverageTable.Size != singleSubstTableFormat2.SubstituteGlyphIDs.Length) { throw new ArgumentException( "The no. coverage table entries should be the same as the size of the substituteGlyphIDs"); } for (int i = 0; i < coverageTable.Size; i++) { int coverageGlyphId = coverageTable.GetGlyphId(i); int substituteGlyphId = coverageGlyphId + singleSubstTableFormat2.SubstituteGlyphIDs[i]; PutNewSubstitutionEntry(glyphSubstitutionMap, substituteGlyphId, new List <int> { coverageGlyphId }); } }
public LookupType7Format1SubTable(CoverageTable coverageTable, SequenceRuleSetTable[] seqRuleSetTables, LookupFlags lookupFlags) : base(lookupFlags) { this.seqRuleSetTables = seqRuleSetTables; this.coverageTable = coverageTable; }
public static LookupType7Format2SubTable Load(BigEndianBinaryReader reader, long offset, LookupFlags lookupFlags) { CoverageTable coverageTable = TableLoadingUtils.LoadSequenceContextFormat2(reader, offset, out ClassDefinitionTable classDefTable, out ClassSequenceRuleSetTable[] classSeqRuleSets); return(new LookupType7Format2SubTable(coverageTable, classDefTable, classSeqRuleSets, lookupFlags)); }
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 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 LookupType3Format1SubTable(CoverageTable coverageTable, EntryExitAnchors[] entryExitAnchors, LookupFlags lookupFlags) : base(lookupFlags) { this.coverageTable = coverageTable; this.entryExitAnchors = entryExitAnchors; }
public LookupSubTable(int substFormat, CoverageTable coverageTable) { this.substFormat = substFormat; this.coverageTable = coverageTable; }
public LookupTypeSingleSubstFormat2(int substFormat, CoverageTable coverageTable, int[] substituteGlyphIDs) : base(substFormat, coverageTable) { ; this.substituteGlyphIDs = substituteGlyphIDs; }
public LookupType2Format1SubTable(CoverageTable coverageTable, PairSetTable[] pairSets, LookupFlags lookupFlags) : base(lookupFlags) { this.coverageTable = coverageTable; this.pairSets = pairSets; }
public LookupTypeSingleSubstFormat1(int substFormat, CoverageTable coverageTable, short deltaGlyphID) : base(substFormat, coverageTable) { this.deltaGlyphID = deltaGlyphID; }
public LookupTypeLigatureSubstitutionSubstFormat1(int substFormat, CoverageTable coverageTable, LigatureSetTable[] ligatureSetTables) : base(substFormat, coverageTable) { this.ligatureSetTables = ligatureSetTables; }