HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
otype_cmap.hpp
1// Copyright Take Vos 2023.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "otype_utilities.hpp"
8#include "font_char_map.hpp"
9
10namespace hi { inline namespace v1 {
11
12[[nodiscard]] inline std::span<std::byte const>
13otype_cmap_find(std::span<std::byte const> bytes, uint16_t platform_id, uint16_t platform_specific_id)
14{
15 struct header_type {
16 big_uint16_buf_t version;
17 big_uint16_buf_t num_tables;
18 };
19
20 struct entry_type {
21 big_uint16_buf_t platform_id;
22 big_uint16_buf_t platform_specific_id;
23 big_uint32_buf_t offset;
24 };
25
26 std::size_t offset = 0;
27
28 hilet& header = implicit_cast<header_type>(offset, bytes);
29 hi_check(*header.version == 0, "CMAP version is not 0");
30
31 hilet entries = implicit_cast<entry_type>(offset, bytes, *header.num_tables);
32
33 auto key = (truncate<uint32_t>(platform_id) << 16) | truncate<uint32_t>(platform_specific_id);
34 if (hilet entry = fast_binary_search_eq<std::endian::big>(entries, key)) {
35 return hi_check_subspan(bytes, *entry->offset);
36 } else {
37 return {};
38 }
39}
40
41[[nodiscard]] inline font_char_map otype_cmap_parse_map_4(std::span<std::byte const> over_sized_bytes)
42{
43 struct header_type {
44 big_uint16_buf_t format;
45 big_uint16_buf_t length;
46 big_uint16_buf_t language;
47 big_uint16_buf_t seg_count_x2;
48 big_uint16_buf_t search_range;
49 big_uint16_buf_t entry_selector;
50 big_uint16_buf_t range_shift;
51 };
52
53 auto offset = 0_uz;
54 hilet& header = implicit_cast<header_type>(offset, over_sized_bytes);
55 hi_axiom(*header.format == 4);
56 hilet length = *header.length;
57 hilet bytes = hi_check_subspan(over_sized_bytes, 0, length);
58
59 hilet seg_count = *header.seg_count_x2 / 2;
60
61 hilet end_codes = implicit_cast<big_uint16_buf_t>(offset, bytes, seg_count);
62 offset += sizeof(uint16_t); // reservedPad
63 hilet start_codes = implicit_cast<big_uint16_buf_t>(offset, bytes, seg_count);
64
65 hilet id_deltas = implicit_cast<big_uint16_buf_t>(offset, bytes, seg_count);
66
67 hilet id_range_offsets = implicit_cast<big_uint16_buf_t>(offset, bytes, seg_count);
68
69 hilet glyph_id_array_count = (bytes.size() - offset) / sizeof(big_uint16_buf_t);
70 hilet glyph_id_array = implicit_cast<big_uint16_buf_t>(offset, bytes, glyph_id_array_count);
71
72 auto r = font_char_map{};
73 r.reserve(seg_count);
74 auto prev_end_code_point = char32_t{0};
75 for (auto i = 0_uz; i != seg_count; ++i) {
76 hilet end_code_point = char_cast<char32_t>(*end_codes[i]);
77 hilet start_code_point = char_cast<char32_t>(*start_codes[i]);
78
79 hi_check(start_code_point <= end_code_point, "'cmap' subtable 4, start code-point must come before end code-point.");
81 i == 0 or prev_end_code_point < start_code_point,
82 "'cmap' subtable 4, all entries must be non-overlapping and ordered.");
83
84 if (start_code_point == 0xffff and end_code_point == 0xffff) {
85 // The last entry is a single character explicit terminator and does not need to be added.
86 break;
87 }
88
89 auto id_range_offset = wide_cast<size_t>(*id_range_offsets[i]);
90 if (id_range_offset == 0) {
91 // Simple modulo 65536 delta on the start_code_point to get a glyph_id.
92 auto start_glyph_id = *id_deltas[i];
93 start_glyph_id += char_cast<uint16_t>(start_code_point);
94
96 start_glyph_id + (end_code_point - start_code_point) + 1_uz < 0xffff,
97 "'cmap' subtable 4, glyph_id must be in range 0 to 0xfffe.");
98 r.add(start_code_point, end_code_point, start_glyph_id);
99
100 } else {
101 // The formula from the specification:
102 // `glyphIndex = *( &idRangeOffset[i] + idRangeOffset[i] / 2 + (c - startCode[i]) )`
103
104 // Get the offset to the glyph_id_array.
105 id_range_offset /= sizeof(big_uint16_buf_t);
106 // Get the index in the glyph_index_table. By subtracting the rest of the id_range_offsets table.
107 id_range_offset -= seg_count - i;
108
109 // When using the glyph_index_table, add glyphs one by one.
110 hilet code_point_count = end_code_point - start_code_point + 1;
111 for (auto j = 0_uz; j != code_point_count; ++j) {
112 hilet code_point = char_cast<char32_t>(start_code_point + j);
113
114 hilet glyph_id = *hi_check_at(glyph_id_array, id_range_offset + j);
115 hi_check(glyph_id < 0xfffe, "'cmap' subtable 4, glyph_id must be in range 0 to 0xfffe.");
116 r.add(code_point, code_point, glyph_id);
117 }
118 }
119
120 prev_end_code_point = end_code_point;
121 }
122
123 r.prepare();
124 return r;
125}
126
127[[nodiscard]] inline font_char_map otype_cmap_parse_map_6(std::span<std::byte const> over_sized_bytes)
128{
129 struct header_type {
130 big_uint16_buf_t format;
131 big_uint16_buf_t length;
132 big_uint16_buf_t language;
133 big_uint16_buf_t first_code;
134 big_uint16_buf_t entry_count;
135 };
136
137 auto offset = 0_uz;
138 hilet& header = implicit_cast<header_type>(offset, over_sized_bytes);
139 hi_axiom(*header.format == 6);
140 hilet bytes = hi_check_subspan(over_sized_bytes, 0, *header.length);
141
142 hilet entry_count = *header.entry_count;
143 hilet entries = implicit_cast<big_uint16_buf_t>(offset, bytes, entry_count);
144
145 auto r = font_char_map{};
146 r.reserve(entry_count);
147 auto code_point = char_cast<char32_t>(*header.first_code);
148 for (auto i = 0_uz; i != entry_count; ++i, ++code_point) {
149 hilet glyph_id = *entries[i];
150 hi_check(glyph_id < 0xfffe, "'cmap' subtable 6, glyph_id must be in range 0 to 0xfffe.");
151 r.add(code_point, code_point, glyph_id);
152 }
153
154 // Add terminator to the table.
155 r.prepare();
156 return r;
157}
158
159[[nodiscard]] inline font_char_map otype_cmap_parse_map_12(std::span<std::byte const> over_sized_bytes)
160{
161 struct header_type {
162 big_uint16_buf_t format;
163 big_uint16_buf_t reserved;
164 big_uint32_buf_t length;
165 big_uint32_buf_t language;
166 big_uint32_buf_t num_groups;
167 };
168
169 struct entry_type {
170 big_uint32_buf_t start_char_code;
171 big_uint32_buf_t end_char_code;
172 big_uint32_buf_t start_glyph_id;
173 };
174
175 auto offset = 0_uz;
176 hilet& header = implicit_cast<header_type>(offset, over_sized_bytes);
177 hi_axiom(*header.format == 12);
178 hilet bytes = hi_check_subspan(over_sized_bytes, 0, *header.length);
179
180 hilet entries = implicit_cast<entry_type>(offset, bytes, *header.num_groups);
181
182 auto r = font_char_map{};
183 r.reserve(*header.num_groups);
184 for (hilet& entry : entries) {
185 hilet start_code_point = char_cast<char32_t>(*entry.start_char_code);
186 hilet end_code_point = char_cast<char32_t>(*entry.end_char_code);
187 hi_check(start_code_point <= end_code_point, "'cmap' subtable 12, has invalid code-point range.");
188
189 hilet start_glyph_id = *entry.start_glyph_id;
190 hi_check(
191 start_glyph_id + (end_code_point - start_code_point) + 1_uz < 0xffff,
192 "'cmap' subtable 12, glyph_id must be in range 0 to 0xfffe.");
193 r.add(start_code_point, end_code_point, narrow_cast<uint16_t>(start_glyph_id));
194 }
195
196 // Add terminator to the table.
197 r.prepare();
198 return r;
199}
200
201[[nodiscard]] inline font_char_map otype_cmap_parse_map(std::span<std::byte const> bytes)
202{
203 // The first 16 bits of a cmap sub-table always contain the format.
204 hilet format = *implicit_cast<big_uint16_buf_t>(bytes);
205
206 switch (format) {
207 case 4:
208 return otype_cmap_parse_map_4(bytes);
209 case 6:
210 return otype_cmap_parse_map_6(bytes);
211 case 12:
212 return otype_cmap_parse_map_12(bytes);
213 default:
214 // Unknown format, let otype_cmap_parse try the next sub-table.
215 return {};
216 }
217}
218
219[[nodiscard]] inline font_char_map otype_cmap_parse(std::span<std::byte const> bytes)
220{
221 constexpr auto search_order = std::array{
222 std::pair{uint16_t{0}, uint16_t{4}}, // Unicode - Unicode 2.0 non-BMP
223 std::pair{uint16_t{0}, uint16_t{3}}, // Unicode - Unicode 2.0 BMP-only
224 std::pair{uint16_t{0}, uint16_t{2}}, // Unicode - ISO 10646 1993
225 std::pair{uint16_t{0}, uint16_t{1}}, // Unicode - Version 1.1
226 std::pair{uint16_t{3}, uint16_t{10}}, // Microsoft Windows - Unicode 32-bit
227 std::pair{uint16_t{3}, uint16_t{1}}, // Microsoft Windows - Unicode 16-bit
228 std::pair{uint16_t{3}, uint16_t{0}} // Microsoft Windows - Symbol.
229 };
230
231 for (hilet[platform_id, platform_specific_id] : search_order) {
232 if (hilet map_bytes = otype_cmap_find(bytes, platform_id, platform_specific_id); not map_bytes.empty()) {
233 if (auto r = otype_cmap_parse_map(map_bytes); not r.empty()) {
234 return r;
235 }
236 }
237 }
238
239 throw parse_error("'cmap' no compatible character map found.");
240}
241
242}} // namespace hi::v1
Defined font_char_map type.
#define hi_check(expression, message,...)
Check if the expression is valid, or throw a parse_error.
Definition assert.hpp:110
#define hi_axiom(expression,...)
Specify an axiom; an expression that is true.
Definition assert.hpp:253
#define hi_check_at(span, index)
Get an element from a span, or throw a parse_error.
Definition assert.hpp:176
#define hi_check_subspan(span, offset,...)
Get a subspan, or throw a parse_error.
Definition assert.hpp:151
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:13
geometry/margins.hpp
Definition cache.hpp:11