1 | /* |
2 | * Copyright © 2014 Google, Inc. |
3 | * |
4 | * This is part of HarfBuzz, a text shaping library. |
5 | * |
6 | * Permission is hereby granted, without written agreement and without |
7 | * license or royalty fees, to use, copy, modify, and distribute this |
8 | * software and its documentation for any purpose, provided that the |
9 | * above copyright notice and the following two paragraphs appear in |
10 | * all copies of this software. |
11 | * |
12 | * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR |
13 | * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES |
14 | * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN |
15 | * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH |
16 | * DAMAGE. |
17 | * |
18 | * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, |
19 | * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND |
20 | * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS |
21 | * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO |
22 | * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. |
23 | * |
24 | * Google Author(s): Behdad Esfahbod |
25 | */ |
26 | |
27 | #ifndef HB_OT_CMAP_TABLE_HH |
28 | #define HB_OT_CMAP_TABLE_HH |
29 | |
30 | #include "hb-open-type.hh" |
31 | #include "hb-set.hh" |
32 | |
33 | /* |
34 | * cmap -- Character to Glyph Index Mapping |
35 | * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap |
36 | */ |
37 | #define HB_OT_TAG_cmap HB_TAG('c','m','a','p') |
38 | |
39 | namespace OT { |
40 | |
41 | |
42 | struct CmapSubtableFormat0 |
43 | { |
44 | bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const |
45 | { |
46 | hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0; |
47 | if (!gid) |
48 | return false; |
49 | *glyph = gid; |
50 | return true; |
51 | } |
52 | void collect_unicodes (hb_set_t *out) const |
53 | { |
54 | for (unsigned int i = 0; i < 256; i++) |
55 | if (glyphIdArray[i]) |
56 | out->add (i); |
57 | } |
58 | |
59 | bool sanitize (hb_sanitize_context_t *c) const |
60 | { |
61 | TRACE_SANITIZE (this); |
62 | return_trace (c->check_struct (this)); |
63 | } |
64 | |
65 | protected: |
66 | HBUINT16 format; /* Format number is set to 0. */ |
67 | HBUINT16 length; /* Byte length of this subtable. */ |
68 | HBUINT16 language; /* Ignore. */ |
69 | HBUINT8 glyphIdArray[256];/* An array that maps character |
70 | * code to glyph index values. */ |
71 | public: |
72 | DEFINE_SIZE_STATIC (6 + 256); |
73 | }; |
74 | |
75 | struct CmapSubtableFormat4 |
76 | { |
77 | |
78 | template<typename Iterator, |
79 | hb_requires (hb_is_iterator (Iterator))> |
80 | HBUINT16* serialize_endcode_array (hb_serialize_context_t *c, |
81 | Iterator it) |
82 | { |
83 | HBUINT16 *endCode = c->start_embed<HBUINT16> (); |
84 | hb_codepoint_t prev_endcp = 0xFFFF; |
85 | |
86 | + it |
87 | | hb_apply ([&] (const hb_item_type<Iterator> _) |
88 | { |
89 | if (prev_endcp != 0xFFFF && prev_endcp + 1u != _.first) |
90 | { |
91 | HBUINT16 end_code; |
92 | end_code = prev_endcp; |
93 | c->copy<HBUINT16> (end_code); |
94 | } |
95 | prev_endcp = _.first; |
96 | }) |
97 | ; |
98 | |
99 | { |
100 | // last endCode |
101 | HBUINT16 endcode; |
102 | endcode = prev_endcp; |
103 | if (unlikely (!c->copy<HBUINT16> (endcode))) return nullptr; |
104 | // There must be a final entry with end_code == 0xFFFF. |
105 | if (prev_endcp != 0xFFFF) |
106 | { |
107 | HBUINT16 finalcode; |
108 | finalcode = 0xFFFF; |
109 | if (unlikely (!c->copy<HBUINT16> (finalcode))) return nullptr; |
110 | } |
111 | } |
112 | |
113 | return endCode; |
114 | } |
115 | |
116 | template<typename Iterator, |
117 | hb_requires (hb_is_iterator (Iterator))> |
118 | HBUINT16* serialize_startcode_array (hb_serialize_context_t *c, |
119 | Iterator it) |
120 | { |
121 | HBUINT16 *startCode = c->start_embed<HBUINT16> (); |
122 | hb_codepoint_t prev_cp = 0xFFFF; |
123 | |
124 | + it |
125 | | hb_apply ([&] (const hb_item_type<Iterator> _) |
126 | { |
127 | if (prev_cp == 0xFFFF || prev_cp + 1u != _.first) |
128 | { |
129 | HBUINT16 start_code; |
130 | start_code = _.first; |
131 | c->copy<HBUINT16> (start_code); |
132 | } |
133 | |
134 | prev_cp = _.first; |
135 | }) |
136 | ; |
137 | |
138 | // There must be a final entry with end_code == 0xFFFF. |
139 | if (it.len () == 0 || prev_cp != 0xFFFF) |
140 | { |
141 | HBUINT16 finalcode; |
142 | finalcode = 0xFFFF; |
143 | if (unlikely (!c->copy<HBUINT16> (finalcode))) return nullptr; |
144 | } |
145 | |
146 | return startCode; |
147 | } |
148 | |
149 | template<typename Iterator, |
150 | hb_requires (hb_is_iterator (Iterator))> |
151 | HBINT16* serialize_idDelta_array (hb_serialize_context_t *c, |
152 | Iterator it, |
153 | HBUINT16 *endCode, |
154 | HBUINT16 *startCode, |
155 | unsigned segcount) |
156 | { |
157 | unsigned i = 0; |
158 | hb_codepoint_t last_gid = 0, start_gid = 0, last_cp = 0xFFFF; |
159 | bool use_delta = true; |
160 | |
161 | HBINT16 *idDelta = c->start_embed<HBINT16> (); |
162 | if ((char *)idDelta - (char *)startCode != (int) segcount * (int) HBINT16::static_size) |
163 | return nullptr; |
164 | |
165 | + it |
166 | | hb_apply ([&] (const hb_item_type<Iterator> _) |
167 | { |
168 | if (_.first == startCode[i]) |
169 | { |
170 | use_delta = true; |
171 | start_gid = _.second; |
172 | } |
173 | else if (_.second != last_gid + 1) use_delta = false; |
174 | |
175 | if (_.first == endCode[i]) |
176 | { |
177 | HBINT16 delta; |
178 | if (use_delta) delta = (int)start_gid - (int)startCode[i]; |
179 | else delta = 0; |
180 | c->copy<HBINT16> (delta); |
181 | |
182 | i++; |
183 | } |
184 | |
185 | last_gid = _.second; |
186 | last_cp = _.first; |
187 | }) |
188 | ; |
189 | |
190 | if (it.len () == 0 || last_cp != 0xFFFF) |
191 | { |
192 | HBINT16 delta; |
193 | delta = 1; |
194 | if (unlikely (!c->copy<HBINT16> (delta))) return nullptr; |
195 | } |
196 | |
197 | return idDelta; |
198 | } |
199 | |
200 | template<typename Iterator, |
201 | hb_requires (hb_is_iterator (Iterator))> |
202 | HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c, |
203 | Iterator it, |
204 | HBUINT16 *endCode, |
205 | HBUINT16 *startCode, |
206 | HBINT16 *idDelta, |
207 | unsigned segcount) |
208 | { |
209 | HBUINT16 *idRangeOffset = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount); |
210 | if (unlikely (!c->check_success (idRangeOffset))) return nullptr; |
211 | if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr; |
212 | |
213 | + hb_range (segcount) |
214 | | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }) |
215 | | hb_apply ([&] (const unsigned i) |
216 | { |
217 | idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i); |
218 | |
219 | + it |
220 | | hb_filter ([&] (const hb_item_type<Iterator> _) { return _.first >= startCode[i] && _.first <= endCode[i]; }) |
221 | | hb_apply ([&] (const hb_item_type<Iterator> _) |
222 | { |
223 | HBUINT16 glyID; |
224 | glyID = _.second; |
225 | c->copy<HBUINT16> (glyID); |
226 | }) |
227 | ; |
228 | |
229 | |
230 | }) |
231 | ; |
232 | |
233 | return idRangeOffset; |
234 | } |
235 | |
236 | template<typename Iterator, |
237 | hb_requires (hb_is_iterator (Iterator))> |
238 | void serialize (hb_serialize_context_t *c, |
239 | Iterator it) |
240 | { |
241 | unsigned table_initpos = c->length (); |
242 | if (unlikely (!c->extend_min (*this))) return; |
243 | this->format = 4; |
244 | |
245 | //serialize endCode[] |
246 | HBUINT16 *endCode = serialize_endcode_array (c, it); |
247 | if (unlikely (!endCode)) return; |
248 | |
249 | unsigned segcount = (c->length () - min_size) / HBUINT16::static_size; |
250 | |
251 | // 2 bytes of padding. |
252 | if (unlikely (!c->allocate_size<HBUINT16> (HBUINT16::static_size))) return; // 2 bytes of padding. |
253 | |
254 | // serialize startCode[] |
255 | HBUINT16 *startCode = serialize_startcode_array (c, it); |
256 | if (unlikely (!startCode)) return; |
257 | |
258 | //serialize idDelta[] |
259 | HBINT16 *idDelta = serialize_idDelta_array (c, it, endCode, startCode, segcount); |
260 | if (unlikely (!idDelta)) return; |
261 | |
262 | HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, it, endCode, startCode, idDelta, segcount); |
263 | if (unlikely (!c->check_success (idRangeOffset))) return; |
264 | |
265 | if (unlikely (!c->check_assign(this->length, c->length () - table_initpos))) return; |
266 | this->segCountX2 = segcount * 2; |
267 | this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; |
268 | this->searchRange = 2 * (1u << this->entrySelector); |
269 | this->rangeShift = segcount * 2 > this->searchRange |
270 | ? 2 * segcount - this->searchRange |
271 | : 0; |
272 | } |
273 | |
274 | struct accelerator_t |
275 | { |
276 | accelerator_t () {} |
277 | accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); } |
278 | ~accelerator_t () { fini (); } |
279 | |
280 | void init (const CmapSubtableFormat4 *subtable) |
281 | { |
282 | segCount = subtable->segCountX2 / 2; |
283 | endCount = subtable->values.arrayZ; |
284 | startCount = endCount + segCount + 1; |
285 | idDelta = startCount + segCount; |
286 | idRangeOffset = idDelta + segCount; |
287 | glyphIdArray = idRangeOffset + segCount; |
288 | glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2; |
289 | } |
290 | void fini () {} |
291 | |
292 | bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const |
293 | { |
294 | /* Custom two-array bsearch. */ |
295 | int min = 0, max = (int) this->segCount - 1; |
296 | const HBUINT16 *startCount = this->startCount; |
297 | const HBUINT16 *endCount = this->endCount; |
298 | unsigned int i; |
299 | while (min <= max) |
300 | { |
301 | int mid = ((unsigned int) min + (unsigned int) max) / 2; |
302 | if (codepoint < startCount[mid]) |
303 | max = mid - 1; |
304 | else if (codepoint > endCount[mid]) |
305 | min = mid + 1; |
306 | else |
307 | { |
308 | i = mid; |
309 | goto found; |
310 | } |
311 | } |
312 | return false; |
313 | |
314 | found: |
315 | hb_codepoint_t gid; |
316 | unsigned int rangeOffset = this->idRangeOffset[i]; |
317 | if (rangeOffset == 0) |
318 | gid = codepoint + this->idDelta[i]; |
319 | else |
320 | { |
321 | /* Somebody has been smoking... */ |
322 | unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; |
323 | if (unlikely (index >= this->glyphIdArrayLength)) |
324 | return false; |
325 | gid = this->glyphIdArray[index]; |
326 | if (unlikely (!gid)) |
327 | return false; |
328 | gid += this->idDelta[i]; |
329 | } |
330 | gid &= 0xFFFFu; |
331 | if (!gid) |
332 | return false; |
333 | *glyph = gid; |
334 | return true; |
335 | } |
336 | HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph) |
337 | { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); } |
338 | void collect_unicodes (hb_set_t *out) const |
339 | { |
340 | unsigned int count = this->segCount; |
341 | if (count && this->startCount[count - 1] == 0xFFFFu) |
342 | count--; /* Skip sentinel segment. */ |
343 | for (unsigned int i = 0; i < count; i++) |
344 | { |
345 | hb_codepoint_t start = this->startCount[i]; |
346 | hb_codepoint_t end = this->endCount[i]; |
347 | unsigned int rangeOffset = this->idRangeOffset[i]; |
348 | if (rangeOffset == 0) |
349 | { |
350 | for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) |
351 | { |
352 | hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu; |
353 | if (unlikely (!gid)) |
354 | continue; |
355 | out->add (codepoint); |
356 | } |
357 | } |
358 | else |
359 | { |
360 | for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) |
361 | { |
362 | unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount; |
363 | if (unlikely (index >= this->glyphIdArrayLength)) |
364 | break; |
365 | hb_codepoint_t gid = this->glyphIdArray[index]; |
366 | if (unlikely (!gid)) |
367 | continue; |
368 | out->add (codepoint); |
369 | } |
370 | } |
371 | } |
372 | } |
373 | |
374 | const HBUINT16 *endCount; |
375 | const HBUINT16 *startCount; |
376 | const HBUINT16 *idDelta; |
377 | const HBUINT16 *idRangeOffset; |
378 | const HBUINT16 *glyphIdArray; |
379 | unsigned int segCount; |
380 | unsigned int glyphIdArrayLength; |
381 | }; |
382 | |
383 | bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const |
384 | { |
385 | accelerator_t accel (this); |
386 | return accel.get_glyph_func (&accel, codepoint, glyph); |
387 | } |
388 | void collect_unicodes (hb_set_t *out) const |
389 | { |
390 | accelerator_t accel (this); |
391 | accel.collect_unicodes (out); |
392 | } |
393 | |
394 | bool sanitize (hb_sanitize_context_t *c) const |
395 | { |
396 | TRACE_SANITIZE (this); |
397 | if (unlikely (!c->check_struct (this))) |
398 | return_trace (false); |
399 | |
400 | if (unlikely (!c->check_range (this, length))) |
401 | { |
402 | /* Some broken fonts have too long of a "length" value. |
403 | * If that is the case, just change the value to truncate |
404 | * the subtable at the end of the blob. */ |
405 | uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, |
406 | (uintptr_t) (c->end - |
407 | (char *) this)); |
408 | if (!c->try_set (&length, new_length)) |
409 | return_trace (false); |
410 | } |
411 | |
412 | return_trace (16 + 4 * (unsigned int) segCountX2 <= length); |
413 | } |
414 | |
415 | |
416 | |
417 | protected: |
418 | HBUINT16 format; /* Format number is set to 4. */ |
419 | HBUINT16 length; /* This is the length in bytes of the |
420 | * subtable. */ |
421 | HBUINT16 language; /* Ignore. */ |
422 | HBUINT16 segCountX2; /* 2 x segCount. */ |
423 | HBUINT16 searchRange; /* 2 * (2**floor(log2(segCount))) */ |
424 | HBUINT16 entrySelector; /* log2(searchRange/2) */ |
425 | HBUINT16 rangeShift; /* 2 x segCount - searchRange */ |
426 | |
427 | UnsizedArrayOf<HBUINT16> |
428 | values; |
429 | #if 0 |
430 | HBUINT16 endCount[segCount]; /* End characterCode for each segment, |
431 | * last=0xFFFFu. */ |
432 | HBUINT16 reservedPad; /* Set to 0. */ |
433 | HBUINT16 startCount[segCount]; /* Start character code for each segment. */ |
434 | HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */ |
435 | HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */ |
436 | UnsizedArrayOf<HBUINT16> |
437 | glyphIdArray; /* Glyph index array (arbitrary length) */ |
438 | #endif |
439 | |
440 | public: |
441 | DEFINE_SIZE_ARRAY (14, values); |
442 | }; |
443 | |
444 | struct CmapSubtableLongGroup |
445 | { |
446 | friend struct CmapSubtableFormat12; |
447 | friend struct CmapSubtableFormat13; |
448 | template<typename U> |
449 | friend struct CmapSubtableLongSegmented; |
450 | friend struct cmap; |
451 | |
452 | int cmp (hb_codepoint_t codepoint) const |
453 | { |
454 | if (codepoint < startCharCode) return -1; |
455 | if (codepoint > endCharCode) return +1; |
456 | return 0; |
457 | } |
458 | |
459 | bool sanitize (hb_sanitize_context_t *c) const |
460 | { |
461 | TRACE_SANITIZE (this); |
462 | return_trace (c->check_struct (this)); |
463 | } |
464 | |
465 | private: |
466 | HBUINT32 startCharCode; /* First character code in this group. */ |
467 | HBUINT32 endCharCode; /* Last character code in this group. */ |
468 | HBUINT32 glyphID; /* Glyph index; interpretation depends on |
469 | * subtable format. */ |
470 | public: |
471 | DEFINE_SIZE_STATIC (12); |
472 | }; |
473 | DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup); |
474 | |
475 | template <typename UINT> |
476 | struct CmapSubtableTrimmed |
477 | { |
478 | bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const |
479 | { |
480 | /* Rely on our implicit array bound-checking. */ |
481 | hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode]; |
482 | if (!gid) |
483 | return false; |
484 | *glyph = gid; |
485 | return true; |
486 | } |
487 | void collect_unicodes (hb_set_t *out) const |
488 | { |
489 | hb_codepoint_t start = startCharCode; |
490 | unsigned int count = glyphIdArray.len; |
491 | for (unsigned int i = 0; i < count; i++) |
492 | if (glyphIdArray[i]) |
493 | out->add (start + i); |
494 | } |
495 | |
496 | bool sanitize (hb_sanitize_context_t *c) const |
497 | { |
498 | TRACE_SANITIZE (this); |
499 | return_trace (c->check_struct (this) && glyphIdArray.sanitize (c)); |
500 | } |
501 | |
502 | protected: |
503 | UINT formatReserved; /* Subtable format and (maybe) padding. */ |
504 | UINT length; /* Byte length of this subtable. */ |
505 | UINT language; /* Ignore. */ |
506 | UINT startCharCode; /* First character code covered. */ |
507 | ArrayOf<HBGlyphID, UINT> |
508 | glyphIdArray; /* Array of glyph index values for character |
509 | * codes in the range. */ |
510 | public: |
511 | DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray); |
512 | }; |
513 | |
514 | struct CmapSubtableFormat6 : CmapSubtableTrimmed<HBUINT16> {}; |
515 | struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32 > {}; |
516 | |
517 | template <typename T> |
518 | struct CmapSubtableLongSegmented |
519 | { |
520 | friend struct cmap; |
521 | |
522 | bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const |
523 | { |
524 | hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint); |
525 | if (!gid) |
526 | return false; |
527 | *glyph = gid; |
528 | return true; |
529 | } |
530 | |
531 | void collect_unicodes (hb_set_t *out) const |
532 | { |
533 | for (unsigned int i = 0; i < this->groups.len; i++) |
534 | { |
535 | hb_codepoint_t start = this->groups[i].startCharCode; |
536 | hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode, |
537 | (hb_codepoint_t) HB_UNICODE_MAX); |
538 | for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++) |
539 | { |
540 | hb_codepoint_t gid = T::group_get_glyph (this->groups[i], codepoint); |
541 | if (unlikely (!gid)) |
542 | continue; |
543 | out->add (codepoint); |
544 | } |
545 | } |
546 | } |
547 | |
548 | bool sanitize (hb_sanitize_context_t *c) const |
549 | { |
550 | TRACE_SANITIZE (this); |
551 | return_trace (c->check_struct (this) && groups.sanitize (c)); |
552 | } |
553 | |
554 | protected: |
555 | HBUINT16 format; /* Subtable format; set to 12. */ |
556 | HBUINT16 reserved; /* Reserved; set to 0. */ |
557 | HBUINT32 length; /* Byte length of this subtable. */ |
558 | HBUINT32 language; /* Ignore. */ |
559 | SortedArrayOf<CmapSubtableLongGroup, HBUINT32> |
560 | groups; /* Groupings. */ |
561 | public: |
562 | DEFINE_SIZE_ARRAY (16, groups); |
563 | }; |
564 | |
565 | struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12> |
566 | { |
567 | static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, |
568 | hb_codepoint_t u) |
569 | { return likely (group.startCharCode <= group.endCharCode) ? |
570 | group.glyphID + (u - group.startCharCode) : 0; } |
571 | |
572 | |
573 | template<typename Iterator, |
574 | hb_requires (hb_is_iterator (Iterator))> |
575 | void serialize (hb_serialize_context_t *c, |
576 | Iterator it) |
577 | { |
578 | if (it.len () == 0) return; |
579 | unsigned table_initpos = c->length (); |
580 | if (unlikely (!c->extend_min (*this))) return; |
581 | |
582 | hb_codepoint_t startCharCode = 0xFFFF, endCharCode = 0xFFFF; |
583 | hb_codepoint_t glyphID = 0; |
584 | |
585 | + it |
586 | | hb_apply ([&] (const hb_item_type<Iterator> _) |
587 | { |
588 | if (startCharCode == 0xFFFF) |
589 | { |
590 | startCharCode = _.first; |
591 | endCharCode = _.first; |
592 | glyphID = _.second; |
593 | } |
594 | else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second)) |
595 | { |
596 | CmapSubtableLongGroup grouprecord; |
597 | grouprecord.startCharCode = startCharCode; |
598 | grouprecord.endCharCode = endCharCode; |
599 | grouprecord.glyphID = glyphID; |
600 | c->copy<CmapSubtableLongGroup> (grouprecord); |
601 | |
602 | startCharCode = _.first; |
603 | endCharCode = _.first; |
604 | glyphID = _.second; |
605 | } |
606 | else |
607 | { |
608 | endCharCode = _.first; |
609 | } |
610 | }) |
611 | ; |
612 | |
613 | CmapSubtableLongGroup record; |
614 | record.startCharCode = startCharCode; |
615 | record.endCharCode = endCharCode; |
616 | record.glyphID = glyphID; |
617 | c->copy<CmapSubtableLongGroup> (record); |
618 | |
619 | this->format = 12; |
620 | this->reserved = 0; |
621 | this->length = c->length () - table_initpos; |
622 | this->groups.len = (this->length - min_size)/CmapSubtableLongGroup::static_size; |
623 | } |
624 | |
625 | static size_t get_sub_table_size (const hb_sorted_vector_t<CmapSubtableLongGroup> &groups_data) |
626 | { return 16 + 12 * groups_data.length; } |
627 | |
628 | private: |
629 | static bool _is_gid_consecutive (hb_codepoint_t endCharCode, |
630 | hb_codepoint_t startCharCode, |
631 | hb_codepoint_t glyphID, |
632 | hb_codepoint_t cp, |
633 | hb_codepoint_t new_gid) |
634 | { |
635 | return (cp - 1 == endCharCode) && |
636 | new_gid == glyphID + (cp - startCharCode); |
637 | } |
638 | |
639 | }; |
640 | |
641 | struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13> |
642 | { |
643 | static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group, |
644 | hb_codepoint_t u HB_UNUSED) |
645 | { return group.glyphID; } |
646 | }; |
647 | |
648 | typedef enum |
649 | { |
650 | GLYPH_VARIANT_NOT_FOUND = 0, |
651 | GLYPH_VARIANT_FOUND = 1, |
652 | GLYPH_VARIANT_USE_DEFAULT = 2 |
653 | } glyph_variant_t; |
654 | |
655 | struct UnicodeValueRange |
656 | { |
657 | int cmp (const hb_codepoint_t &codepoint) const |
658 | { |
659 | if (codepoint < startUnicodeValue) return -1; |
660 | if (codepoint > startUnicodeValue + additionalCount) return +1; |
661 | return 0; |
662 | } |
663 | |
664 | bool sanitize (hb_sanitize_context_t *c) const |
665 | { |
666 | TRACE_SANITIZE (this); |
667 | return_trace (c->check_struct (this)); |
668 | } |
669 | |
670 | HBUINT24 startUnicodeValue; /* First value in this range. */ |
671 | HBUINT8 additionalCount; /* Number of additional values in this |
672 | * range. */ |
673 | public: |
674 | DEFINE_SIZE_STATIC (4); |
675 | }; |
676 | |
677 | struct DefaultUVS : SortedArrayOf<UnicodeValueRange, HBUINT32> |
678 | { |
679 | void collect_unicodes (hb_set_t *out) const |
680 | { |
681 | unsigned int count = len; |
682 | for (unsigned int i = 0; i < count; i++) |
683 | { |
684 | hb_codepoint_t first = arrayZ[i].startUnicodeValue; |
685 | hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount), |
686 | (hb_codepoint_t) HB_UNICODE_MAX); |
687 | out->add_range (first, last); |
688 | } |
689 | } |
690 | |
691 | DefaultUVS* copy (hb_serialize_context_t *c, |
692 | const hb_set_t *unicodes) const |
693 | { |
694 | DefaultUVS *out = c->start_embed<DefaultUVS> (); |
695 | if (unlikely (!out)) return nullptr; |
696 | auto snap = c->snapshot (); |
697 | |
698 | HBUINT32 len; |
699 | len = 0; |
700 | if (unlikely (!c->copy<HBUINT32> (len))) return nullptr; |
701 | unsigned init_len = c->length (); |
702 | |
703 | hb_codepoint_t lastCode = HB_MAP_VALUE_INVALID; |
704 | int count = -1; |
705 | |
706 | for (const UnicodeValueRange& _ : as_array ()) |
707 | { |
708 | for (const unsigned addcnt : hb_range ((unsigned) _.additionalCount + 1)) |
709 | { |
710 | unsigned curEntry = (unsigned) _.startUnicodeValue + addcnt; |
711 | if (!unicodes->has (curEntry)) continue; |
712 | count += 1; |
713 | if (lastCode == HB_MAP_VALUE_INVALID) |
714 | lastCode = curEntry; |
715 | else if (lastCode + count != curEntry) |
716 | { |
717 | UnicodeValueRange rec; |
718 | rec.startUnicodeValue = lastCode; |
719 | rec.additionalCount = count - 1; |
720 | c->copy<UnicodeValueRange> (rec); |
721 | |
722 | lastCode = curEntry; |
723 | count = 0; |
724 | } |
725 | } |
726 | } |
727 | |
728 | if (lastCode != HB_MAP_VALUE_INVALID) |
729 | { |
730 | UnicodeValueRange rec; |
731 | rec.startUnicodeValue = lastCode; |
732 | rec.additionalCount = count; |
733 | c->copy<UnicodeValueRange> (rec); |
734 | } |
735 | |
736 | if (c->length () - init_len == 0) |
737 | { |
738 | c->revert (snap); |
739 | return nullptr; |
740 | } |
741 | else |
742 | { |
743 | if (unlikely (!c->check_assign (out->len, (c->length () - init_len) / UnicodeValueRange::static_size))) return nullptr; |
744 | return out; |
745 | } |
746 | } |
747 | |
748 | public: |
749 | DEFINE_SIZE_ARRAY (4, *this); |
750 | }; |
751 | |
752 | struct UVSMapping |
753 | { |
754 | int cmp (const hb_codepoint_t &codepoint) const |
755 | { return unicodeValue.cmp (codepoint); } |
756 | |
757 | bool sanitize (hb_sanitize_context_t *c) const |
758 | { |
759 | TRACE_SANITIZE (this); |
760 | return_trace (c->check_struct (this)); |
761 | } |
762 | |
763 | HBUINT24 unicodeValue; /* Base Unicode value of the UVS */ |
764 | HBGlyphID glyphID; /* Glyph ID of the UVS */ |
765 | public: |
766 | DEFINE_SIZE_STATIC (5); |
767 | }; |
768 | |
769 | struct NonDefaultUVS : SortedArrayOf<UVSMapping, HBUINT32> |
770 | { |
771 | void collect_unicodes (hb_set_t *out) const |
772 | { |
773 | unsigned int count = len; |
774 | for (unsigned int i = 0; i < count; i++) |
775 | out->add (arrayZ[i].glyphID); |
776 | } |
777 | |
778 | void closure_glyphs (const hb_set_t *unicodes, |
779 | hb_set_t *glyphset) const |
780 | { |
781 | + as_array () |
782 | | hb_filter (unicodes, &UVSMapping::unicodeValue) |
783 | | hb_map (&UVSMapping::glyphID) |
784 | | hb_sink (glyphset) |
785 | ; |
786 | } |
787 | |
788 | NonDefaultUVS* copy (hb_serialize_context_t *c, |
789 | const hb_set_t *unicodes, |
790 | const hb_set_t *glyphs, |
791 | const hb_map_t *glyph_map) const |
792 | { |
793 | NonDefaultUVS *out = c->start_embed<NonDefaultUVS> (); |
794 | if (unlikely (!out)) return nullptr; |
795 | |
796 | auto it = |
797 | + as_array () |
798 | | hb_filter ([&] (const UVSMapping& _) |
799 | { |
800 | return unicodes->has (_.unicodeValue) || glyphs->has (_.glyphID); |
801 | }) |
802 | ; |
803 | |
804 | if (!it) return nullptr; |
805 | |
806 | HBUINT32 len; |
807 | len = it.len (); |
808 | if (unlikely (!c->copy<HBUINT32> (len))) return nullptr; |
809 | |
810 | for (const UVSMapping& _ : it) |
811 | { |
812 | UVSMapping mapping; |
813 | mapping.unicodeValue = _.unicodeValue; |
814 | mapping.glyphID = glyph_map->get (_.glyphID); |
815 | c->copy<UVSMapping> (mapping); |
816 | } |
817 | |
818 | return out; |
819 | } |
820 | |
821 | public: |
822 | DEFINE_SIZE_ARRAY (4, *this); |
823 | }; |
824 | |
825 | struct VariationSelectorRecord |
826 | { |
827 | glyph_variant_t get_glyph (hb_codepoint_t codepoint, |
828 | hb_codepoint_t *glyph, |
829 | const void *base) const |
830 | { |
831 | if ((base+defaultUVS).bfind (codepoint)) |
832 | return GLYPH_VARIANT_USE_DEFAULT; |
833 | const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint); |
834 | if (nonDefault.glyphID) |
835 | { |
836 | *glyph = nonDefault.glyphID; |
837 | return GLYPH_VARIANT_FOUND; |
838 | } |
839 | return GLYPH_VARIANT_NOT_FOUND; |
840 | } |
841 | |
842 | void collect_unicodes (hb_set_t *out, const void *base) const |
843 | { |
844 | (base+defaultUVS).collect_unicodes (out); |
845 | (base+nonDefaultUVS).collect_unicodes (out); |
846 | } |
847 | |
848 | int cmp (const hb_codepoint_t &variation_selector) const |
849 | { return varSelector.cmp (variation_selector); } |
850 | |
851 | bool sanitize (hb_sanitize_context_t *c, const void *base) const |
852 | { |
853 | TRACE_SANITIZE (this); |
854 | return_trace (c->check_struct (this) && |
855 | defaultUVS.sanitize (c, base) && |
856 | nonDefaultUVS.sanitize (c, base)); |
857 | } |
858 | |
859 | VariationSelectorRecord* copy (hb_serialize_context_t *c, |
860 | const hb_set_t *unicodes, |
861 | const hb_set_t *glyphs, |
862 | const hb_map_t *glyph_map, |
863 | const void *src_base, |
864 | const void *dst_base) const |
865 | { |
866 | auto snap = c->snapshot (); |
867 | auto *out = c->embed<VariationSelectorRecord> (*this); |
868 | if (unlikely (!out)) return nullptr; |
869 | |
870 | out->defaultUVS = 0; |
871 | out->nonDefaultUVS = 0; |
872 | |
873 | bool drop = true; |
874 | |
875 | if (defaultUVS != 0) |
876 | { |
877 | c->push (); |
878 | if (c->copy (src_base+defaultUVS, unicodes)) |
879 | { |
880 | c->add_link (out->defaultUVS, c->pop_pack (), dst_base); |
881 | drop = false; |
882 | } |
883 | else c->pop_discard (); |
884 | } |
885 | |
886 | if (nonDefaultUVS != 0) |
887 | { |
888 | c->push (); |
889 | if (c->copy (src_base+nonDefaultUVS, unicodes, glyphs, glyph_map)) |
890 | { |
891 | c->add_link (out->nonDefaultUVS, c->pop_pack (), dst_base); |
892 | drop = false; |
893 | } |
894 | else c->pop_discard (); |
895 | } |
896 | |
897 | if (drop) |
898 | { |
899 | c->revert (snap); |
900 | return nullptr; |
901 | } |
902 | else return out; |
903 | } |
904 | |
905 | HBUINT24 varSelector; /* Variation selector. */ |
906 | LOffsetTo<DefaultUVS> |
907 | defaultUVS; /* Offset to Default UVS Table. May be 0. */ |
908 | LOffsetTo<NonDefaultUVS> |
909 | nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */ |
910 | public: |
911 | DEFINE_SIZE_STATIC (11); |
912 | }; |
913 | |
914 | struct CmapSubtableFormat14 |
915 | { |
916 | glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint, |
917 | hb_codepoint_t variation_selector, |
918 | hb_codepoint_t *glyph) const |
919 | { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); } |
920 | |
921 | void collect_variation_selectors (hb_set_t *out) const |
922 | { |
923 | unsigned int count = record.len; |
924 | for (unsigned int i = 0; i < count; i++) |
925 | out->add (record.arrayZ[i].varSelector); |
926 | } |
927 | void collect_variation_unicodes (hb_codepoint_t variation_selector, |
928 | hb_set_t *out) const |
929 | { record.bsearch (variation_selector).collect_unicodes (out, this); } |
930 | |
931 | void serialize (hb_serialize_context_t *c, |
932 | const hb_set_t *unicodes, |
933 | const hb_set_t *glyphs, |
934 | const hb_map_t *glyph_map, |
935 | const void *src_base) |
936 | { |
937 | auto snap = c->snapshot (); |
938 | unsigned table_initpos = c->length (); |
939 | const char* init_tail = c->tail; |
940 | |
941 | if (unlikely (!c->extend_min (*this))) return; |
942 | this->format = 14; |
943 | |
944 | const CmapSubtableFormat14 *src_tbl = reinterpret_cast<const CmapSubtableFormat14*> (src_base); |
945 | for (const VariationSelectorRecord& _ : src_tbl->record) |
946 | c->copy (_, unicodes, glyphs, glyph_map, src_base, this); |
947 | |
948 | if (c->length () - table_initpos == CmapSubtableFormat14::min_size) |
949 | c->revert (snap); |
950 | else |
951 | { |
952 | int tail_len = init_tail - c->tail; |
953 | c->check_assign (this->length, c->length () - table_initpos + tail_len); |
954 | c->check_assign (this->record.len, (c->length () - table_initpos - CmapSubtableFormat14::min_size) / VariationSelectorRecord::static_size); |
955 | } |
956 | } |
957 | |
958 | void closure_glyphs (const hb_set_t *unicodes, |
959 | hb_set_t *glyphset) const |
960 | { |
961 | + hb_iter (record) |
962 | | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS) |
963 | | hb_map (&VariationSelectorRecord::nonDefaultUVS) |
964 | | hb_map (hb_add (this)) |
965 | | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); }) |
966 | ; |
967 | } |
968 | |
969 | bool sanitize (hb_sanitize_context_t *c) const |
970 | { |
971 | TRACE_SANITIZE (this); |
972 | return_trace (c->check_struct (this) && |
973 | record.sanitize (c, this)); |
974 | } |
975 | |
976 | protected: |
977 | HBUINT16 format; /* Format number is set to 14. */ |
978 | HBUINT32 length; /* Byte length of this subtable. */ |
979 | SortedArrayOf<VariationSelectorRecord, HBUINT32> |
980 | record; /* Variation selector records; sorted |
981 | * in increasing order of `varSelector'. */ |
982 | public: |
983 | DEFINE_SIZE_ARRAY (10, record); |
984 | }; |
985 | |
986 | struct CmapSubtable |
987 | { |
988 | /* Note: We intentionally do NOT implement subtable formats 2 and 8. */ |
989 | |
990 | bool get_glyph (hb_codepoint_t codepoint, |
991 | hb_codepoint_t *glyph) const |
992 | { |
993 | switch (u.format) { |
994 | case 0: return u.format0 .get_glyph (codepoint, glyph); |
995 | case 4: return u.format4 .get_glyph (codepoint, glyph); |
996 | case 6: return u.format6 .get_glyph (codepoint, glyph); |
997 | case 10: return u.format10.get_glyph (codepoint, glyph); |
998 | case 12: return u.format12.get_glyph (codepoint, glyph); |
999 | case 13: return u.format13.get_glyph (codepoint, glyph); |
1000 | case 14: |
1001 | default: return false; |
1002 | } |
1003 | } |
1004 | void collect_unicodes (hb_set_t *out) const |
1005 | { |
1006 | switch (u.format) { |
1007 | case 0: u.format0 .collect_unicodes (out); return; |
1008 | case 4: u.format4 .collect_unicodes (out); return; |
1009 | case 6: u.format6 .collect_unicodes (out); return; |
1010 | case 10: u.format10.collect_unicodes (out); return; |
1011 | case 12: u.format12.collect_unicodes (out); return; |
1012 | case 13: u.format13.collect_unicodes (out); return; |
1013 | case 14: |
1014 | default: return; |
1015 | } |
1016 | } |
1017 | |
1018 | template<typename Iterator, |
1019 | hb_requires (hb_is_iterator (Iterator))> |
1020 | void serialize (hb_serialize_context_t *c, |
1021 | Iterator it, |
1022 | unsigned format, |
1023 | const hb_subset_plan_t *plan, |
1024 | const void *src_base) |
1025 | { |
1026 | switch (format) { |
1027 | case 4: u.format4.serialize (c, it); return; |
1028 | case 12: u.format12.serialize (c, it); return; |
1029 | case 14: u.format14.serialize (c, plan->unicodes, plan->_glyphset, plan->glyph_map, src_base); return; |
1030 | default: return; |
1031 | } |
1032 | } |
1033 | |
1034 | bool sanitize (hb_sanitize_context_t *c) const |
1035 | { |
1036 | TRACE_SANITIZE (this); |
1037 | if (!u.format.sanitize (c)) return_trace (false); |
1038 | switch (u.format) { |
1039 | case 0: return_trace (u.format0 .sanitize (c)); |
1040 | case 4: return_trace (u.format4 .sanitize (c)); |
1041 | case 6: return_trace (u.format6 .sanitize (c)); |
1042 | case 10: return_trace (u.format10.sanitize (c)); |
1043 | case 12: return_trace (u.format12.sanitize (c)); |
1044 | case 13: return_trace (u.format13.sanitize (c)); |
1045 | case 14: return_trace (u.format14.sanitize (c)); |
1046 | default:return_trace (true); |
1047 | } |
1048 | } |
1049 | |
1050 | public: |
1051 | union { |
1052 | HBUINT16 format; /* Format identifier */ |
1053 | CmapSubtableFormat0 format0; |
1054 | CmapSubtableFormat4 format4; |
1055 | CmapSubtableFormat6 format6; |
1056 | CmapSubtableFormat10 format10; |
1057 | CmapSubtableFormat12 format12; |
1058 | CmapSubtableFormat13 format13; |
1059 | CmapSubtableFormat14 format14; |
1060 | } u; |
1061 | public: |
1062 | DEFINE_SIZE_UNION (2, format); |
1063 | }; |
1064 | |
1065 | |
1066 | struct EncodingRecord |
1067 | { |
1068 | int cmp (const EncodingRecord &other) const |
1069 | { |
1070 | int ret; |
1071 | ret = platformID.cmp (other.platformID); |
1072 | if (ret) return ret; |
1073 | ret = encodingID.cmp (other.encodingID); |
1074 | if (ret) return ret; |
1075 | return 0; |
1076 | } |
1077 | |
1078 | bool sanitize (hb_sanitize_context_t *c, const void *base) const |
1079 | { |
1080 | TRACE_SANITIZE (this); |
1081 | return_trace (c->check_struct (this) && |
1082 | subtable.sanitize (c, base)); |
1083 | } |
1084 | |
1085 | template<typename Iterator, |
1086 | hb_requires (hb_is_iterator (Iterator))> |
1087 | EncodingRecord* copy (hb_serialize_context_t *c, |
1088 | Iterator it, |
1089 | unsigned format, |
1090 | const void *src_base, |
1091 | const void *dst_base, |
1092 | const hb_subset_plan_t *plan, |
1093 | /* INOUT */ unsigned *objidx) const |
1094 | { |
1095 | TRACE_SERIALIZE (this); |
1096 | auto snap = c->snapshot (); |
1097 | auto *out = c->embed (this); |
1098 | if (unlikely (!out)) return_trace (nullptr); |
1099 | out->subtable = 0; |
1100 | |
1101 | if (*objidx == 0) |
1102 | { |
1103 | CmapSubtable *cmapsubtable = c->push<CmapSubtable> (); |
1104 | unsigned origin_length = c->length (); |
1105 | cmapsubtable->serialize (c, it, format, plan, &(src_base+subtable)); |
1106 | if (c->length () - origin_length > 0) *objidx = c->pop_pack (); |
1107 | else c->pop_discard (); |
1108 | } |
1109 | |
1110 | if (*objidx == 0) |
1111 | { |
1112 | c->revert (snap); |
1113 | return_trace (nullptr); |
1114 | } |
1115 | |
1116 | c->add_link (out->subtable, *objidx, dst_base); |
1117 | return_trace (out); |
1118 | } |
1119 | |
1120 | HBUINT16 platformID; /* Platform ID. */ |
1121 | HBUINT16 encodingID; /* Platform-specific encoding ID. */ |
1122 | LOffsetTo<CmapSubtable> |
1123 | subtable; /* Byte offset from beginning of table to the subtable for this encoding. */ |
1124 | public: |
1125 | DEFINE_SIZE_STATIC (8); |
1126 | }; |
1127 | |
1128 | struct cmap |
1129 | { |
1130 | static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap; |
1131 | |
1132 | template<typename Iterator, typename EncodingRecIter, |
1133 | hb_requires (hb_is_iterator (Iterator))> |
1134 | void serialize (hb_serialize_context_t *c, |
1135 | Iterator it, |
1136 | EncodingRecIter encodingrec_iter, |
1137 | const void *src_base, |
1138 | const hb_subset_plan_t *plan) |
1139 | { |
1140 | if (unlikely (!c->extend_min ((*this)))) return; |
1141 | this->version = 0; |
1142 | |
1143 | unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; |
1144 | |
1145 | for (const EncodingRecord& _ : encodingrec_iter) |
1146 | { |
1147 | unsigned format = (src_base+_.subtable).u.format; |
1148 | |
1149 | if (format == 4) c->copy (_, it, 4u, src_base, this, plan, &format4objidx); |
1150 | else if (format == 12) c->copy (_, it, 12u, src_base, this, plan, &format12objidx); |
1151 | else if (format == 14) c->copy (_, it, 14u, src_base, this, plan, &format14objidx); |
1152 | } |
1153 | |
1154 | c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size); |
1155 | } |
1156 | |
1157 | void closure_glyphs (const hb_set_t *unicodes, |
1158 | hb_set_t *glyphset) const |
1159 | { |
1160 | + hb_iter (encodingRecord) |
1161 | | hb_map (&EncodingRecord::subtable) |
1162 | | hb_map (hb_add (this)) |
1163 | | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) |
1164 | | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) |
1165 | ; |
1166 | } |
1167 | |
1168 | bool subset (hb_subset_context_t *c) const |
1169 | { |
1170 | TRACE_SUBSET (this); |
1171 | |
1172 | cmap *cmap_prime = c->serializer->start_embed<cmap> (); |
1173 | if (unlikely (!c->serializer->check_success (cmap_prime))) return_trace (false); |
1174 | |
1175 | auto encodingrec_iter = |
1176 | + hb_iter (encodingRecord) |
1177 | | hb_filter ([&] (const EncodingRecord& _) |
1178 | { |
1179 | if ((_.platformID == 0 && _.encodingID == 3) || |
1180 | (_.platformID == 0 && _.encodingID == 4) || |
1181 | (_.platformID == 3 && _.encodingID == 1) || |
1182 | (_.platformID == 3 && _.encodingID == 10) || |
1183 | (this + _.subtable).u.format == 14) |
1184 | return true; |
1185 | |
1186 | return false; |
1187 | }) |
1188 | ; |
1189 | |
1190 | |
1191 | if (unlikely (!encodingrec_iter.len ())) return_trace (false); |
1192 | |
1193 | const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr; |
1194 | bool has_format12 = false; |
1195 | |
1196 | for (const EncodingRecord& _ : encodingrec_iter) |
1197 | { |
1198 | unsigned format = (this + _.subtable).u.format; |
1199 | if (format == 12) has_format12 = true; |
1200 | |
1201 | const EncodingRecord *table = hb_addressof (_); |
1202 | if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table; |
1203 | else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table; |
1204 | else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table; |
1205 | else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table; |
1206 | } |
1207 | |
1208 | if (unlikely (!unicode_bmp && !ms_bmp)) return_trace (false); |
1209 | if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false); |
1210 | |
1211 | auto it = |
1212 | + hb_iter (c->plan->unicodes) |
1213 | | hb_map ([&] (hb_codepoint_t _) |
1214 | { |
1215 | hb_codepoint_t new_gid = HB_MAP_VALUE_INVALID; |
1216 | c->plan->new_gid_for_codepoint (_, &new_gid); |
1217 | return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (_, new_gid); |
1218 | }) |
1219 | | hb_filter ([&] (const hb_pair_t<hb_codepoint_t, hb_codepoint_t> _) |
1220 | { return (_.second != HB_MAP_VALUE_INVALID); }) |
1221 | ; |
1222 | |
1223 | cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan); |
1224 | return_trace (true); |
1225 | } |
1226 | |
1227 | const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const |
1228 | { |
1229 | if (symbol) *symbol = false; |
1230 | |
1231 | const CmapSubtable *subtable; |
1232 | |
1233 | /* Symbol subtable. |
1234 | * Prefer symbol if available. |
1235 | * https://github.com/harfbuzz/harfbuzz/issues/1918 */ |
1236 | if ((subtable = this->find_subtable (3, 0))) |
1237 | { |
1238 | if (symbol) *symbol = true; |
1239 | return subtable; |
1240 | } |
1241 | |
1242 | /* 32-bit subtables. */ |
1243 | if ((subtable = this->find_subtable (3, 10))) return subtable; |
1244 | if ((subtable = this->find_subtable (0, 6))) return subtable; |
1245 | if ((subtable = this->find_subtable (0, 4))) return subtable; |
1246 | |
1247 | /* 16-bit subtables. */ |
1248 | if ((subtable = this->find_subtable (3, 1))) return subtable; |
1249 | if ((subtable = this->find_subtable (0, 3))) return subtable; |
1250 | if ((subtable = this->find_subtable (0, 2))) return subtable; |
1251 | if ((subtable = this->find_subtable (0, 1))) return subtable; |
1252 | if ((subtable = this->find_subtable (0, 0))) return subtable; |
1253 | |
1254 | /* Meh. */ |
1255 | return &Null (CmapSubtable); |
1256 | } |
1257 | |
1258 | struct accelerator_t |
1259 | { |
1260 | void init (hb_face_t *face) |
1261 | { |
1262 | this->table = hb_sanitize_context_t ().reference_table<cmap> (face); |
1263 | bool symbol; |
1264 | this->subtable = table->find_best_subtable (&symbol); |
1265 | this->subtable_uvs = &Null (CmapSubtableFormat14); |
1266 | { |
1267 | const CmapSubtable *st = table->find_subtable (0, 5); |
1268 | if (st && st->u.format == 14) |
1269 | subtable_uvs = &st->u.format14; |
1270 | } |
1271 | |
1272 | this->get_glyph_data = subtable; |
1273 | if (unlikely (symbol)) |
1274 | this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable>; |
1275 | else |
1276 | { |
1277 | switch (subtable->u.format) { |
1278 | /* Accelerate format 4 and format 12. */ |
1279 | default: |
1280 | this->get_glyph_funcZ = get_glyph_from<CmapSubtable>; |
1281 | break; |
1282 | case 12: |
1283 | this->get_glyph_funcZ = get_glyph_from<CmapSubtableFormat12>; |
1284 | break; |
1285 | case 4: |
1286 | { |
1287 | this->format4_accel.init (&subtable->u.format4); |
1288 | this->get_glyph_data = &this->format4_accel; |
1289 | this->get_glyph_funcZ = this->format4_accel.get_glyph_func; |
1290 | break; |
1291 | } |
1292 | } |
1293 | } |
1294 | } |
1295 | |
1296 | void fini () { this->table.destroy (); } |
1297 | |
1298 | bool get_nominal_glyph (hb_codepoint_t unicode, |
1299 | hb_codepoint_t *glyph) const |
1300 | { |
1301 | if (unlikely (!this->get_glyph_funcZ)) return false; |
1302 | return this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph); |
1303 | } |
1304 | unsigned int get_nominal_glyphs (unsigned int count, |
1305 | const hb_codepoint_t *first_unicode, |
1306 | unsigned int unicode_stride, |
1307 | hb_codepoint_t *first_glyph, |
1308 | unsigned int glyph_stride) const |
1309 | { |
1310 | if (unlikely (!this->get_glyph_funcZ)) return 0; |
1311 | |
1312 | hb_cmap_get_glyph_func_t get_glyph_funcZ = this->get_glyph_funcZ; |
1313 | const void *get_glyph_data = this->get_glyph_data; |
1314 | |
1315 | unsigned int done; |
1316 | for (done = 0; |
1317 | done < count && get_glyph_funcZ (get_glyph_data, *first_unicode, first_glyph); |
1318 | done++) |
1319 | { |
1320 | first_unicode = &StructAtOffsetUnaligned<hb_codepoint_t> (first_unicode, unicode_stride); |
1321 | first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride); |
1322 | } |
1323 | return done; |
1324 | } |
1325 | |
1326 | bool get_variation_glyph (hb_codepoint_t unicode, |
1327 | hb_codepoint_t variation_selector, |
1328 | hb_codepoint_t *glyph) const |
1329 | { |
1330 | switch (this->subtable_uvs->get_glyph_variant (unicode, |
1331 | variation_selector, |
1332 | glyph)) |
1333 | { |
1334 | case GLYPH_VARIANT_NOT_FOUND: return false; |
1335 | case GLYPH_VARIANT_FOUND: return true; |
1336 | case GLYPH_VARIANT_USE_DEFAULT: break; |
1337 | } |
1338 | |
1339 | return get_nominal_glyph (unicode, glyph); |
1340 | } |
1341 | |
1342 | void collect_unicodes (hb_set_t *out) const |
1343 | { subtable->collect_unicodes (out); } |
1344 | void collect_variation_selectors (hb_set_t *out) const |
1345 | { subtable_uvs->collect_variation_selectors (out); } |
1346 | void collect_variation_unicodes (hb_codepoint_t variation_selector, |
1347 | hb_set_t *out) const |
1348 | { subtable_uvs->collect_variation_unicodes (variation_selector, out); } |
1349 | |
1350 | protected: |
1351 | typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj, |
1352 | hb_codepoint_t codepoint, |
1353 | hb_codepoint_t *glyph); |
1354 | |
1355 | template <typename Type> |
1356 | HB_INTERNAL static bool get_glyph_from (const void *obj, |
1357 | hb_codepoint_t codepoint, |
1358 | hb_codepoint_t *glyph) |
1359 | { |
1360 | const Type *typed_obj = (const Type *) obj; |
1361 | return typed_obj->get_glyph (codepoint, glyph); |
1362 | } |
1363 | |
1364 | template <typename Type> |
1365 | HB_INTERNAL static bool get_glyph_from_symbol (const void *obj, |
1366 | hb_codepoint_t codepoint, |
1367 | hb_codepoint_t *glyph) |
1368 | { |
1369 | const Type *typed_obj = (const Type *) obj; |
1370 | if (likely (typed_obj->get_glyph (codepoint, glyph))) |
1371 | return true; |
1372 | |
1373 | if (codepoint <= 0x00FFu) |
1374 | { |
1375 | /* For symbol-encoded OpenType fonts, we duplicate the |
1376 | * U+F000..F0FF range at U+0000..U+00FF. That's what |
1377 | * Windows seems to do, and that's hinted about at: |
1378 | * https://docs.microsoft.com/en-us/typography/opentype/spec/recom |
1379 | * under "Non-Standard (Symbol) Fonts". */ |
1380 | return typed_obj->get_glyph (0xF000u + codepoint, glyph); |
1381 | } |
1382 | |
1383 | return false; |
1384 | } |
1385 | |
1386 | private: |
1387 | hb_nonnull_ptr_t<const CmapSubtable> subtable; |
1388 | hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs; |
1389 | |
1390 | hb_cmap_get_glyph_func_t get_glyph_funcZ; |
1391 | const void *get_glyph_data; |
1392 | |
1393 | CmapSubtableFormat4::accelerator_t format4_accel; |
1394 | |
1395 | public: |
1396 | hb_blob_ptr_t<cmap> table; |
1397 | }; |
1398 | |
1399 | protected: |
1400 | |
1401 | const CmapSubtable *find_subtable (unsigned int platform_id, |
1402 | unsigned int encoding_id) const |
1403 | { |
1404 | EncodingRecord key; |
1405 | key.platformID = platform_id; |
1406 | key.encodingID = encoding_id; |
1407 | |
1408 | const EncodingRecord &result = encodingRecord.bsearch (key); |
1409 | if (!result.subtable) |
1410 | return nullptr; |
1411 | |
1412 | return &(this+result.subtable); |
1413 | } |
1414 | |
1415 | const EncodingRecord *find_encodingrec (unsigned int platform_id, |
1416 | unsigned int encoding_id) const |
1417 | { |
1418 | EncodingRecord key; |
1419 | key.platformID = platform_id; |
1420 | key.encodingID = encoding_id; |
1421 | |
1422 | return encodingRecord.as_array ().bsearch (key); |
1423 | } |
1424 | |
1425 | bool find_subtable (unsigned format) const |
1426 | { |
1427 | auto it = |
1428 | + hb_iter (encodingRecord) |
1429 | | hb_map (&EncodingRecord::subtable) |
1430 | | hb_map (hb_add (this)) |
1431 | | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; }) |
1432 | ; |
1433 | |
1434 | return it.len (); |
1435 | } |
1436 | |
1437 | public: |
1438 | |
1439 | bool sanitize (hb_sanitize_context_t *c) const |
1440 | { |
1441 | TRACE_SANITIZE (this); |
1442 | return_trace (c->check_struct (this) && |
1443 | likely (version == 0) && |
1444 | encodingRecord.sanitize (c, this)); |
1445 | } |
1446 | |
1447 | protected: |
1448 | HBUINT16 version; /* Table version number (0). */ |
1449 | SortedArrayOf<EncodingRecord> |
1450 | encodingRecord; /* Encoding tables. */ |
1451 | public: |
1452 | DEFINE_SIZE_ARRAY (4, encodingRecord); |
1453 | }; |
1454 | |
1455 | struct cmap_accelerator_t : cmap::accelerator_t {}; |
1456 | |
1457 | } /* namespace OT */ |
1458 | |
1459 | |
1460 | #endif /* HB_OT_CMAP_TABLE_HH */ |
1461 | |