Cmap.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * Copyright (c) 2020, Srimanta Barua <srimanta.barua1@gmail.com>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Optional.h>
  7. #include <LibTTF/Cmap.h>
  8. namespace TTF {
  9. extern u16 be_u16(const u8* ptr);
  10. extern u32 be_u32(const u8* ptr);
  11. extern i16 be_i16(const u8* ptr);
  12. Cmap::Subtable::Platform Cmap::Subtable::platform_id() const
  13. {
  14. switch (m_raw_platform_id) {
  15. case 0:
  16. return Platform::Unicode;
  17. case 1:
  18. return Platform::Macintosh;
  19. case 3:
  20. return Platform::Windows;
  21. case 4:
  22. return Platform::Custom;
  23. default:
  24. VERIFY_NOT_REACHED();
  25. }
  26. }
  27. Cmap::Subtable::Format Cmap::Subtable::format() const
  28. {
  29. switch (be_u16(m_slice.offset_pointer(0))) {
  30. case 0:
  31. return Format::ByteEncoding;
  32. case 2:
  33. return Format::HighByte;
  34. case 4:
  35. return Format::SegmentToDelta;
  36. case 6:
  37. return Format::TrimmedTable;
  38. case 8:
  39. return Format::Mixed16And32;
  40. case 10:
  41. return Format::TrimmedArray;
  42. case 12:
  43. return Format::SegmentedCoverage;
  44. case 13:
  45. return Format::ManyToOneRange;
  46. case 14:
  47. return Format::UnicodeVariationSequences;
  48. default:
  49. VERIFY_NOT_REACHED();
  50. }
  51. }
  52. u32 Cmap::num_subtables() const
  53. {
  54. return be_u16(m_slice.offset_pointer((u32)Offsets::NumTables));
  55. }
  56. Optional<Cmap::Subtable> Cmap::subtable(u32 index) const
  57. {
  58. if (index >= num_subtables()) {
  59. return {};
  60. }
  61. u32 record_offset = (u32)Sizes::TableHeader + index * (u32)Sizes::EncodingRecord;
  62. u16 platform_id = be_u16(m_slice.offset_pointer(record_offset));
  63. u16 encoding_id = be_u16(m_slice.offset_pointer(record_offset + (u32)Offsets::EncodingRecord_EncodingID));
  64. u32 subtable_offset = be_u32(m_slice.offset_pointer(record_offset + (u32)Offsets::EncodingRecord_Offset));
  65. VERIFY(subtable_offset < m_slice.size());
  66. auto subtable_slice = ReadonlyBytes(m_slice.offset_pointer(subtable_offset), m_slice.size() - subtable_offset);
  67. return Subtable(subtable_slice, platform_id, encoding_id);
  68. }
  69. // FIXME: This only handles formats 4 (SegmentToDelta) and 12 (SegmentedCoverage) for now.
  70. u32 Cmap::Subtable::glyph_id_for_code_point(u32 code_point) const
  71. {
  72. switch (format()) {
  73. case Format::SegmentToDelta:
  74. return glyph_id_for_code_point_table_4(code_point);
  75. case Format::SegmentedCoverage:
  76. return glyph_id_for_code_point_table_12(code_point);
  77. default:
  78. return 0;
  79. }
  80. }
  81. u32 Cmap::Subtable::glyph_id_for_code_point_table_4(u32 code_point) const
  82. {
  83. u32 segcount_x2 = be_u16(m_slice.offset_pointer((u32)Table4Offsets::SegCountX2));
  84. if (m_slice.size() < segcount_x2 * (u32)Table4Sizes::NonConstMultiplier + (u32)Table4Sizes::Constant) {
  85. return 0;
  86. }
  87. for (u32 offset = 0; offset < segcount_x2; offset += 2) {
  88. u32 end_code_point = be_u16(m_slice.offset_pointer((u32)Table4Offsets::EndConstBase + offset));
  89. if (code_point > end_code_point) {
  90. continue;
  91. }
  92. u32 start_code_point = be_u16(m_slice.offset_pointer((u32)Table4Offsets::StartConstBase + segcount_x2 + offset));
  93. if (code_point < start_code_point) {
  94. break;
  95. }
  96. u32 delta = be_u16(m_slice.offset_pointer((u32)Table4Offsets::DeltaConstBase + segcount_x2 * 2 + offset));
  97. u32 range = be_u16(m_slice.offset_pointer((u32)Table4Offsets::RangeConstBase + segcount_x2 * 3 + offset));
  98. if (range == 0) {
  99. return (code_point + delta) & 0xffff;
  100. }
  101. u32 glyph_offset = (u32)Table4Offsets::GlyphOffsetConstBase + segcount_x2 * 3 + offset + range + (code_point - start_code_point) * 2;
  102. VERIFY(glyph_offset + 2 <= m_slice.size());
  103. return (be_u16(m_slice.offset_pointer(glyph_offset)) + delta) & 0xffff;
  104. }
  105. return 0;
  106. }
  107. u32 Cmap::Subtable::glyph_id_for_code_point_table_12(u32 code_point) const
  108. {
  109. u32 num_groups = be_u32(m_slice.offset_pointer((u32)Table12Offsets::NumGroups));
  110. VERIFY(m_slice.size() >= (u32)Table12Sizes::Header + (u32)Table12Sizes::Record * num_groups);
  111. for (u32 offset = 0; offset < num_groups * (u32)Table12Sizes::Record; offset += (u32)Table12Sizes::Record) {
  112. u32 start_code_point = be_u32(m_slice.offset_pointer((u32)Table12Offsets::Record_StartCode + offset));
  113. if (code_point < start_code_point) {
  114. break;
  115. }
  116. u32 end_code_point = be_u32(m_slice.offset_pointer((u32)Table12Offsets::Record_EndCode + offset));
  117. if (code_point > end_code_point) {
  118. continue;
  119. }
  120. u32 glyph_offset = be_u32(m_slice.offset_pointer((u32)Table12Offsets::Record_StartGlyph + offset));
  121. return code_point - start_code_point + glyph_offset;
  122. }
  123. return 0;
  124. }
  125. u32 Cmap::glyph_id_for_code_point(u32 code_point) const
  126. {
  127. auto opt_subtable = subtable(m_active_index);
  128. if (!opt_subtable.has_value()) {
  129. return 0;
  130. }
  131. auto subtable = opt_subtable.value();
  132. return subtable.glyph_id_for_code_point(code_point);
  133. }
  134. Optional<Cmap> Cmap::from_slice(const ReadonlyBytes& slice)
  135. {
  136. if (slice.size() < (size_t)Sizes::TableHeader) {
  137. return {};
  138. }
  139. return Cmap(slice);
  140. }
  141. }