public static CMapTable Load(BinaryReader reader) { ushort version = reader.ReadUInt16(); ushort numTables = reader.ReadUInt16(); var encodings = new EncodingRecord[numTables]; for (int i = 0; i < numTables; i++) { encodings[i] = EncodingRecord.Read(reader); } // foreach encoding we move forward looking for th subtables var tables = new List <CMapSubTable>(numTables); foreach (IGrouping <uint, EncodingRecord> encoding in encodings.Where(x => x.PlatformID == PlatformIDs.Windows).GroupBy(x => x.Offset)) { reader.Seek(encoding.Key, System.IO.SeekOrigin.Begin); ushort subTypeTableFormat = reader.ReadUInt16(); switch (subTypeTableFormat) { case 0: tables.AddRange(Format0SubTable.Load(encoding, reader)); break; case 4: tables.AddRange(Format4SubTable.Load(encoding, reader)); break; } } return(new CMapTable(tables.ToArray())); }
[InlineData(500, 0)] //not in range public void GetCharcter(int src, int expected) { // segCountX2: 8 // searchRange: 8 // entrySelector: 4 // rangeShift: 0 // endCode: 20 90 480 0xffff // reservedPad: 0 // startCode: 10 30 153 0xffff // dDelta: -9 -18 -27 1 // idRangeOffset: 0 0 0 0 ushort[] glyphs = Enumerable.Range(0, expected).Select(x => (ushort)x).ToArray(); Format4SubTable table = new Format4SubTable( 0, PlatformIDs.Windows, 0, new[] { new Format4SubTable.Segment(0, 20, 10, -9, 0), new Format4SubTable.Segment(1, 90, 30, -18, 0), new Format4SubTable.Segment(2, 480, 153, -27, 0), }, glyphs); ushort id = table.GetGlyphId((char)src); Assert.Equal(expected, id); }
public void LoadFormat4() { BinaryWriter writer = new BinaryWriter(); //int subtableCount = 1; writer.WriteCMapSubTable(new SixLabors.Fonts.Tables.General.CMap.Format4SubTable(0, PlatformIDs.Windows, 2, new[] { new Format4SubTable.Segment(0, 1, 2, 3, 4) }, new ushort[] { 1, 2, 3, 4, 5, 6, 7, 8 })); BinaryReader reader = writer.GetReader(); ushort format = reader.ReadUInt16(); // read format before we pass along as thats whet the cmap table does Assert.Equal(4, format); Format4SubTable table = Format4SubTable.Load(new[] { new EncodingRecord(PlatformIDs.Windows, 2, 0) }, reader).Single(); Assert.Equal(0, table.Language); Assert.Equal(PlatformIDs.Windows, table.Platform); Assert.Equal(2, table.Encoding); Assert.Equal(new ushort[] { 1, 2, 3, 4, 5, 6, 7, 8 }, table.GlyphIds); Assert.Equal(1, table.Segments.Length); Format4SubTable.Segment seg = table.Segments[0]; Assert.Equal(0, seg.Index); Assert.Equal(1, seg.End); Assert.Equal(2, seg.Start); Assert.Equal(3, seg.Delta); Assert.Equal(4, seg.Offset); }
public void Format4SubTableWithSegmentsHasOffByOneWhenOverflowing() { Segment[] segments = new[] { new Segment( 0, ushort.MaxValue, // end ushort.MinValue, // start of range short.MaxValue, // delta 0) // zero to force correctly tested codepath }; var tbl = new Format4SubTable( 0, WellKnownIds.PlatformIDs.Windows, 0, segments, null); const int delta = short.MaxValue + 2; // extra 2 to handle the difference between ushort and short when offsettings const int codePoint = delta + 5; Assert.True(tbl.TryGetGlyphId(new CodePoint(codePoint), out ushort gid)); Assert.Equal(5, gid); }
public static void WriteCMapSubTable(this BinaryWriter writer, Format4SubTable subtable) { if (subtable == null) { return; } // 'cmap' Subtable Format 4: // Type | Name | Description // -------|----------------------------|------------------------------------------------------------------------ // uint16 | format | Format number is set to 4. // uint16 | length | This is the length in bytes of the subtable. // uint16 | language | Please see “Note on the language field in 'cmap' subtables“ in this document. // uint16 | segCountX2 | 2 x segCount. // uint16 | searchRange | 2 x (2**floor(log2(segCount))) // uint16 | entrySelector | log2(searchRange/2) // uint16 | rangeShift | 2 x segCount - searchRange // uint16 | endCount[segCount] | End characterCode for each segment, last=0xFFFF. // uint16 | reservedPad | Set to 0. // uint16 | startCount[segCount] | Start character code for each segment. // int16 | idDelta[segCount] | Delta for all character codes in segment. // uint16 | idRangeOffset[segCount] | Offsets into glyphIdArray or 0 // uint16 | glyphIdArray[ ] | Glyph index array (arbitrary length) writer.WriteUInt16(4); writer.WriteUInt16((ushort)subtable.DataLength()); writer.WriteUInt16(subtable.Language); int segCount = subtable.Segments.Length; writer.WriteUInt16((ushort)(subtable.Segments.Length * 2)); double searchRange = Math.Pow(2, Math.Floor(Math.Log(segCount, 2))); writer.WriteUInt16((ushort)searchRange); double entrySelector = Math.Log(searchRange / 2, 2); writer.WriteUInt16((ushort)entrySelector); double rangeShift = (2 * segCount) - searchRange; writer.WriteUInt16((ushort)rangeShift); foreach (Format4SubTable.Segment seg in subtable.Segments) { writer.WriteUInt16(seg.End); } writer.WriteUInt16(0); foreach (Format4SubTable.Segment seg in subtable.Segments) { writer.WriteUInt16(seg.Start); } foreach (Format4SubTable.Segment seg in subtable.Segments) { writer.WriteInt16(seg.Delta); } foreach (Format4SubTable.Segment seg in subtable.Segments) { writer.WriteUInt16(seg.Offset); } foreach (ushort c in subtable.GlyphIds) { writer.WriteUInt16(c); } }