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