CmapTableClass.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. using System;
  2. using System.Runtime.InteropServices;
  3. namespace FastReport.Fonts
  4. {
  5. /////////////////////////////////////////////////////////////////////////////////////////////////
  6. // Cmap table
  7. /////////////////////////////////////////////////////////////////////////////////////////////////
  8. class CmapTableClass : TrueTypeTable
  9. {
  10. #region "Type definition"
  11. public enum EncodingFormats
  12. {
  13. ByteEncoding = 0,
  14. HighByteMapping = 2,
  15. SegmentMapping = 4,
  16. TrimmedTable = 6,
  17. TrimmedArray = 10,
  18. SegmentedCoverage = 12,
  19. ManyToOneRangeMapping = 13,
  20. UnicodeVariationSequences = 14
  21. }
  22. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  23. public struct Table_CMAP
  24. {
  25. [FieldOffset(0)]
  26. public ushort TableVersion;
  27. [FieldOffset(2)]
  28. public ushort NumSubTables;
  29. }
  30. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  31. public struct Table_SUBMAP
  32. {
  33. [FieldOffset(0)]
  34. public ushort Platform;
  35. [FieldOffset(2)]
  36. public ushort EncodingID;
  37. [FieldOffset(4)]
  38. public uint TableOffset;
  39. }
  40. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  41. public struct Table_Encode
  42. {
  43. [FieldOffset(0)]
  44. public ushort Format;
  45. [FieldOffset(2)]
  46. public ushort Length;
  47. [FieldOffset(4)]
  48. public ushort Version;
  49. }
  50. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  51. public struct SegmentMapping
  52. {
  53. [FieldOffset(0)]
  54. public ushort segCountX2; // 2 x segCount.
  55. [FieldOffset(2)]
  56. public ushort searchRange; // 2 x (2**floor(log2(segCount)))
  57. [FieldOffset(4)]
  58. public ushort entrySelector; // log2(searchRange/2)
  59. [FieldOffset(6)]
  60. public ushort rangeShift; // 2 x segCount - searchRange
  61. }
  62. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  63. public struct SequentialMapGroup
  64. {
  65. [FieldOffset(0)]
  66. public uint startCharCode;
  67. [FieldOffset(4)]
  68. public uint searchRange;
  69. [FieldOffset(8)]
  70. public uint entrySelector;
  71. }
  72. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  73. public struct Format6 // Trimmed table
  74. {
  75. [FieldOffset(0)]
  76. public ushort format;
  77. [FieldOffset(4)]
  78. public ushort length;
  79. [FieldOffset(6)]
  80. public ushort language;
  81. [FieldOffset(8)]
  82. public ushort startCharCode;
  83. [FieldOffset(10)]
  84. public ushort numChars;
  85. }
  86. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  87. public struct Format8
  88. {
  89. }
  90. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  91. public struct Format10 // Trimmed array
  92. {
  93. [FieldOffset(0)]
  94. public ushort format;
  95. [FieldOffset(2)]
  96. public ushort reserved;
  97. [FieldOffset(4)]
  98. public uint length;
  99. [FieldOffset(8)]
  100. public uint language;
  101. [FieldOffset(12)]
  102. public uint startCharCode;
  103. [FieldOffset(16)]
  104. public uint numChars;
  105. }
  106. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  107. public struct Format12
  108. {
  109. [FieldOffset(0)]
  110. public ushort format; // Subtable format; set to 12.
  111. [FieldOffset(2)]
  112. public ushort reserved; // Reserved; set to 0
  113. [FieldOffset(4)]
  114. public uint length; // Byte length of this subtable (including the header)
  115. [FieldOffset(8)]
  116. public uint language;
  117. [FieldOffset(12)]
  118. public uint numGroups; // Number of groupings which follow
  119. }
  120. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  121. public struct VariationSequences
  122. {
  123. [FieldOffset(0)]
  124. public ushort Format;
  125. [FieldOffset(2)]
  126. public uint Length;
  127. [FieldOffset(6)]
  128. public uint NumRecords;
  129. }
  130. [StructLayout(LayoutKind.Explicit, Pack = 1)]
  131. public struct VariationSequenceRecord
  132. {
  133. [FieldOffset(0)]
  134. public byte _b0; //
  135. [FieldOffset(1)]
  136. public byte _b1; //
  137. [FieldOffset(2)]
  138. public byte _b2; //
  139. [FieldOffset(3)]
  140. public uint defaultUVSOffset; //
  141. [FieldOffset(7)]
  142. public uint nonDefaultUVSOffset; //
  143. // Not shure that followin property is allowed in packed structures
  144. public uint VariantSelector { get { return (uint)(_b0 | (_b1 << 8) | (_b2 << 16)); } }
  145. }
  146. #endregion
  147. // Format 4: Segment mapping to delta values
  148. int segment_count;
  149. ushort[] endCount;
  150. ushort[] startCount;
  151. short[] idDelta;
  152. ushort[] idRangeOffset;
  153. ushort[] glyphIndexArray;
  154. // Format 6 and 10
  155. uint trimmedStat;
  156. ushort[] trimmed;
  157. // Format 12: Segmented coverage
  158. SequentialMapGroup[] mapGroup;
  159. private ushort[] LoadCmapSegment(IntPtr segment_ptr, int segment_count, short[] temp_mem)
  160. {
  161. ushort[] result = new ushort[segment_count];
  162. Marshal.Copy(segment_ptr, temp_mem, 0, segment_count);
  163. for (int i = 0; i < segment_count; i++)
  164. {
  165. byte[] buf = BitConverter.GetBytes(temp_mem[i]);
  166. if (BitConverter.IsLittleEndian)
  167. Array.Reverse(buf);
  168. result[i] = BitConverter.ToUInt16(buf, 0);
  169. }
  170. return result;
  171. }
  172. private short[] LoadSignedCmapSegment(IntPtr segment_ptr, int segment_count, short[] temp_mem)
  173. {
  174. short[] result = new short[segment_count];
  175. Marshal.Copy(segment_ptr, temp_mem, 0, segment_count);
  176. for (int i = 0; i < segment_count; i++)
  177. {
  178. result[i] = SwapInt16(temp_mem[i]);
  179. }
  180. return result;
  181. }
  182. internal void LoadCmapTable(IntPtr font)
  183. {
  184. IntPtr subtable_ptr;
  185. IntPtr cmap_ptr = Increment(font, (int)this.Offset);
  186. Table_CMAP cmap = (Table_CMAP)Marshal.PtrToStructure(cmap_ptr, typeof(Table_CMAP));
  187. int subtables_count = SwapUInt16(cmap.NumSubTables);
  188. IntPtr submap_ptr = Increment(cmap_ptr, Marshal.SizeOf(cmap));
  189. IntPtr payload_ptr;
  190. Table_SUBMAP submap;
  191. for (int j = 0; j < subtables_count; j++)
  192. {
  193. submap = (Table_SUBMAP)Marshal.PtrToStructure(submap_ptr, typeof(Table_SUBMAP));
  194. submap_ptr = Increment(submap_ptr, Marshal.SizeOf(submap));
  195. submap.Platform = SwapUInt16(submap.Platform);
  196. submap.EncodingID = SwapUInt16(submap.EncodingID);
  197. submap.TableOffset = SwapUInt32(submap.TableOffset);
  198. // --- Skip non microsft unicode charmaps
  199. // --- No no no! We will try to parse as much as ppossible
  200. // if ((submap.Platform != 3 || submap.EncodingID != 1)) continue;
  201. IntPtr encode_ptr = Increment(cmap_ptr, (int)submap.TableOffset);
  202. Table_Encode encode = (Table_Encode)Marshal.PtrToStructure(encode_ptr, typeof(Table_Encode));
  203. subtable_ptr = encode_ptr;
  204. encode.Format = SwapUInt16(encode.Format);
  205. encode.Length = SwapUInt16(encode.Length);
  206. encode.Version = SwapUInt16(encode.Version);
  207. switch ((EncodingFormats)encode.Format)
  208. {
  209. case EncodingFormats.ByteEncoding:
  210. //throw new Exception("TO DO: ByteEncoding cmap format not implemented");
  211. continue;
  212. case EncodingFormats.HighByteMapping:
  213. //throw new Exception("TO DO: HighByteMapping cmap format not implemented");
  214. continue;
  215. case EncodingFormats.SegmentMapping:
  216. payload_ptr = Increment(encode_ptr, Marshal.SizeOf(encode));
  217. SegmentMapping segment = (SegmentMapping)Marshal.PtrToStructure(payload_ptr, typeof(SegmentMapping));
  218. segment.segCountX2 = SwapUInt16(segment.segCountX2); // 2 x segCount.
  219. segment.searchRange = SwapUInt16(segment.searchRange); // 2 x (2**floor(log2(segCount)))
  220. segment.entrySelector = SwapUInt16(segment.entrySelector); // log2(searchRange/2)
  221. segment.rangeShift = SwapUInt16(segment.rangeShift); // 2 x segCount - searchRange
  222. segment_count = segment.segCountX2 / 2;
  223. // Euristic algoritmm for selection best representation. Not sure about it.
  224. if (startCount == null || startCount.Length < segment_count)
  225. {
  226. short[] toFix = new short[segment_count];
  227. payload_ptr = Increment(payload_ptr, Marshal.SizeOf(segment));
  228. endCount = LoadCmapSegment(payload_ptr, segment_count, toFix);
  229. payload_ptr = Increment(payload_ptr, segment.segCountX2 + sizeof(ushort));
  230. startCount = LoadCmapSegment(payload_ptr, segment_count, toFix);
  231. payload_ptr = Increment(payload_ptr, segment.segCountX2);
  232. idDelta = LoadSignedCmapSegment(payload_ptr, segment_count, toFix);
  233. payload_ptr = Increment(payload_ptr, segment.segCountX2);
  234. idRangeOffset = LoadCmapSegment(payload_ptr, segment_count, toFix);
  235. toFix = null;
  236. uint index_array_size = (8 + 4 * (uint)segment_count) * 2;
  237. index_array_size = (encode.Length - index_array_size) / 2;
  238. payload_ptr = Increment(payload_ptr, segment.segCountX2);
  239. // int checksize = encode.Length - 3 * segment_count * 2;
  240. toFix = new short[index_array_size];
  241. glyphIndexArray = LoadCmapSegment(payload_ptr, (int)index_array_size, toFix);
  242. toFix = null;
  243. #if false
  244. string[] debug = new string[segment_count];
  245. for (int z = 0; z < segment_count; z++)
  246. {
  247. debug[z] = ""+(char)startCount[z]+" - "+(char)endCount[z] +" = " + idDelta[z].ToString() + " & " + idRangeOffset[z].ToString();
  248. }
  249. #endif
  250. }
  251. continue;
  252. case EncodingFormats.TrimmedTable:
  253. Format6 format6 = (Format6)Marshal.PtrToStructure(encode_ptr, typeof(Format6));
  254. format6.length = SwapUInt16(format6.length);
  255. format6.language = SwapUInt16(format6.language);
  256. format6.startCharCode = SwapUInt16(format6.startCharCode);
  257. format6.numChars = SwapUInt16(format6.numChars);
  258. trimmed = new ushort[format6.numChars];
  259. payload_ptr = Increment(encode_ptr, Marshal.SizeOf(format6));
  260. short[] temp_6 = new short[format6.numChars];
  261. Marshal.Copy(payload_ptr, temp_6, 0, (int)format6.numChars);
  262. for (int i = 0; i < format6.numChars; ++i)
  263. {
  264. byte[] buf = BitConverter.GetBytes(temp_6[i]);
  265. if (BitConverter.IsLittleEndian)
  266. Array.Reverse(buf);
  267. trimmed[i] = BitConverter.ToUInt16(buf, 0);
  268. }
  269. temp_6 = null;
  270. trimmedStat = format6.startCharCode;
  271. continue;
  272. case EncodingFormats.TrimmedArray:
  273. Format10 format10 = (Format10)Marshal.PtrToStructure(encode_ptr, typeof(Format10));
  274. format10.length = SwapUInt32(format10.length);
  275. format10.language = SwapUInt32(format10.language);
  276. format10.startCharCode = SwapUInt32(format10.startCharCode);
  277. format10.numChars = SwapUInt32(format10.numChars);
  278. trimmed = new ushort[format10.numChars];
  279. payload_ptr = Increment(encode_ptr, Marshal.SizeOf(format10));
  280. short[] temp_10 = new short[format10.numChars];
  281. Marshal.Copy(payload_ptr, temp_10, 0, (int)format10.numChars);
  282. for (int i = 0; i < format10.numChars; ++i)
  283. {
  284. byte[] buf = BitConverter.GetBytes(temp_10[i]);
  285. if (BitConverter.IsLittleEndian)
  286. Array.Reverse(buf);
  287. trimmed[i] = BitConverter.ToUInt16(buf, 0);
  288. }
  289. temp_10 = null;
  290. trimmedStat = format10.startCharCode;
  291. continue;
  292. case EncodingFormats.SegmentedCoverage:
  293. Format12 format12 = (Format12)Marshal.PtrToStructure(encode_ptr, typeof(Format12));
  294. format12.length = SwapUInt32(format12.length);
  295. format12.language = SwapUInt32(format12.language);
  296. format12.numGroups = SwapUInt32(format12.numGroups);
  297. mapGroup = new SequentialMapGroup[format12.numGroups];
  298. payload_ptr = Increment(encode_ptr, Marshal.SizeOf(format12));
  299. for (int i = 0; i < format12.numGroups; ++i)
  300. {
  301. mapGroup[i] = (SequentialMapGroup)Marshal.PtrToStructure(payload_ptr, typeof(SequentialMapGroup));
  302. mapGroup[i].startCharCode = SwapUInt32(mapGroup[i].startCharCode);
  303. mapGroup[i].searchRange = SwapUInt32(mapGroup[i].searchRange);
  304. mapGroup[i].entrySelector = SwapUInt32(mapGroup[i].entrySelector);
  305. payload_ptr = Increment(payload_ptr, Marshal.SizeOf(mapGroup[0]));
  306. }
  307. continue;
  308. case EncodingFormats.ManyToOneRangeMapping:
  309. //throw new Exception("TO DO: ManyToOneRangeMapping cmap format not implemented");
  310. continue;
  311. case EncodingFormats.UnicodeVariationSequences:
  312. VariationSequences seq_header = (VariationSequences)Marshal.PtrToStructure(encode_ptr, typeof(VariationSequences));
  313. seq_header.Length = SwapUInt32(seq_header.Length);
  314. seq_header.NumRecords = SwapUInt32(seq_header.NumRecords);
  315. // Not parsed, but this table is optional
  316. continue;
  317. default:
  318. throw new Exception("cmap format not known");
  319. }
  320. }
  321. }
  322. internal ushort GetGlyphIndex(ushort ch)
  323. {
  324. ushort GlyphIDX = 0;
  325. if (segment_count != 0)
  326. {
  327. for (int i = 0; i < segment_count; i++)
  328. {
  329. if (endCount[i] >= ch)
  330. {
  331. if (startCount[i] <= ch)
  332. {
  333. if (idRangeOffset[i] == 0)
  334. {
  335. GlyphIDX = (ushort)((ch + idDelta[i]) % 65536);
  336. }
  337. else
  338. {
  339. int j = (ushort)(idRangeOffset[i] / 2 + (ch - startCount[i]) - (segment_count - i));
  340. GlyphIDX = this.glyphIndexArray[j];
  341. }
  342. }
  343. return GlyphIDX;
  344. }
  345. }
  346. }
  347. if (mapGroup != null && mapGroup.Length != 0)
  348. {
  349. for (int i = 0; i < mapGroup.Length; ++i)
  350. {
  351. if (ch >= mapGroup[i].startCharCode && ch <= mapGroup[i].searchRange)
  352. {
  353. GlyphIDX = (ushort)((uint)ch - mapGroup[i].startCharCode + mapGroup[i].entrySelector);
  354. return GlyphIDX;
  355. }
  356. }
  357. }
  358. if (trimmed != null && trimmed.Length != 0)
  359. {
  360. uint idx;
  361. if (ch > trimmedStat)
  362. {
  363. idx = (uint)(ch - trimmedStat);
  364. if (idx < trimmed.Length)
  365. GlyphIDX = trimmed[idx];
  366. }
  367. }
  368. return GlyphIDX;
  369. }
  370. public CmapTableClass(TrueTypeTable src) : base(src) { }
  371. }
  372. }