public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } SkippingGlyphIterator iterator = new(fontMetrics, collection, index, this.LookupFlags); if (!AdvancedTypographicUtils.MatchCoverageSequence(iterator, this.coverageTables, 0)) { return(false); } return(AdvancedTypographicUtils.ApplyLookupList( fontMetrics, table, feature, this.LookupFlags, this.sequenceLookupRecords, collection, index, count)); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } if (this.coverageTable.CoverageIndexOf(glyphId) < 0) { return(false); } int offset = this.classDefinitionTable.ClassIndexOf(glyphId); if (offset < 0) { return(false); } ClassSequenceRuleSetTable ruleSetTable = this.sequenceRuleSetTables[offset]; SkippingGlyphIterator iterator = new(fontMetrics, collection, index, this.LookupFlags); foreach (ClassSequenceRuleTable ruleTable in ruleSetTable.SequenceRuleTables) { int remaining = count - 1; int seqLength = ruleTable.InputSequence.Length; if (seqLength > remaining) { continue; } if (!AdvancedTypographicUtils.MatchClassSequence(iterator, 1, ruleTable.InputSequence, this.classDefinitionTable)) { continue; } // It's a match. Perform position update and return true if anything changed. return(AdvancedTypographicUtils.ApplyLookupList( fontMetrics, table, feature, this.LookupFlags, ruleTable.SequenceLookupRecords, collection, index, count)); } return(false); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { // Mark-to-Base Attachment Positioning Subtable. // Implements: https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-4-mark-to-base-attachment-positioning-subtable ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } int markIndex = this.markCoverage.CoverageIndexOf(glyphId); if (markIndex == -1) { return(false); } // Search backward for a base glyph. int baseGlyphIndex = index; while (--baseGlyphIndex >= 0) { GlyphShapingData data = collection.GetGlyphShapingData(baseGlyphIndex); if (!AdvancedTypographicUtils.IsMarkGlyph(fontMetrics, data.GlyphIds[0], data) && !(data.LigatureComponent > 0)) { break; } } if (baseGlyphIndex < 0) { return(false); } ushort baseGlyphId = collection[baseGlyphIndex][0]; int baseIndex = this.baseCoverage.CoverageIndexOf(baseGlyphId); if (baseIndex < 0) { return(false); } MarkRecord markRecord = this.markArrayTable.MarkRecords[markIndex]; AnchorTable baseAnchor = this.baseArrayTable.BaseRecords[baseIndex].BaseAnchorTables[markRecord.MarkClass]; AdvancedTypographicUtils.ApplyAnchor(collection, index, baseAnchor, markRecord, baseGlyphIndex); return(true); }
private bool ShouldIgnore(int index) { GlyphShapingData data = this.Collection.GetGlyphShapingData(index); GlyphShapingClass shapingClass = AdvancedTypographicUtils.GetGlyphShapingClass(this.fontMetrics, data.GlyphIds[0], data); return((this.ignoreMarks && shapingClass.IsMark) || (this.ignoreBaseGlypghs && shapingClass.IsBase) || (this.ignoreLigatures && shapingClass.IsLigature) || (this.markAttachmentType > 0 && shapingClass.IsMark && shapingClass.MarkAttachmentType != this.markAttachmentType)); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } int offset = this.coverageTable.CoverageIndexOf(glyphId); if (offset <= -1) { return(false); } // TODO: Check this. // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#example-7-contextual-substitution-format-1 SequenceRuleSetTable ruleSetTable = this.seqRuleSetTables[offset]; SkippingGlyphIterator iterator = new(fontMetrics, collection, index, this.LookupFlags); foreach (SequenceRuleTable ruleTable in ruleSetTable.SequenceRuleTables) { int remaining = count - 1; int seqLength = ruleTable.InputSequence.Length; if (seqLength > remaining) { continue; } if (!AdvancedTypographicUtils.MatchSequence(iterator, 1, ruleTable.InputSequence)) { continue; } // It's a match. Perform position update and return true if anything changed. return(AdvancedTypographicUtils.ApplyLookupList( fontMetrics, table, feature, this.LookupFlags, ruleTable.SequenceLookupRecords, collection, index, count)); } return(false); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { if (count <= 1) { return(false); } ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } int coverage = this.coverageTable.CoverageIndexOf(glyphId); if (coverage > -1) { PairSetTable pairSet = this.pairSets[coverage]; ushort glyphId2 = collection[index + 1][0]; if (glyphId2 == 0) { return(false); } if (pairSet.TryGetPairValueRecord(glyphId2, out PairValueRecord pairValueRecord)) { ValueRecord record1 = pairValueRecord.ValueRecord1; AdvancedTypographicUtils.ApplyPosition(collection, index, record1); ValueRecord record2 = pairValueRecord.ValueRecord2; AdvancedTypographicUtils.ApplyPosition(collection, (ushort)(index + 1), record2); return(true); } } return(false); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { // Implements Chained Contexts Substitution, Format 1: // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#61-chained-contexts-substitution-format-1-simple-glyph-contexts ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } // Search for the current glyph in the Coverage table. int offset = this.coverageTable.CoverageIndexOf(glyphId); if (offset <= -1) { return(false); } if (this.seqRuleSetTables is null || this.seqRuleSetTables.Length is 0) { return(false); } ChainedSequenceRuleSetTable seqRuleSet = this.seqRuleSetTables[offset]; if (seqRuleSet is null) { return(false); } // Apply ruleset for the given glyph id. ChainedSequenceRuleTable[] rules = seqRuleSet.SequenceRuleTables; SkippingGlyphIterator iterator = new(fontMetrics, collection, index, this.LookupFlags); for (int lookupIndex = 0; lookupIndex < rules.Length; lookupIndex++) { ChainedSequenceRuleTable rule = rules[lookupIndex]; if (!AdvancedTypographicUtils.ApplyChainedSequenceRule(iterator, rule)) { continue; } bool hasChanged = false; for (int j = 0; j < rule.SequenceLookupRecords.Length; j++) { SequenceLookupRecord sequenceLookupRecord = rule.SequenceLookupRecords[j]; LookupTable lookup = table.LookupList.LookupTables[sequenceLookupRecord.LookupListIndex]; ushort sequenceIndex = sequenceLookupRecord.SequenceIndex; if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, (ushort)(index + sequenceIndex), 1)) { hasChanged = true; } } return(hasChanged); } return(false); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { // Implements Chained Contexts Substitution for Format 2: // https://docs.microsoft.com/en-us/typography/opentype/spec/gsub#62-chained-contexts-substitution-format-2-class-based-glyph-contexts ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } // Search for the current glyph in the Coverage table. int offset = this.coverageTable.CoverageIndexOf(glyphId); if (offset <= -1) { return(false); } // Search in the class definition table to find the class value assigned to the currently glyph. int classId = this.inputClassDefinitionTable.ClassIndexOf(glyphId); ChainedClassSequenceRuleTable[]? rules = classId >= 0 && classId < this.sequenceRuleSetTables.Length ? this.sequenceRuleSetTables[classId].SubRules : null; if (rules is null) { return(false); } // Apply ruleset for the given glyph class id. SkippingGlyphIterator iterator = new(fontMetrics, collection, index, this.LookupFlags); for (int lookupIndex = 0; lookupIndex < rules.Length; lookupIndex++) { ChainedClassSequenceRuleTable rule = rules[lookupIndex]; if (!AdvancedTypographicUtils.ApplyChainedClassSequenceRule(iterator, rule, this.inputClassDefinitionTable, this.backtrackClassDefinitionTable, this.lookaheadClassDefinitionTable)) { continue; } // It's a match. Perform position update and return true if anything changed. bool hasChanged = false; for (int j = 0; j < rule.SequenceLookupRecords.Length; j++) { SequenceLookupRecord sequenceLookupRecord = rule.SequenceLookupRecords[j]; LookupTable lookup = table.LookupList.LookupTables[sequenceLookupRecord.LookupListIndex]; ushort sequenceIndex = sequenceLookupRecord.SequenceIndex; if (lookup.TryUpdatePosition(fontMetrics, table, collection, feature, (ushort)(index + sequenceIndex), 1)) { hasChanged = true; } } return(hasChanged); } return(false); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { // Mark-to-Ligature Attachment Positioning. // Implements: https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-5-mark-to-ligature-attachment-positioning-subtable ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } int markIndex = this.markCoverage.CoverageIndexOf(glyphId); if (markIndex < 0) { return(false); } // Search backward for a base glyph. int baseGlyphIndex = index; while (--baseGlyphIndex >= 0) { GlyphShapingData data = collection.GetGlyphShapingData(baseGlyphIndex); if (!AdvancedTypographicUtils.IsMarkGlyph(fontMetrics, data.GlyphIds[0], data)) { break; } } if (baseGlyphIndex < 0) { return(false); } ushort baseGlyphId = collection[baseGlyphIndex][0]; int ligatureIndex = this.ligatureCoverage.CoverageIndexOf(baseGlyphId); if (ligatureIndex < 0) { return(false); } // We must now check whether the ligature ID of the current mark glyph // is identical to the ligature ID of the found ligature. // If yes, we can directly use the component index. If not, we attach the mark // glyph to the last component of the ligature. LigatureAttachTable ligatureAttach = this.ligatureArrayTable.LigatureAttachTables[ligatureIndex]; GlyphShapingData markGlyph = collection.GetGlyphShapingData(index); GlyphShapingData ligGlyph = collection.GetGlyphShapingData(baseGlyphIndex); int compIndex = ligGlyph.LigatureId > 0 && ligGlyph.LigatureId == markGlyph.LigatureId && markGlyph.LigatureComponent > 0 ? Math.Min(markGlyph.LigatureComponent, ligGlyph.CodePointCount) - 1 : ligGlyph.CodePointCount - 1; MarkRecord markRecord = this.markArrayTable.MarkRecords[markIndex]; AnchorTable baseAnchor = ligatureAttach.ComponentRecords[compIndex].LigatureAnchorTables[markRecord.MarkClass]; AdvancedTypographicUtils.ApplyAnchor(collection, index, baseAnchor, markRecord, baseGlyphIndex); return(true); }
public override bool TryUpdatePosition( FontMetrics fontMetrics, GPosTable table, GlyphPositioningCollection collection, Tag feature, ushort index, int count) { // Mark to mark positioning. // Implements: https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-6-mark-to-mark-attachment-positioning-subtable ushort glyphId = collection[index][0]; if (glyphId == 0) { return(false); } int mark1Index = this.mark1Coverage.CoverageIndexOf(glyphId); if (mark1Index == -1) { return(false); } // Get the previous mark to attach to. if (index < 1) { return(false); } int prevIdx = index - 1; ushort prevGlyphId = collection[prevIdx][0]; GlyphShapingData prevGlyph = collection.GetGlyphShapingData(prevIdx); if (!AdvancedTypographicUtils.IsMarkGlyph(fontMetrics, prevGlyphId, prevGlyph)) { return(false); } // The following logic was borrowed from Harfbuzz, // see: https://github.com/harfbuzz/harfbuzz/blob/3e635cf5e26e33d6210d3092256a49291752deec/src/hb-ot-layout-gpos-table.hh#L2525 bool good = false; GlyphShapingData curGlyph = collection.GetGlyphShapingData(index); if (curGlyph.LigatureId == prevGlyph.LigatureId) { if (curGlyph.LigatureId > 0) { // Marks belonging to the same base. good = true; } else if (curGlyph.LigatureComponent == prevGlyph.LigatureComponent) { // Marks belonging to the same ligature component. good = true; } } else { // If ligature ids don't match, it may be the case that one of the marks // itself is a ligature, in which case match. if ((curGlyph.LigatureId > 0 && curGlyph.LigatureComponent <= 0) || (prevGlyph.LigatureId > 0 && prevGlyph.LigatureComponent <= 0)) { good = true; } } if (!good) { return(false); } int mark2Index = this.mark2Coverage.CoverageIndexOf(prevGlyphId); if (mark2Index == -1) { return(false); } MarkRecord markRecord = this.mark1ArrayTable.MarkRecords[mark1Index]; AnchorTable baseAnchor = this.mark2ArrayTable.Mark2Records[mark2Index].MarkAnchorTable[markRecord.MarkClass]; AdvancedTypographicUtils.ApplyAnchor(collection, index, baseAnchor, markRecord, prevIdx); return(true); }