KerningTableClass.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. using System;
  2. using System.Collections;
  3. using System.Runtime.InteropServices;
  4. #pragma warning disable CS3001, CS3002, CS3003, CS1591
  5. namespace FastReport.Fonts
  6. {
  7. /// <summary>
  8. /// Kerning table
  9. /// </summary>
  10. public class KerningTableClass : TrueTypeTable
  11. {
  12. #region "Type definition"
  13. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  14. public struct KerningTableHeader
  15. {
  16. public ushort Version;
  17. public ushort nTables;
  18. }
  19. internal class KerningSubtableClass : TTF_Helpers
  20. {
  21. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  22. public struct CommonKerningHeader
  23. {
  24. public ushort Version;
  25. public ushort Length;
  26. public ushort Coverage;
  27. }
  28. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  29. public struct FormatZero
  30. {
  31. public ushort nPairs; // This gives the number of kerning pairs in the table.
  32. public ushort searchRange; // The largest power of two less than or equal to the value of nPairs, multiplied by the size in bytes of an entry in the table.
  33. public ushort entrySelector; // This is calculated as log2 of the largest power of two less than or equal to the value of nPairs. This value indicates how many iterations of the search loop will have to be made. (For example, in a list of eight items, there would have to be three iterations of the loop).
  34. public ushort rangeShift; // The value of nPairs minus the largest power of two less than or equal to nPairs, and then multiplied by the size in bytes of an entry in the table.
  35. }
  36. public CommonKerningHeader common_header;
  37. FormatZero format_zero;
  38. [StructLayout(LayoutKind.Sequential, Pack = 1)]
  39. public struct Kern_Zero_Pair
  40. {
  41. public ushort left; /* index of left glyph in pair */
  42. public ushort right; /* index of right glyph in pair */
  43. public short value; /* kerning value */
  44. };
  45. public Kern_Zero_Pair[] kerning0_pairs;
  46. public int Length { get { return common_header.Length; } }
  47. public KerningSubtableClass(IntPtr kerning_table_ptr)
  48. {
  49. common_header = (CommonKerningHeader)Marshal.PtrToStructure(kerning_table_ptr, typeof(CommonKerningHeader));
  50. common_header.Length = SwapUInt16(common_header.Length);
  51. common_header.Coverage = SwapUInt16(common_header.Coverage);
  52. kerning_table_ptr = Increment(kerning_table_ptr, Marshal.SizeOf(common_header));
  53. if (common_header.Coverage == 1)
  54. {
  55. format_zero = (FormatZero)Marshal.PtrToStructure(kerning_table_ptr, typeof(FormatZero));
  56. format_zero.nPairs = SwapUInt16(format_zero.nPairs);
  57. format_zero.searchRange = SwapUInt16(format_zero.searchRange);
  58. format_zero.entrySelector = SwapUInt16(format_zero.entrySelector);
  59. format_zero.rangeShift = SwapUInt16(format_zero.rangeShift);
  60. kerning_table_ptr = Increment(kerning_table_ptr, Marshal.SizeOf(format_zero));
  61. kerning0_pairs = new Kern_Zero_Pair[format_zero.nPairs];
  62. for (int i = 0; i < format_zero.nPairs; i++)
  63. {
  64. kerning0_pairs[i] = (Kern_Zero_Pair)Marshal.PtrToStructure(kerning_table_ptr, typeof(Kern_Zero_Pair));
  65. kerning0_pairs[i].left = SwapUInt16(kerning0_pairs[i].left);
  66. kerning0_pairs[i].right = SwapUInt16(kerning0_pairs[i].right);
  67. kerning0_pairs[i].value = SwapInt16(kerning0_pairs[i].value);
  68. kerning_table_ptr = Increment(kerning_table_ptr, Marshal.SizeOf(typeof(Kern_Zero_Pair)));
  69. }
  70. }
  71. else if (common_header.Coverage == 0x8000)
  72. {
  73. // new Apple-dialect of kerning table
  74. }
  75. else if (common_header.Coverage == 0x0000)
  76. {
  77. // classic Apple-dialect of kerning table
  78. }
  79. else
  80. {
  81. throw new Exception("An unknown dialect of kerning table");
  82. }
  83. }
  84. }
  85. #endregion
  86. public KerningTableHeader kerning_table_header;
  87. private ArrayList kerning_subtables_collection;
  88. internal KerningSubtableClass format_zero_kern = null;
  89. private void ChangeEndian()
  90. {
  91. kerning_table_header.nTables = SwapUInt16(kerning_table_header.nTables);
  92. }
  93. internal override void Load(IntPtr font)
  94. {
  95. IntPtr kerning_table_ptr = Increment(font, (int)this.Offset);
  96. kerning_table_header = (KerningTableHeader)Marshal.PtrToStructure(kerning_table_ptr, typeof(KerningTableHeader));
  97. ChangeEndian();
  98. IntPtr subtable_ptr = Increment(kerning_table_ptr, Marshal.SizeOf(kerning_table_header));
  99. for (int i = 0; i < kerning_table_header.nTables; i++)
  100. {
  101. KerningSubtableClass subtable = new KerningSubtableClass(subtable_ptr);
  102. if(subtable.common_header.Coverage == 1)
  103. {
  104. this.format_zero_kern = subtable;
  105. }
  106. kerning_subtables_collection.Add(subtable);
  107. subtable_ptr = Increment(subtable_ptr, subtable.Length);
  108. }
  109. }
  110. internal short TryKerning(ushort left, ushort right)
  111. {
  112. long target_idx = (((long)left) << 16) + right;
  113. short min, max, middle;
  114. short new_min = 0;
  115. short new_max = (short)(this.format_zero_kern.kerning0_pairs.Length - 1);
  116. short fix_kerning = 0;
  117. do
  118. {
  119. min = new_min;
  120. max = new_max;
  121. middle = (short)(max - ((max - min) >> 1));
  122. if (this.format_zero_kern.kerning0_pairs.Length == 0)
  123. break;
  124. long current_idx = (this.format_zero_kern.kerning0_pairs[middle].left << 16) +
  125. this.format_zero_kern.kerning0_pairs[middle].right;
  126. if (target_idx == current_idx)
  127. {
  128. fix_kerning += this.format_zero_kern.kerning0_pairs[middle].value;
  129. break;
  130. }
  131. else if (target_idx < current_idx)
  132. {
  133. if (middle == min)
  134. break;
  135. new_max = (short)(middle - 1);
  136. }
  137. else
  138. {
  139. if (middle == max)
  140. break;
  141. new_min = (short)(middle + 1);
  142. }
  143. } while (min < max);
  144. return fix_kerning;
  145. }
  146. public KerningTableClass(TrueTypeTable src) : base(src)
  147. {
  148. kerning_subtables_collection = new ArrayList();
  149. }
  150. }
  151. }
  152. #pragma warning restore