Ejemplo n.º 1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="ComponentRecord"/> class.
        /// </summary>
        /// <param name="reader">The big endian binary reader.</param>
        /// <param name="markClassCount">Number of defined mark classes.</param>
        /// <param name="offset">Offset from beginning of LigatureAttach table.</param>
        public ComponentRecord(BigEndianBinaryReader reader, ushort markClassCount, long offset)
        {
            // +--------------+---------------------------------------+----------------------------------------------------------------------------------------+
            // | Type         | Name                                  | Description                                                                            |
            // +==============+=======================================+========================================================================================+
            // | Offset16     | ligatureAnchorOffsets[markClassCount] | Array of offsets (one per class) to Anchor tables. Offsets are from                    |
            // |              |                                       | beginning of LigatureAttach table, ordered by class (offsets may be NULL).             |
            // +--------------+---------------------------------------+----------------------------------------------------------------------------------------+
            this.LigatureAnchorTables = new AnchorTable[markClassCount];
            ushort[] ligatureAnchorOffsets = new ushort[markClassCount];
            for (int i = 0; i < markClassCount; i++)
            {
                ligatureAnchorOffsets[i] = reader.ReadOffset16();
            }

            long position = reader.BaseStream.Position;

            for (int i = 0; i < markClassCount; i++)
            {
                if (ligatureAnchorOffsets[i] is not 0)
                {
                    this.LigatureAnchorTables[i] = AnchorTable.Load(reader, offset + ligatureAnchorOffsets[i]);
                }
            }

            reader.BaseStream.Position = position;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="BaseRecord"/> struct.
        /// </summary>
        /// <param name="reader">The big endian binary reader.</param>
        /// <param name="classCount">The class count.</param>
        /// <param name="offset">Offset to the from beginning of BaseArray table.</param>
        public BaseRecord(BigEndianBinaryReader reader, ushort classCount, long offset)
        {
            // +--------------+-----------------------------------+----------------------------------------------------------------------------------------+
            // | Type         | Name                              | Description                                                                            |
            // +==============+===================================+========================================================================================+
            // | Offset16     | baseAnchorOffsets[markClassCount] | Array of offsets (one per mark class) to Anchor tables.                                |
            // |              |                                   | Offsets are from beginning of BaseArray table, ordered by class (offsets may be NULL). |
            // +--------------+-----------------------------------+----------------------------------------------------------------------------------------+
            this.BaseAnchorTables = new AnchorTable[classCount];
            ushort[] baseAnchorOffsets = new ushort[classCount];
            for (int i = 0; i < classCount; i++)
            {
                baseAnchorOffsets[i] = reader.ReadOffset16();
            }

            long position = reader.BaseStream.Position;

            for (int i = 0; i < classCount; i++)
            {
                if (baseAnchorOffsets[i] is not 0)
                {
                    this.BaseAnchorTables[i] = AnchorTable.Load(reader, offset + baseAnchorOffsets[i]);
                }
            }

            reader.BaseStream.Position = position;
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Initializes a new instance of the <see cref="Mark2Record"/> class.
        /// </summary>
        /// <param name="reader">The big endian binary reader.</param>
        /// <param name="markClassCount">The Number of Mark2 records.</param>
        /// <param name="offset">Offset to the beginning of MarkArray table.</param>
        public Mark2Record(BigEndianBinaryReader reader, ushort markClassCount, long offset)
        {
            // +--------------+------------------------------------+--------------------------------------------------------------------------------------+
            // | Type         | Name                               | Description                                                                          |
            // +==============+====================================+======================================================================================+
            // | Offset16     | mark2AnchorOffsets[markClassCount] | Array of offsets (one per class) to Anchor tables. Offsets are from beginning of     |
            // |              |                                    | Mark2Array table, in class order (offsets may be NULL).                              |
            // +--------------+------------------------------------+--------------------------------------------------------------------------------------+
            ushort[] mark2AnchorOffsets = new ushort[markClassCount];
            this.MarkAnchorTable = new AnchorTable[markClassCount];
            for (int i = 0; i < markClassCount; i++)
            {
                mark2AnchorOffsets[i] = reader.ReadOffset16();
            }

            long position = reader.BaseStream.Position;

            for (int i = 0; i < markClassCount; i++)
            {
                if (mark2AnchorOffsets[i] != 0)
                {
                    this.MarkAnchorTable[i] = AnchorTable.Load(reader, offset + mark2AnchorOffsets[i]);
                }
            }

            reader.BaseStream.Position = position;
        }
            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 static void ApplyAnchor(
            GlyphPositioningCollection collection,
            ushort index,
            AnchorTable baseAnchor,
            MarkRecord markRecord,
            int baseGlyphIndex)
        {
            short baseX = baseAnchor.XCoordinate;
            short baseY = baseAnchor.YCoordinate;
            short markX = markRecord.MarkAnchorTable.XCoordinate;
            short markY = markRecord.MarkAnchorTable.YCoordinate;

            GlyphShapingData data = collection.GetGlyphShapingData(index);

            data.Bounds.X       = baseX - markX;
            data.Bounds.Y       = baseY - markY;
            data.MarkAttachment = baseGlyphIndex;
        }
Ejemplo n.º 6
0
 public AnchorTable GetLigatureAnchorTable(uint i)
 {
     AnchorTable at = null;
     uint offset = GetLigatureAnchorOffset(i);
     if (offset != 0)
     {
         at = new AnchorTable(m_offsetLigatureAttach + offset, m_bufTable);
     }
     return at;
 }
Ejemplo n.º 7
0
                public AnchorTable GetExitAnchorTable()
                {
                    AnchorTable at = null;

                    if (ExitAnchorOffset != 0)
                    {
                        uint offset = m_offsetCursivePos + ExitAnchorOffset;
                        at = new AnchorTable(offset, m_bufTable);
                    }

                    return at;
                }
 /// <summary>
 /// Initializes a new instance of the <see cref="EntryExitAnchors"/> class.
 /// </summary>
 /// <param name="reader">The big endian binary reader.</param>
 /// <param name="offset">The offset to exitAnchor table, from beginning of CursivePos subtable.</param>
 /// <param name="entryExitRecord">Offsets to entry and exit Anchor table, from beginning of CursivePos subtable.</param>
 public EntryExitAnchors(BigEndianBinaryReader reader, long offset, EntryExitRecord entryExitRecord)
 {
     this.EntryAnchor = entryExitRecord.EntryAnchorOffset != 0 ? AnchorTable.Load(reader, offset + entryExitRecord.EntryAnchorOffset) : null;
     this.ExitAnchor  = entryExitRecord.ExitAnchorOffset != 0 ? AnchorTable.Load(reader, offset + entryExitRecord.ExitAnchorOffset) : null;
 }
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);
            }
            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);
            }