Ejemplo n.º 1
0
            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));
            }
Ejemplo n.º 2
0
 public override bool TryUpdatePosition(
     FontMetrics fontMetrics,
     GPosTable table,
     GlyphPositioningCollection collection,
     Tag feature,
     ushort index,
     int count)
 => false;
Ejemplo n.º 3
0
            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);
            }
Ejemplo n.º 5
0
            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);
            }
Ejemplo n.º 9
0
            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);
            }
Ejemplo n.º 10
0
            public override bool TryUpdatePosition(
                FontMetrics fontMetrics,
                GPosTable table,
                GlyphPositioningCollection collection,
                Tag feature,
                ushort index,
                int count)
            {
                if (count <= 1)
                {
                    return(false);
                }

                // Implements Cursive Attachment Positioning Subtable:
                // https://docs.microsoft.com/en-us/typography/opentype/spec/gpos#lookup-type-3-cursive-attachment-positioning-subtable
                ushort glyphId = collection[index][0];

                if (glyphId == 0)
                {
                    return(false);
                }

                ushort nextIndex   = (ushort)(index + 1);
                ushort nextGlyphId = collection[nextIndex][0];

                if (nextGlyphId == 0)
                {
                    return(false);
                }

                int coverageNext = this.coverageTable.CoverageIndexOf(nextGlyphId);

                if (coverageNext < 0)
                {
                    return(false);
                }

                EntryExitAnchors nextRecord = this.entryExitAnchors[coverageNext];
                AnchorTable?     entry      = nextRecord.EntryAnchor;

                if (entry is null)
                {
                    return(false);
                }

                int coverage = this.coverageTable.CoverageIndexOf(glyphId);

                if (coverage < 0)
                {
                    return(false);
                }

                EntryExitAnchors curRecord = this.entryExitAnchors[coverage];
                AnchorTable?     exit      = curRecord.ExitAnchor;

                if (exit is null)
                {
                    return(false);
                }

                GlyphShapingData current = collection.GetGlyphShapingData(index);
                GlyphShapingData next    = collection.GetGlyphShapingData(nextIndex);

                // TODO: Vertical.
                if (current.Direction == TextDirection.LeftToRight)
                {
                    current.Bounds.Width = exit.XCoordinate + current.Bounds.X;

                    int delta = entry.XCoordinate + next.Bounds.X;
                    next.Bounds.Width -= delta;
                    next.Bounds.X     -= delta;
                }
                else
                {
                    int delta = exit.XCoordinate + current.Bounds.X;
                    current.Bounds.Width -= delta;
                    current.Bounds.X     -= delta;

                    next.Bounds.Width = entry.XCoordinate + next.Bounds.X;
                }

                int child   = index;
                int parent  = nextIndex;
                int xOffset = entry.XCoordinate - exit.XCoordinate;
                int yOffset = entry.YCoordinate - exit.YCoordinate;

                if (this.LookupFlags.HasFlag(LookupFlags.RightToLeft))
                {
                    int temp = child;
                    child  = parent;
                    parent = temp;

                    xOffset = -xOffset;
                    yOffset = -yOffset;
                }

                // If child was already connected to someone else, walk through its old
                // chain and reverse the link direction, such that the whole tree of its
                // previous connection now attaches to new parent.Watch out for case
                // where new parent is on the path from old chain...
                bool horizontal = !collection.IsVerticalLayoutMode;

                ReverseCursiveMinorOffset(collection, index, child, horizontal, parent);

                GlyphShapingData c = collection.GetGlyphShapingData(child);

                c.CursiveAttachment = parent - child;
                if (horizontal)
                {
                    c.Bounds.Y = yOffset;
                }
                else
                {
                    c.Bounds.X = xOffset;
                }

                // If parent was attached to child, separate them.
                GlyphShapingData p = collection.GetGlyphShapingData(parent);

                if (p.CursiveAttachment == -c.CursiveAttachment)
                {
                    p.CursiveAttachment = 0;
                }

                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);
            }