using System; using System.Runtime.InteropServices; namespace FastReport.Fonts { ///////////////////////////////////////////////////////////////////////////////////////////////// // Cmap table ///////////////////////////////////////////////////////////////////////////////////////////////// class CmapTableClass : TrueTypeTable { #region "Type definition" public enum EncodingFormats { ByteEncoding = 0, HighByteMapping = 2, SegmentMapping = 4, TrimmedTable = 6, TrimmedArray = 10, SegmentedCoverage = 12, ManyToOneRangeMapping = 13, UnicodeVariationSequences = 14 } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Table_CMAP { [FieldOffset(0)] public ushort TableVersion; [FieldOffset(2)] public ushort NumSubTables; } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Table_SUBMAP { [FieldOffset(0)] public ushort Platform; [FieldOffset(2)] public ushort EncodingID; [FieldOffset(4)] public uint TableOffset; } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct SegmentMapping { [FieldOffset(0)] public ushort Format; [FieldOffset(2)] public ushort Length; [FieldOffset(4)] public ushort Version; [FieldOffset(6)] public ushort segCountX2; // 2 x segCount. [FieldOffset(8)] public ushort searchRange; // 2 x (2**floor(log2(segCount))) [FieldOffset(10)] public ushort entrySelector; // log2(searchRange/2) [FieldOffset(12)] public ushort rangeShift; // 2 x segCount - searchRange } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct SequentialMapGroup { [FieldOffset(0)] public uint startCharCode; [FieldOffset(4)] public uint searchRange; [FieldOffset(8)] public uint entrySelector; } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Format6 // Trimmed table { [FieldOffset(0)] public ushort format; [FieldOffset(4)] public ushort length; [FieldOffset(6)] public ushort language; [FieldOffset(8)] public ushort startCharCode; [FieldOffset(10)] public ushort numChars; } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Format8 { } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Format10 // Trimmed array { [FieldOffset(0)] public ushort format; [FieldOffset(2)] public ushort reserved; [FieldOffset(4)] public uint length; [FieldOffset(8)] public uint language; [FieldOffset(12)] public uint startCharCode; [FieldOffset(16)] public uint numChars; } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct Format12 { [FieldOffset(0)] public ushort format; // Subtable format; set to 12. [FieldOffset(2)] public ushort reserved; // Reserved; set to 0 [FieldOffset(4)] public uint length; // Byte length of this subtable (including the header) [FieldOffset(8)] public uint language; [FieldOffset(12)] public uint numGroups; // Number of groupings which follow } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct VariationSequences { [FieldOffset(0)] public ushort Format; [FieldOffset(2)] public uint Length; [FieldOffset(6)] public uint NumRecords; } [StructLayout(LayoutKind.Explicit, Pack = 1)] public struct VariationSequenceRecord { [FieldOffset(0)] public byte _b0; // [FieldOffset(1)] public byte _b1; // [FieldOffset(2)] public byte _b2; // [FieldOffset(3)] public uint defaultUVSOffset; // [FieldOffset(7)] public uint nonDefaultUVSOffset; // // Not shure that followin property is allowed in packed structures public uint VariantSelector { get { return (uint)(_b0 | (_b1 << 8) | (_b2 << 16)); } } } #endregion // Format 4: Segment mapping to delta values int segment_count; ushort[] endCount; ushort[] startCount; short[] idDelta; ushort[] idRangeOffset; ushort[] glyphIndexArray; // Format 6 and 10 uint trimmedStat; ushort[] trimmed; // Format 12: Segmented coverage SequentialMapGroup[] mapGroup; private ushort[] LoadCmapSegment(FontStream stream, int segment_count) { ushort[] result = new ushort[segment_count]; for (int i = 0; i < segment_count; i++) result[i] = stream.ReadUInt16(); return result; } private short[] LoadSignedCmapSegment(FontStream stream, int segment_count) { short[] result = new short[segment_count]; for (int i = 0; i < segment_count; i++) { result[i] = stream.ReadInt16(); } return result; } internal void LoadCmapTable(FontStream stream) { // IntPtr subtable_ptr; // IntPtr cmap_ptr = Increment(font, (int)this.Offset); stream.Seek(this.Offset, System.IO.SeekOrigin.Begin); Table_CMAP cmap = new Table_CMAP(); // Marshal.PtrToStructure(cmap_ptr, typeof(Table_CMAP)); cmap.TableVersion = stream.ReadUInt16(); cmap.NumSubTables = stream.ReadUInt16(); // int subtables_count = cmap.NumSubTables; // IntPtr submap_ptr = Increment(cmap_ptr, Marshal.SizeOf(cmap)); // IntPtr payload_ptr; Table_SUBMAP submap; long record_position = stream.Position; for (int j = 0; j < cmap.NumSubTables; j++) { // submap = (Table_SUBMAP)Marshal.PtrToStructure(submap_ptr, typeof(Table_SUBMAP)); // submap_ptr = Increment(submap_ptr, Marshal.SizeOf(submap)); stream.Seek(record_position, System.IO.SeekOrigin.Begin); submap.Platform = stream.ReadUInt16(); // SwapUInt16(submap.Platform); submap.EncodingID = stream.ReadUInt16(); // SwapUInt16(submap.EncodingID); submap.TableOffset = stream.ReadUInt32(); //SwapUInt32(submap.TableOffset); record_position = stream.Position; // --- Skip non microsft unicode charmaps // --- No no no! We will try to parse as much as possible // if ((submap.Platform != 3 || submap.EncodingID != 1)) continue; //IntPtr encode_ptr = Increment(cmap_ptr, (int)submap.TableOffset); stream.Seek(this.Offset + submap.TableOffset, System.IO.SeekOrigin.Begin); ushort format = stream.ReadUInt16(); // SwapUInt16(encode.Format); switch ((EncodingFormats)format) { case EncodingFormats.ByteEncoding: //throw new Exception("TO DO: ByteEncoding cmap format not implemented"); continue; case EncodingFormats.HighByteMapping: //throw new Exception("TO DO: HighByteMapping cmap format not implemented"); continue; case EncodingFormats.SegmentMapping: // payload_ptr = Increment(encode_ptr, Marshal.SizeOf(encode)); SegmentMapping segment = new SegmentMapping(); // Marshal.PtrToStructure(payload_ptr, typeof(SegmentMapping)); segment.Format = format; segment.Length = stream.ReadUInt16(); // SwapUInt16(encode.Length); segment.Version = stream.ReadUInt16(); // SwapUInt16(encode.Version); segment.segCountX2 = stream.ReadUInt16(); // SwapUInt16(segment.segCountX2); // 2 x segCount. segment.searchRange = stream.ReadUInt16(); // SwapUInt16(segment.searchRange); // 2 x (2**floor(log2(segCount))) segment.entrySelector = stream.ReadUInt16(); // SwapUInt16(segment.entrySelector); // log2(searchRange/2) segment.rangeShift = stream.ReadUInt16(); // SwapUInt16(segment.rangeShift); // 2 x segCount - searchRange segment_count = segment.segCountX2 / 2; // Euristic algoritmm for selection best representation. Not sure about it. if (startCount == null || startCount.Length < segment_count) { // payload_ptr = Increment(payload_ptr, Marshal.SizeOf(segment)); endCount = LoadCmapSegment(stream, segment_count); // payload_ptr = Increment(payload_ptr, segment.segCountX2 + sizeof(ushort)); ushort zeropad = stream.ReadUInt16(); startCount = LoadCmapSegment(stream, segment_count); //payload_ptr = Increment(payload_ptr, segment.segCountX2); idDelta = LoadSignedCmapSegment(stream, segment_count); //payload_ptr = Increment(payload_ptr, segment.segCountX2); idRangeOffset = LoadCmapSegment(stream, segment_count); uint index_array_size = (8 + 4 * (uint)segment_count) * 2; index_array_size = (segment.Length - index_array_size) / 2; //payload_ptr = Increment(payload_ptr, segment.segCountX2); //////// int checksize = encode.Length - 3 * segment_count * 2; glyphIndexArray = LoadCmapSegment(stream, (int)index_array_size); #if false string[] debug = new string[segment_count]; for (int z = 0; z < segment_count; z++) { debug[z] = ""+(char)startCount[z]+" - "+(char)endCount[z] +" = " + idDelta[z].ToString() + " & " + idRangeOffset[z].ToString(); } #endif } continue; case EncodingFormats.TrimmedTable: Format6 format6 = new Format6(); // Marshal.PtrToStructure(encode_ptr, typeof(Format6)); format6.format = format; format6.length = stream.ReadUInt16(); // SwapUInt16(format6.length); format6.language = stream.ReadUInt16(); //SwapUInt16(format6.language); format6.startCharCode = stream.ReadUInt16(); // SwapUInt16(format6.startCharCode); format6.numChars = stream.ReadUInt16(); // SwapUInt16(format6.numChars); trimmed = new ushort[format6.numChars]; for (int i = 0; i < format6.numChars; ++i) { trimmed[i] = stream.ReadUInt16(); } trimmedStat = format6.startCharCode; continue; case EncodingFormats.TrimmedArray: Format10 format10 = new Format10(); // Marshal.PtrToStructure(encode_ptr, typeof(Format10)); format10.format = format; format10.reserved = stream.ReadUInt16(); format10.length = stream.ReadUInt32(); // SwapUInt32(format10.length); format10.language = stream.ReadUInt32(); // SwapUInt32(format10.language); format10.startCharCode = stream.ReadUInt32(); // SwapUInt32(format10.startCharCode); format10.numChars = SwapUInt32(format10.numChars); trimmed = new ushort[format10.numChars]; // payload_ptr = Increment(encode_ptr, Marshal.SizeOf(format10)); for (int i = 0; i < format10.numChars; ++i) { trimmed[i] = stream.ReadUInt16(); } trimmedStat = format10.startCharCode; continue; case EncodingFormats.SegmentedCoverage: Format12 format12 = new Format12(); // Marshal.PtrToStructure(encode_ptr, typeof(Format12)); format12.format = format; format12.reserved = stream.ReadUInt16(); format12.length = stream.ReadUInt32(); // SwapUInt32(format12.length); format12.language = stream.ReadUInt32(); // SwapUInt32(format12.language); format12.numGroups = stream.ReadUInt32(); mapGroup = new SequentialMapGroup[format12.numGroups]; // payload_ptr = Increment(encode_ptr, Marshal.SizeOf(format12)); for (int i = 0; i < format12.numGroups; ++i) { // mapGroup[i] = (SequentialMapGroup)Marshal.PtrToStructure(payload_ptr, typeof(SequentialMapGroup)); mapGroup[i].startCharCode = stream.ReadUInt32(); // SwapUInt32(mapGroup[i].startCharCode); mapGroup[i].searchRange = stream.ReadUInt32(); // SwapUInt32(mapGroup[i].searchRange); mapGroup[i].entrySelector = stream.ReadUInt32(); // SwapUInt32(mapGroup[i].entrySelector); // payload_ptr = Increment(payload_ptr, Marshal.SizeOf(mapGroup[0])); } continue; case EncodingFormats.ManyToOneRangeMapping: //throw new Exception("TO DO: ManyToOneRangeMapping cmap format not implemented"); continue; case EncodingFormats.UnicodeVariationSequences: VariationSequences seq_header = new VariationSequences(); // Marshal.PtrToStructure(encode_ptr, typeof(VariationSequences)); seq_header.Format = format; seq_header.Length = stream.ReadUInt32(); // SwapUInt32(seq_header.Length); seq_header.NumRecords = stream.ReadUInt32(); // SwapUInt32(seq_header.NumRecords); // Not parsed, but this table is optional continue; default: throw new Exception("cmap format not known"); } } } internal ushort GetGlyphIndex(ushort ch) { ushort GlyphIDX = 0; if (segment_count != 0) { for (int i = 0; i < segment_count; i++) { if (endCount[i] >= ch) { if (startCount[i] <= ch) { if (idRangeOffset[i] == 0) { GlyphIDX = (ushort)((ch + idDelta[i]) % 65536); } else { int j = (ushort)(idRangeOffset[i] / 2 + (ch - startCount[i]) - (segment_count - i)); GlyphIDX = this.glyphIndexArray[j]; } } return GlyphIDX; } } } if (mapGroup != null && mapGroup.Length != 0) { for (int i = 0; i < mapGroup.Length; ++i) { if (ch >= mapGroup[i].startCharCode && ch <= mapGroup[i].searchRange) { GlyphIDX = (ushort)((uint)ch - mapGroup[i].startCharCode + mapGroup[i].entrySelector); return GlyphIDX; } } } if (trimmed != null && trimmed.Length != 0) { uint idx; if (ch > trimmedStat) { idx = (uint)(ch - trimmedStat); if (idx < trimmed.Length) GlyphIDX = trimmed[idx]; } } return GlyphIDX; } internal ushort GetGlyph32Index(uint ch) { ushort GlyphIDX = 0; if (mapGroup != null && mapGroup.Length != 0) { for (int i = 0; i < mapGroup.Length; ++i) { if (ch >= mapGroup[i].startCharCode && ch <= mapGroup[i].searchRange) { GlyphIDX = (ushort)((uint)ch - mapGroup[i].startCharCode + mapGroup[i].entrySelector); return GlyphIDX; } } } return GlyphIDX; } public CmapTableClass(TrueTypeTable src) : base(src) { } } }