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-ot-os2-table.hh"
31#include "hb-ot-shaper-arabic-pua.hh"
32#include "hb-open-type.hh"
33#include "hb-set.hh"
34#include "hb-cache.hh"
35
36/*
37 * cmap -- Character to Glyph Index Mapping
38 * https://docs.microsoft.com/en-us/typography/opentype/spec/cmap
39 */
40#define HB_OT_TAG_cmap HB_TAG('c','m','a','p')
41
42namespace OT {
43
44
45struct CmapSubtableFormat0
46{
47 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
48 {
49 hb_codepoint_t gid = codepoint < 256 ? glyphIdArray[codepoint] : 0;
50 if (unlikely (!gid))
51 return false;
52 *glyph = gid;
53 return true;
54 }
55
56 unsigned get_language () const
57 {
58 return language;
59 }
60
61 void collect_unicodes (hb_set_t *out) const
62 {
63 for (unsigned int i = 0; i < 256; i++)
64 if (glyphIdArray[i])
65 out->add (i);
66 }
67
68 void collect_mapping (hb_set_t *unicodes, /* OUT */
69 hb_map_t *mapping /* OUT */) const
70 {
71 for (unsigned i = 0; i < 256; i++)
72 if (glyphIdArray[i])
73 {
74 hb_codepoint_t glyph = glyphIdArray[i];
75 unicodes->add (i);
76 mapping->set (i, glyph);
77 }
78 }
79
80 bool sanitize (hb_sanitize_context_t *c) const
81 {
82 TRACE_SANITIZE (this);
83 return_trace (c->check_struct (this));
84 }
85
86 protected:
87 HBUINT16 format; /* Format number is set to 0. */
88 HBUINT16 length; /* Byte length of this subtable. */
89 HBUINT16 language; /* Ignore. */
90 HBUINT8 glyphIdArray[256];/* An array that maps character
91 * code to glyph index values. */
92 public:
93 DEFINE_SIZE_STATIC (6 + 256);
94};
95
96struct CmapSubtableFormat4
97{
98
99
100 template<typename Iterator,
101 typename Writer,
102 hb_requires (hb_is_iterator (Iterator))>
103 void to_ranges (Iterator it, Writer& range_writer)
104 {
105 hb_codepoint_t start_cp = 0, prev_run_start_cp = 0, run_start_cp = 0, end_cp = 0, last_gid = 0;
106 int run_length = 0 , delta = 0, prev_delta = 0;
107
108 enum {
109 FIRST_SUB_RANGE,
110 FOLLOWING_SUB_RANGE,
111 } mode;
112
113 while (it) {
114 // Start a new range
115 {
116 const auto& pair = *it;
117 start_cp = pair.first;
118 prev_run_start_cp = start_cp;
119 run_start_cp = start_cp;
120 end_cp = start_cp;
121 last_gid = pair.second;
122 run_length = 1;
123 prev_delta = 0;
124 }
125
126 delta = last_gid - start_cp;
127 mode = FIRST_SUB_RANGE;
128 it++;
129
130 while (it) {
131 // Process range
132 const auto& pair = *it;
133 hb_codepoint_t next_cp = pair.first;
134 hb_codepoint_t next_gid = pair.second;
135 if (next_cp != end_cp + 1) {
136 // Current range is over, stop processing.
137 break;
138 }
139
140 if (next_gid == last_gid + 1) {
141 // The current run continues.
142 end_cp = next_cp;
143 run_length++;
144 last_gid = next_gid;
145 it++;
146 continue;
147 }
148
149 // A new run is starting, decide if we want to commit the current run.
150 int split_cost = (mode == FIRST_SUB_RANGE) ? 8 : 16;
151 int run_cost = run_length * 2;
152 if (run_cost >= split_cost) {
153 commit_current_range(start_cp,
154 prev_run_start_cp,
155 run_start_cp,
156 end_cp,
157 delta,
158 prev_delta,
159 split_cost,
160 range_writer);
161 start_cp = next_cp;
162 }
163
164 // Start the new run
165 mode = FOLLOWING_SUB_RANGE;
166 prev_run_start_cp = run_start_cp;
167 run_start_cp = next_cp;
168 end_cp = next_cp;
169 prev_delta = delta;
170 delta = next_gid - run_start_cp;
171 run_length = 1;
172 last_gid = next_gid;
173 it++;
174 }
175
176 // Finalize range
177 commit_current_range (start_cp,
178 prev_run_start_cp,
179 run_start_cp,
180 end_cp,
181 delta,
182 prev_delta,
183 8,
184 range_writer);
185 }
186
187 if (likely (end_cp != 0xFFFF)) {
188 range_writer (0xFFFF, 0xFFFF, 1);
189 }
190 }
191
192 /*
193 * Writes the current range as either one or two ranges depending on what is most efficient.
194 */
195 template<typename Writer>
196 void commit_current_range (hb_codepoint_t start,
197 hb_codepoint_t prev_run_start,
198 hb_codepoint_t run_start,
199 hb_codepoint_t end,
200 int run_delta,
201 int previous_run_delta,
202 int split_cost,
203 Writer& range_writer) {
204 bool should_split = false;
205 if (start < run_start && run_start < end) {
206 int run_cost = (end - run_start + 1) * 2;
207 if (run_cost >= split_cost) {
208 should_split = true;
209 }
210 }
211
212 // TODO(grieger): handle case where delta is legitimately 0, mark range offset array instead?
213 if (should_split) {
214 if (start == prev_run_start)
215 range_writer (start, run_start - 1, previous_run_delta);
216 else
217 range_writer (start, run_start - 1, 0);
218 range_writer (run_start, end, run_delta);
219 return;
220 }
221
222
223 if (start == run_start) {
224 // Range is only a run
225 range_writer (start, end, run_delta);
226 return;
227 }
228
229 // Write only a single non-run range.
230 range_writer (start, end, 0);
231 }
232
233 template<typename Iterator,
234 hb_requires (hb_is_iterator (Iterator))>
235 unsigned serialize_find_segcount (Iterator it) {
236 struct Counter {
237 unsigned segcount = 0;
238
239 void operator() (hb_codepoint_t start,
240 hb_codepoint_t end,
241 int delta) {
242 segcount++;
243 }
244 } counter;
245
246 to_ranges (+it, counter);
247 return counter.segcount;
248 }
249
250
251 template<typename Iterator,
252 hb_requires (hb_is_iterator (Iterator))>
253 bool serialize_start_end_delta_arrays (hb_serialize_context_t *c,
254 Iterator it,
255 int segcount)
256 {
257 struct Writer {
258 hb_serialize_context_t *serializer_;
259 HBUINT16* end_code_;
260 HBUINT16* start_code_;
261 HBINT16* id_delta_;
262 int index_;
263
264 Writer(hb_serialize_context_t *serializer)
265 : serializer_(serializer),
266 end_code_(nullptr),
267 start_code_(nullptr),
268 id_delta_(nullptr),
269 index_ (0) {}
270 void operator() (hb_codepoint_t start,
271 hb_codepoint_t end,
272 int delta) {
273 start_code_[index_] = start;
274 end_code_[index_] = end;
275 id_delta_[index_] = delta;
276 index_++;
277 }
278 } writer(c);
279
280 writer.end_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false);
281 (void) c->allocate_size<HBUINT16> (2); // padding
282 writer.start_code_ = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount, false);
283 writer.id_delta_ = c->allocate_size<HBINT16> (HBINT16::static_size * segcount, false);
284
285 if (unlikely (!writer.end_code_ || !writer.start_code_ || !writer.id_delta_)) return false;
286
287 to_ranges (+it, writer);
288 return true;
289 }
290
291 template<typename Iterator,
292 hb_requires (hb_is_iterator (Iterator))>
293 HBUINT16* serialize_rangeoffset_glyid (hb_serialize_context_t *c,
294 Iterator it,
295 HBUINT16 *endCode,
296 HBUINT16 *startCode,
297 HBINT16 *idDelta,
298 unsigned segcount)
299 {
300 hb_map_t cp_to_gid { it };
301
302 HBUINT16 *idRangeOffset = c->allocate_size<HBUINT16> (HBUINT16::static_size * segcount);
303 if (unlikely (!c->check_success (idRangeOffset))) return nullptr;
304 if (unlikely ((char *)idRangeOffset - (char *)idDelta != (int) segcount * (int) HBINT16::static_size)) return nullptr;
305
306 for (unsigned i : + hb_range (segcount)
307 | hb_filter ([&] (const unsigned _) { return idDelta[_] == 0; }))
308 {
309 idRangeOffset[i] = 2 * (c->start_embed<HBUINT16> () - idRangeOffset - i);
310 for (hb_codepoint_t cp = startCode[i]; cp <= endCode[i]; cp++)
311 {
312 HBUINT16 gid;
313 gid = cp_to_gid[cp];
314 c->copy<HBUINT16> (gid);
315 }
316 }
317
318 return idRangeOffset;
319 }
320
321 template<typename Iterator,
322 hb_requires (hb_is_iterator (Iterator))>
323 void serialize (hb_serialize_context_t *c,
324 Iterator it)
325 {
326 auto format4_iter =
327 + it
328 | hb_filter ([&] (const hb_codepoint_pair_t _)
329 { return _.first <= 0xFFFF; })
330 ;
331
332 if (!format4_iter) return;
333
334 unsigned table_initpos = c->length ();
335 if (unlikely (!c->extend_min (this))) return;
336 this->format = 4;
337
338 hb_vector_t<hb_codepoint_pair_t> cp_to_gid {
339 format4_iter
340 };
341
342 //serialize endCode[], startCode[], idDelta[]
343 HBUINT16* endCode = c->start_embed<HBUINT16> ();
344 unsigned segcount = serialize_find_segcount (cp_to_gid.iter());
345 if (unlikely (!serialize_start_end_delta_arrays (c, cp_to_gid.iter(), segcount)))
346 return;
347
348 HBUINT16 *startCode = endCode + segcount + 1;
349 HBINT16 *idDelta = ((HBINT16*)startCode) + segcount;
350
351 HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c,
352 cp_to_gid.iter (),
353 endCode,
354 startCode,
355 idDelta,
356 segcount);
357 if (unlikely (!c->check_success (idRangeOffset))) return;
358
359 this->length = c->length () - table_initpos;
360 if ((long long) this->length != (long long) c->length () - table_initpos)
361 {
362 // Length overflowed. Discard the current object before setting the error condition, otherwise
363 // discard is a noop which prevents the higher level code from reverting the serializer to the
364 // pre-error state in cmap4 overflow handling code.
365 c->pop_discard ();
366 c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW);
367 return;
368 }
369
370 this->segCountX2 = segcount * 2;
371 this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1;
372 this->searchRange = 2 * (1u << this->entrySelector);
373 this->rangeShift = segcount * 2 > this->searchRange
374 ? 2 * segcount - this->searchRange
375 : 0;
376 }
377
378 unsigned get_language () const
379 {
380 return language;
381 }
382
383 struct accelerator_t
384 {
385 accelerator_t () {}
386 accelerator_t (const CmapSubtableFormat4 *subtable) { init (subtable); }
387
388 void init (const CmapSubtableFormat4 *subtable)
389 {
390 segCount = subtable->segCountX2 / 2;
391 endCount = subtable->values.arrayZ;
392 startCount = endCount + segCount + 1;
393 idDelta = startCount + segCount;
394 idRangeOffset = idDelta + segCount;
395 glyphIdArray = idRangeOffset + segCount;
396 glyphIdArrayLength = (subtable->length - 16 - 8 * segCount) / 2;
397 }
398
399 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
400 {
401 struct CustomRange
402 {
403 int cmp (hb_codepoint_t k,
404 unsigned distance) const
405 {
406 if (k > last) return +1;
407 if (k < (&last)[distance]/*first*/) return -1;
408 return 0;
409 }
410 HBUINT16 last;
411 };
412
413 const HBUINT16 *found = hb_bsearch (codepoint,
414 this->endCount,
415 this->segCount,
416 sizeof (CustomRange),
417 _hb_cmp_method<hb_codepoint_t, CustomRange, unsigned>,
418 this->segCount + 1);
419 if (unlikely (!found))
420 return false;
421 unsigned int i = found - endCount;
422
423 hb_codepoint_t gid;
424 unsigned int rangeOffset = this->idRangeOffset[i];
425 if (rangeOffset == 0)
426 gid = codepoint + this->idDelta[i];
427 else
428 {
429 /* Somebody has been smoking... */
430 unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
431 if (unlikely (index >= this->glyphIdArrayLength))
432 return false;
433 gid = this->glyphIdArray[index];
434 if (unlikely (!gid))
435 return false;
436 gid += this->idDelta[i];
437 }
438 gid &= 0xFFFFu;
439 if (unlikely (!gid))
440 return false;
441 *glyph = gid;
442 return true;
443 }
444
445 HB_INTERNAL static bool get_glyph_func (const void *obj, hb_codepoint_t codepoint, hb_codepoint_t *glyph)
446 { return ((const accelerator_t *) obj)->get_glyph (codepoint, glyph); }
447
448 void collect_unicodes (hb_set_t *out) const
449 {
450 unsigned int count = this->segCount;
451 if (count && this->startCount[count - 1] == 0xFFFFu)
452 count--; /* Skip sentinel segment. */
453 for (unsigned int i = 0; i < count; i++)
454 {
455 hb_codepoint_t start = this->startCount[i];
456 hb_codepoint_t end = this->endCount[i];
457 unsigned int rangeOffset = this->idRangeOffset[i];
458 out->add_range(start, end);
459 if (rangeOffset == 0)
460 {
461 for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
462 {
463 hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu;
464 if (unlikely (!gid))
465 out->del(codepoint);
466 }
467 }
468 else
469 {
470 for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
471 {
472 unsigned int index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
473 if (unlikely (index >= this->glyphIdArrayLength))
474 {
475 out->del_range (codepoint, end);
476 break;
477 }
478 hb_codepoint_t gid = this->glyphIdArray[index];
479 if (unlikely (!gid))
480 out->del(codepoint);
481 }
482 }
483 }
484 }
485
486 void collect_mapping (hb_set_t *unicodes, /* OUT */
487 hb_map_t *mapping /* OUT */) const
488 {
489 // TODO(grieger): optimize similar to collect_unicodes
490 // (ie. use add_range())
491 unsigned count = this->segCount;
492 if (count && this->startCount[count - 1] == 0xFFFFu)
493 count--; /* Skip sentinel segment. */
494 for (unsigned i = 0; i < count; i++)
495 {
496 hb_codepoint_t start = this->startCount[i];
497 hb_codepoint_t end = this->endCount[i];
498 unsigned rangeOffset = this->idRangeOffset[i];
499 if (rangeOffset == 0)
500 {
501 for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
502 {
503 hb_codepoint_t gid = (codepoint + this->idDelta[i]) & 0xFFFFu;
504 if (unlikely (!gid))
505 continue;
506 unicodes->add (codepoint);
507 mapping->set (codepoint, gid);
508 }
509 }
510 else
511 {
512 for (hb_codepoint_t codepoint = start; codepoint <= end; codepoint++)
513 {
514 unsigned index = rangeOffset / 2 + (codepoint - this->startCount[i]) + i - this->segCount;
515 if (unlikely (index >= this->glyphIdArrayLength))
516 break;
517 hb_codepoint_t gid = this->glyphIdArray[index];
518 if (unlikely (!gid))
519 continue;
520 unicodes->add (codepoint);
521 mapping->set (codepoint, gid);
522 }
523 }
524 }
525 }
526
527 const HBUINT16 *endCount;
528 const HBUINT16 *startCount;
529 const HBUINT16 *idDelta;
530 const HBUINT16 *idRangeOffset;
531 const HBUINT16 *glyphIdArray;
532 unsigned int segCount;
533 unsigned int glyphIdArrayLength;
534 };
535
536 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
537 {
538 accelerator_t accel (this);
539 return accel.get_glyph_func (&accel, codepoint, glyph);
540 }
541 void collect_unicodes (hb_set_t *out) const
542 {
543 accelerator_t accel (this);
544 accel.collect_unicodes (out);
545 }
546
547 void collect_mapping (hb_set_t *unicodes, /* OUT */
548 hb_map_t *mapping /* OUT */) const
549 {
550 accelerator_t accel (this);
551 accel.collect_mapping (unicodes, mapping);
552 }
553
554 bool sanitize (hb_sanitize_context_t *c) const
555 {
556 TRACE_SANITIZE (this);
557 if (unlikely (!c->check_struct (this)))
558 return_trace (false);
559
560 if (unlikely (!c->check_range (this, length)))
561 {
562 /* Some broken fonts have too long of a "length" value.
563 * If that is the case, just change the value to truncate
564 * the subtable at the end of the blob. */
565 uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535,
566 (uintptr_t) (c->end -
567 (char *) this));
568 if (!c->try_set (&length, new_length))
569 return_trace (false);
570 }
571
572 return_trace (16 + 4 * (unsigned int) segCountX2 <= length);
573 }
574
575
576
577 protected:
578 HBUINT16 format; /* Format number is set to 4. */
579 HBUINT16 length; /* This is the length in bytes of the
580 * subtable. */
581 HBUINT16 language; /* Ignore. */
582 HBUINT16 segCountX2; /* 2 x segCount. */
583 HBUINT16 searchRange; /* 2 * (2**floor(log2(segCount))) */
584 HBUINT16 entrySelector; /* log2(searchRange/2) */
585 HBUINT16 rangeShift; /* 2 x segCount - searchRange */
586
587 UnsizedArrayOf<HBUINT16>
588 values;
589#if 0
590 HBUINT16 endCount[segCount]; /* End characterCode for each segment,
591 * last=0xFFFFu. */
592 HBUINT16 reservedPad; /* Set to 0. */
593 HBUINT16 startCount[segCount]; /* Start character code for each segment. */
594 HBINT16 idDelta[segCount]; /* Delta for all character codes in segment. */
595 HBUINT16 idRangeOffset[segCount];/* Offsets into glyphIdArray or 0 */
596 UnsizedArrayOf<HBUINT16>
597 glyphIdArray; /* Glyph index array (arbitrary length) */
598#endif
599
600 public:
601 DEFINE_SIZE_ARRAY (14, values);
602};
603
604struct CmapSubtableLongGroup
605{
606 friend struct CmapSubtableFormat12;
607 friend struct CmapSubtableFormat13;
608 template<typename U>
609 friend struct CmapSubtableLongSegmented;
610 friend struct cmap;
611
612 int cmp (hb_codepoint_t codepoint) const
613 {
614 if (codepoint < startCharCode) return -1;
615 if (codepoint > endCharCode) return +1;
616 return 0;
617 }
618
619 bool sanitize (hb_sanitize_context_t *c) const
620 {
621 TRACE_SANITIZE (this);
622 return_trace (c->check_struct (this));
623 }
624
625 private:
626 HBUINT32 startCharCode; /* First character code in this group. */
627 HBUINT32 endCharCode; /* Last character code in this group. */
628 HBUINT32 glyphID; /* Glyph index; interpretation depends on
629 * subtable format. */
630 public:
631 DEFINE_SIZE_STATIC (12);
632};
633DECLARE_NULL_NAMESPACE_BYTES (OT, CmapSubtableLongGroup);
634
635template <typename UINT>
636struct CmapSubtableTrimmed
637{
638 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
639 {
640 /* Rely on our implicit array bound-checking. */
641 hb_codepoint_t gid = glyphIdArray[codepoint - startCharCode];
642 if (unlikely (!gid))
643 return false;
644 *glyph = gid;
645 return true;
646 }
647
648 unsigned get_language () const
649 {
650 return language;
651 }
652
653 void collect_unicodes (hb_set_t *out) const
654 {
655 hb_codepoint_t start = startCharCode;
656 unsigned int count = glyphIdArray.len;
657 for (unsigned int i = 0; i < count; i++)
658 if (glyphIdArray[i])
659 out->add (start + i);
660 }
661
662 void collect_mapping (hb_set_t *unicodes, /* OUT */
663 hb_map_t *mapping /* OUT */) const
664 {
665 hb_codepoint_t start_cp = startCharCode;
666 unsigned count = glyphIdArray.len;
667 for (unsigned i = 0; i < count; i++)
668 if (glyphIdArray[i])
669 {
670 hb_codepoint_t unicode = start_cp + i;
671 hb_codepoint_t glyphid = glyphIdArray[i];
672 unicodes->add (unicode);
673 mapping->set (unicode, glyphid);
674 }
675 }
676
677 bool sanitize (hb_sanitize_context_t *c) const
678 {
679 TRACE_SANITIZE (this);
680 return_trace (c->check_struct (this) && glyphIdArray.sanitize (c));
681 }
682
683 protected:
684 UINT formatReserved; /* Subtable format and (maybe) padding. */
685 UINT length; /* Byte length of this subtable. */
686 UINT language; /* Ignore. */
687 UINT startCharCode; /* First character code covered. */
688 ArrayOf<HBGlyphID16, UINT>
689 glyphIdArray; /* Array of glyph index values for character
690 * codes in the range. */
691 public:
692 DEFINE_SIZE_ARRAY (5 * sizeof (UINT), glyphIdArray);
693};
694
695struct CmapSubtableFormat6 : CmapSubtableTrimmed<HBUINT16> {};
696struct CmapSubtableFormat10 : CmapSubtableTrimmed<HBUINT32> {};
697
698template <typename T>
699struct CmapSubtableLongSegmented
700{
701 friend struct cmap;
702
703 bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const
704 {
705 hb_codepoint_t gid = T::group_get_glyph (groups.bsearch (codepoint), codepoint);
706 if (unlikely (!gid))
707 return false;
708 *glyph = gid;
709 return true;
710 }
711
712 unsigned get_language () const
713 {
714 return language;
715 }
716
717 void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
718 {
719 for (unsigned int i = 0; i < this->groups.len; i++)
720 {
721 hb_codepoint_t start = this->groups[i].startCharCode;
722 hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
723 (hb_codepoint_t) HB_UNICODE_MAX);
724 hb_codepoint_t gid = this->groups[i].glyphID;
725 if (!gid)
726 {
727 /* Intention is: if (hb_is_same (T, CmapSubtableFormat13)) continue; */
728 if (! T::group_get_glyph (this->groups[i], end)) continue;
729 start++;
730 gid++;
731 }
732 if (unlikely ((unsigned int) gid >= num_glyphs)) continue;
733 if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs))
734 end = start + (hb_codepoint_t) num_glyphs - gid;
735
736 out->add_range (start, hb_min (end, 0x10FFFFu));
737 }
738 }
739
740 void collect_mapping (hb_set_t *unicodes, /* OUT */
741 hb_map_t *mapping, /* OUT */
742 unsigned num_glyphs) const
743 {
744 hb_codepoint_t last_end = 0;
745 for (unsigned i = 0; i < this->groups.len; i++)
746 {
747 hb_codepoint_t start = this->groups[i].startCharCode;
748 hb_codepoint_t end = hb_min ((hb_codepoint_t) this->groups[i].endCharCode,
749 (hb_codepoint_t) HB_UNICODE_MAX);
750 if (unlikely (start > end || start < last_end)) {
751 // Range is not in order and is invalid, skip it.
752 continue;
753 }
754 last_end = end;
755
756
757 hb_codepoint_t gid = this->groups[i].glyphID;
758 if (!gid)
759 {
760 if (T::formatNumber == 13) continue;
761 start++;
762 gid++;
763 }
764 if (unlikely ((unsigned int) gid >= num_glyphs)) continue;
765 if (unlikely ((unsigned int) (gid + end - start) >= num_glyphs))
766 end = start + (hb_codepoint_t) num_glyphs - gid;
767
768 mapping->alloc (mapping->get_population () + end - start + 1);
769
770 for (unsigned cp = start; cp <= end; cp++)
771 {
772 unicodes->add (cp);
773 mapping->set (cp, gid);
774 gid += T::increment;
775 }
776 }
777 }
778
779 bool sanitize (hb_sanitize_context_t *c) const
780 {
781 TRACE_SANITIZE (this);
782 return_trace (c->check_struct (this) && groups.sanitize (c));
783 }
784
785 protected:
786 HBUINT16 format; /* Subtable format; set to 12. */
787 HBUINT16 reserved; /* Reserved; set to 0. */
788 HBUINT32 length; /* Byte length of this subtable. */
789 HBUINT32 language; /* Ignore. */
790 SortedArray32Of<CmapSubtableLongGroup>
791 groups; /* Groupings. */
792 public:
793 DEFINE_SIZE_ARRAY (16, groups);
794};
795
796struct CmapSubtableFormat12 : CmapSubtableLongSegmented<CmapSubtableFormat12>
797{
798 static constexpr int increment = 1;
799 static constexpr int formatNumber = 12;
800
801 static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
802 hb_codepoint_t u)
803 { return likely (group.startCharCode <= group.endCharCode) ?
804 group.glyphID + (u - group.startCharCode) : 0; }
805
806
807 template<typename Iterator,
808 hb_requires (hb_is_iterator (Iterator))>
809 void serialize (hb_serialize_context_t *c,
810 Iterator it)
811 {
812 if (!it) return;
813 unsigned table_initpos = c->length ();
814 if (unlikely (!c->extend_min (this))) return;
815
816 hb_codepoint_t startCharCode = (hb_codepoint_t) -1, endCharCode = (hb_codepoint_t) -1;
817 hb_codepoint_t glyphID = 0;
818
819 for (const auto& _ : +it)
820 {
821 if (startCharCode == (hb_codepoint_t) -1)
822 {
823 startCharCode = _.first;
824 endCharCode = _.first;
825 glyphID = _.second;
826 }
827 else if (!_is_gid_consecutive (endCharCode, startCharCode, glyphID, _.first, _.second))
828 {
829 CmapSubtableLongGroup grouprecord;
830 grouprecord.startCharCode = startCharCode;
831 grouprecord.endCharCode = endCharCode;
832 grouprecord.glyphID = glyphID;
833 c->copy<CmapSubtableLongGroup> (grouprecord);
834
835 startCharCode = _.first;
836 endCharCode = _.first;
837 glyphID = _.second;
838 }
839 else
840 endCharCode = _.first;
841 }
842
843 CmapSubtableLongGroup record;
844 record.startCharCode = startCharCode;
845 record.endCharCode = endCharCode;
846 record.glyphID = glyphID;
847 c->copy<CmapSubtableLongGroup> (record);
848
849 this->format = 12;
850 this->reserved = 0;
851 this->length = c->length () - table_initpos;
852 this->groups.len = (this->length - min_size) / CmapSubtableLongGroup::static_size;
853 }
854
855 static size_t get_sub_table_size (const hb_sorted_vector_t<CmapSubtableLongGroup> &groups_data)
856 { return 16 + 12 * groups_data.length; }
857
858 private:
859 static bool _is_gid_consecutive (hb_codepoint_t endCharCode,
860 hb_codepoint_t startCharCode,
861 hb_codepoint_t glyphID,
862 hb_codepoint_t cp,
863 hb_codepoint_t new_gid)
864 {
865 return (cp - 1 == endCharCode) &&
866 new_gid == glyphID + (cp - startCharCode);
867 }
868
869};
870
871struct CmapSubtableFormat13 : CmapSubtableLongSegmented<CmapSubtableFormat13>
872{
873 static constexpr int increment = 0;
874 static constexpr int formatNumber = 13;
875
876 static hb_codepoint_t group_get_glyph (const CmapSubtableLongGroup &group,
877 hb_codepoint_t u HB_UNUSED)
878 { return group.glyphID; }
879};
880
881typedef enum
882{
883 GLYPH_VARIANT_NOT_FOUND = 0,
884 GLYPH_VARIANT_FOUND = 1,
885 GLYPH_VARIANT_USE_DEFAULT = 2
886} glyph_variant_t;
887
888struct UnicodeValueRange
889{
890 int cmp (const hb_codepoint_t &codepoint) const
891 {
892 if (codepoint < startUnicodeValue) return -1;
893 if (codepoint > startUnicodeValue + additionalCount) return +1;
894 return 0;
895 }
896
897 bool sanitize (hb_sanitize_context_t *c) const
898 {
899 TRACE_SANITIZE (this);
900 return_trace (c->check_struct (this));
901 }
902
903 HBUINT24 startUnicodeValue; /* First value in this range. */
904 HBUINT8 additionalCount; /* Number of additional values in this
905 * range. */
906 public:
907 DEFINE_SIZE_STATIC (4);
908};
909
910struct DefaultUVS : SortedArray32Of<UnicodeValueRange>
911{
912 void collect_unicodes (hb_set_t *out) const
913 {
914 unsigned int count = len;
915 for (unsigned int i = 0; i < count; i++)
916 {
917 hb_codepoint_t first = arrayZ[i].startUnicodeValue;
918 hb_codepoint_t last = hb_min ((hb_codepoint_t) (first + arrayZ[i].additionalCount),
919 (hb_codepoint_t) HB_UNICODE_MAX);
920 out->add_range (first, last);
921 }
922 }
923
924 DefaultUVS* copy (hb_serialize_context_t *c,
925 const hb_set_t *unicodes) const
926 {
927 auto *out = c->start_embed<DefaultUVS> ();
928 auto snap = c->snapshot ();
929
930 HBUINT32 len;
931 len = 0;
932 if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;
933 unsigned init_len = c->length ();
934
935 if (this->len > unicodes->get_population () * hb_bit_storage ((unsigned) this->len))
936 {
937 hb_codepoint_t start = HB_SET_VALUE_INVALID;
938 hb_codepoint_t end = HB_SET_VALUE_INVALID;
939
940 for (auto u : *unicodes)
941 {
942 if (!as_array ().bsearch (u))
943 continue;
944 if (start == HB_SET_VALUE_INVALID)
945 {
946 start = u;
947 end = start - 1;
948 }
949 if (end + 1 != u || end - start == 255)
950 {
951 UnicodeValueRange rec;
952 rec.startUnicodeValue = start;
953 rec.additionalCount = end - start;
954 c->copy<UnicodeValueRange> (rec);
955 start = u;
956 }
957 end = u;
958 }
959 if (start != HB_SET_VALUE_INVALID)
960 {
961 UnicodeValueRange rec;
962 rec.startUnicodeValue = start;
963 rec.additionalCount = end - start;
964 c->copy<UnicodeValueRange> (rec);
965 }
966
967 }
968 else
969 {
970 hb_codepoint_t lastCode = HB_SET_VALUE_INVALID;
971 int count = -1;
972
973 for (const UnicodeValueRange& _ : *this)
974 {
975 hb_codepoint_t curEntry = (hb_codepoint_t) (_.startUnicodeValue - 1);
976 hb_codepoint_t end = curEntry + _.additionalCount + 2;
977
978 for (; unicodes->next (&curEntry) && curEntry < end;)
979 {
980 count += 1;
981 if (lastCode == HB_SET_VALUE_INVALID)
982 lastCode = curEntry;
983 else if (lastCode + count != curEntry)
984 {
985 UnicodeValueRange rec;
986 rec.startUnicodeValue = lastCode;
987 rec.additionalCount = count - 1;
988 c->copy<UnicodeValueRange> (rec);
989
990 lastCode = curEntry;
991 count = 0;
992 }
993 }
994 }
995
996 if (lastCode != HB_MAP_VALUE_INVALID)
997 {
998 UnicodeValueRange rec;
999 rec.startUnicodeValue = lastCode;
1000 rec.additionalCount = count;
1001 c->copy<UnicodeValueRange> (rec);
1002 }
1003 }
1004
1005 if (c->length () - init_len == 0)
1006 {
1007 c->revert (snap);
1008 return nullptr;
1009 }
1010 else
1011 {
1012 if (unlikely (!c->check_assign (out->len,
1013 (c->length () - init_len) / UnicodeValueRange::static_size,
1014 HB_SERIALIZE_ERROR_INT_OVERFLOW))) return nullptr;
1015 return out;
1016 }
1017 }
1018
1019 public:
1020 DEFINE_SIZE_ARRAY (4, *this);
1021};
1022
1023struct UVSMapping
1024{
1025 int cmp (const hb_codepoint_t &codepoint) const
1026 { return unicodeValue.cmp (codepoint); }
1027
1028 bool sanitize (hb_sanitize_context_t *c) const
1029 {
1030 TRACE_SANITIZE (this);
1031 return_trace (c->check_struct (this));
1032 }
1033
1034 HBUINT24 unicodeValue; /* Base Unicode value of the UVS */
1035 HBGlyphID16 glyphID; /* Glyph ID of the UVS */
1036 public:
1037 DEFINE_SIZE_STATIC (5);
1038};
1039
1040struct NonDefaultUVS : SortedArray32Of<UVSMapping>
1041{
1042 void collect_unicodes (hb_set_t *out) const
1043 {
1044 for (const auto& a : as_array ())
1045 out->add (a.unicodeValue);
1046 }
1047
1048 void collect_mapping (hb_set_t *unicodes, /* OUT */
1049 hb_map_t *mapping /* OUT */) const
1050 {
1051 for (const auto& a : as_array ())
1052 {
1053 hb_codepoint_t unicode = a.unicodeValue;
1054 hb_codepoint_t glyphid = a.glyphID;
1055 unicodes->add (unicode);
1056 mapping->set (unicode, glyphid);
1057 }
1058 }
1059
1060 void closure_glyphs (const hb_set_t *unicodes,
1061 hb_set_t *glyphset) const
1062 {
1063 + as_array ()
1064 | hb_filter (unicodes, &UVSMapping::unicodeValue)
1065 | hb_map (&UVSMapping::glyphID)
1066 | hb_sink (glyphset)
1067 ;
1068 }
1069
1070 NonDefaultUVS* copy (hb_serialize_context_t *c,
1071 const hb_set_t *unicodes,
1072 const hb_set_t *glyphs_requested,
1073 const hb_map_t *glyph_map) const
1074 {
1075 auto *out = c->start_embed<NonDefaultUVS> ();
1076 auto it =
1077 + as_array ()
1078 | hb_filter ([&] (const UVSMapping& _)
1079 {
1080 return unicodes->has (_.unicodeValue) || glyphs_requested->has (_.glyphID);
1081 })
1082 ;
1083
1084 if (!it) return nullptr;
1085
1086 HBUINT32 len;
1087 len = it.len ();
1088 if (unlikely (!c->copy<HBUINT32> (len))) return nullptr;
1089
1090 for (const UVSMapping& _ : it)
1091 {
1092 UVSMapping mapping;
1093 mapping.unicodeValue = _.unicodeValue;
1094 mapping.glyphID = glyph_map->get (_.glyphID);
1095 c->copy<UVSMapping> (mapping);
1096 }
1097
1098 return out;
1099 }
1100
1101 public:
1102 DEFINE_SIZE_ARRAY (4, *this);
1103};
1104
1105struct VariationSelectorRecord
1106{
1107 glyph_variant_t get_glyph (hb_codepoint_t codepoint,
1108 hb_codepoint_t *glyph,
1109 const void *base) const
1110 {
1111 if ((base+defaultUVS).bfind (codepoint))
1112 return GLYPH_VARIANT_USE_DEFAULT;
1113 const UVSMapping &nonDefault = (base+nonDefaultUVS).bsearch (codepoint);
1114 if (nonDefault.glyphID)
1115 {
1116 *glyph = nonDefault.glyphID;
1117 return GLYPH_VARIANT_FOUND;
1118 }
1119 return GLYPH_VARIANT_NOT_FOUND;
1120 }
1121
1122 VariationSelectorRecord(const VariationSelectorRecord& other)
1123 {
1124 *this = other;
1125 }
1126
1127 void operator= (const VariationSelectorRecord& other)
1128 {
1129 varSelector = other.varSelector;
1130 HBUINT32 offset = other.defaultUVS;
1131 defaultUVS = offset;
1132 offset = other.nonDefaultUVS;
1133 nonDefaultUVS = offset;
1134 }
1135
1136 void collect_unicodes (hb_set_t *out, const void *base) const
1137 {
1138 (base+defaultUVS).collect_unicodes (out);
1139 (base+nonDefaultUVS).collect_unicodes (out);
1140 }
1141
1142 void collect_mapping (const void *base,
1143 hb_set_t *unicodes, /* OUT */
1144 hb_map_t *mapping /* OUT */) const
1145 {
1146 (base+defaultUVS).collect_unicodes (unicodes);
1147 (base+nonDefaultUVS).collect_mapping (unicodes, mapping);
1148 }
1149
1150 int cmp (const hb_codepoint_t &variation_selector) const
1151 { return varSelector.cmp (variation_selector); }
1152
1153 bool sanitize (hb_sanitize_context_t *c, const void *base) const
1154 {
1155 TRACE_SANITIZE (this);
1156 return_trace (c->check_struct (this) &&
1157 defaultUVS.sanitize (c, base) &&
1158 nonDefaultUVS.sanitize (c, base));
1159 }
1160
1161 hb_pair_t<unsigned, unsigned>
1162 copy (hb_serialize_context_t *c,
1163 const hb_set_t *unicodes,
1164 const hb_set_t *glyphs_requested,
1165 const hb_map_t *glyph_map,
1166 const void *base) const
1167 {
1168 auto snap = c->snapshot ();
1169 auto *out = c->embed<VariationSelectorRecord> (*this);
1170 if (unlikely (!out)) return hb_pair (0, 0);
1171
1172 out->defaultUVS = 0;
1173 out->nonDefaultUVS = 0;
1174
1175 unsigned non_default_uvs_objidx = 0;
1176 if (nonDefaultUVS != 0)
1177 {
1178 c->push ();
1179 if (c->copy (base+nonDefaultUVS, unicodes, glyphs_requested, glyph_map))
1180 non_default_uvs_objidx = c->pop_pack ();
1181 else c->pop_discard ();
1182 }
1183
1184 unsigned default_uvs_objidx = 0;
1185 if (defaultUVS != 0)
1186 {
1187 c->push ();
1188 if (c->copy (base+defaultUVS, unicodes))
1189 default_uvs_objidx = c->pop_pack ();
1190 else c->pop_discard ();
1191 }
1192
1193
1194 if (!default_uvs_objidx && !non_default_uvs_objidx)
1195 c->revert (snap);
1196
1197 return hb_pair (default_uvs_objidx, non_default_uvs_objidx);
1198 }
1199
1200 HBUINT24 varSelector; /* Variation selector. */
1201 Offset32To<DefaultUVS>
1202 defaultUVS; /* Offset to Default UVS Table. May be 0. */
1203 Offset32To<NonDefaultUVS>
1204 nonDefaultUVS; /* Offset to Non-Default UVS Table. May be 0. */
1205 public:
1206 DEFINE_SIZE_STATIC (11);
1207};
1208
1209struct CmapSubtableFormat14
1210{
1211 glyph_variant_t get_glyph_variant (hb_codepoint_t codepoint,
1212 hb_codepoint_t variation_selector,
1213 hb_codepoint_t *glyph) const
1214 { return record.bsearch (variation_selector).get_glyph (codepoint, glyph, this); }
1215
1216 void collect_variation_selectors (hb_set_t *out) const
1217 {
1218 for (const auto& a : record.as_array ())
1219 out->add (a.varSelector);
1220 }
1221 void collect_variation_unicodes (hb_codepoint_t variation_selector,
1222 hb_set_t *out) const
1223 { record.bsearch (variation_selector).collect_unicodes (out, this); }
1224
1225 void serialize (hb_serialize_context_t *c,
1226 const hb_set_t *unicodes,
1227 const hb_set_t *glyphs_requested,
1228 const hb_map_t *glyph_map,
1229 const void *base)
1230 {
1231 auto snap = c->snapshot ();
1232 unsigned table_initpos = c->length ();
1233 const char* init_tail = c->tail;
1234
1235 if (unlikely (!c->extend_min (this))) return;
1236 this->format = 14;
1237
1238 auto src_tbl = reinterpret_cast<const CmapSubtableFormat14*> (base);
1239
1240 /*
1241 * Some versions of OTS require that offsets are in order. Due to the use
1242 * of push()/pop_pack() serializing the variation records in order results
1243 * in the offsets being in reverse order (first record has the largest
1244 * offset). While this is perfectly valid, it will cause some versions of
1245 * OTS to consider this table bad.
1246 *
1247 * So to prevent this issue we serialize the variation records in reverse
1248 * order, so that the offsets are ordered from small to large. Since
1249 * variation records are supposed to be in increasing order of varSelector
1250 * we then have to reverse the order of the written variation selector
1251 * records after everything is finalized.
1252 */
1253 hb_vector_t<hb_pair_t<unsigned, unsigned>> obj_indices;
1254 for (int i = src_tbl->record.len - 1; i >= 0; i--)
1255 {
1256 hb_pair_t<unsigned, unsigned> result = src_tbl->record[i].copy (c, unicodes, glyphs_requested, glyph_map, base);
1257 if (result.first || result.second)
1258 obj_indices.push (result);
1259 }
1260
1261 if (c->length () - table_initpos == CmapSubtableFormat14::min_size)
1262 {
1263 c->revert (snap);
1264 return;
1265 }
1266
1267 if (unlikely (!c->check_success (!obj_indices.in_error ())))
1268 return;
1269
1270 int tail_len = init_tail - c->tail;
1271 c->check_assign (this->length, c->length () - table_initpos + tail_len,
1272 HB_SERIALIZE_ERROR_INT_OVERFLOW);
1273 c->check_assign (this->record.len,
1274 (c->length () - table_initpos - CmapSubtableFormat14::min_size) /
1275 VariationSelectorRecord::static_size,
1276 HB_SERIALIZE_ERROR_INT_OVERFLOW);
1277
1278 /* Correct the incorrect write order by reversing the order of the variation
1279 records array. */
1280 _reverse_variation_records ();
1281
1282 /* Now that records are in the right order, we can set up the offsets. */
1283 _add_links_to_variation_records (c, obj_indices);
1284 }
1285
1286 void _reverse_variation_records ()
1287 {
1288 record.as_array ().reverse ();
1289 }
1290
1291 void _add_links_to_variation_records (hb_serialize_context_t *c,
1292 const hb_vector_t<hb_pair_t<unsigned, unsigned>>& obj_indices)
1293 {
1294 for (unsigned i = 0; i < obj_indices.length; i++)
1295 {
1296 /*
1297 * Since the record array has been reversed (see comments in copy())
1298 * but obj_indices has not been, the indices at obj_indices[i]
1299 * are for the variation record at record[j].
1300 */
1301 int j = obj_indices.length - 1 - i;
1302 c->add_link (record[j].defaultUVS, obj_indices[i].first);
1303 c->add_link (record[j].nonDefaultUVS, obj_indices[i].second);
1304 }
1305 }
1306
1307 void closure_glyphs (const hb_set_t *unicodes,
1308 hb_set_t *glyphset) const
1309 {
1310 + hb_iter (record)
1311 | hb_filter (hb_bool, &VariationSelectorRecord::nonDefaultUVS)
1312 | hb_map (&VariationSelectorRecord::nonDefaultUVS)
1313 | hb_map (hb_add (this))
1314 | hb_apply ([=] (const NonDefaultUVS& _) { _.closure_glyphs (unicodes, glyphset); })
1315 ;
1316 }
1317
1318 void collect_unicodes (hb_set_t *out) const
1319 {
1320 for (const VariationSelectorRecord& _ : record)
1321 _.collect_unicodes (out, this);
1322 }
1323
1324 void collect_mapping (hb_set_t *unicodes, /* OUT */
1325 hb_map_t *mapping /* OUT */) const
1326 {
1327 for (const VariationSelectorRecord& _ : record)
1328 _.collect_mapping (this, unicodes, mapping);
1329 }
1330
1331 bool sanitize (hb_sanitize_context_t *c) const
1332 {
1333 TRACE_SANITIZE (this);
1334 return_trace (c->check_struct (this) &&
1335 record.sanitize (c, this));
1336 }
1337
1338 protected:
1339 HBUINT16 format; /* Format number is set to 14. */
1340 HBUINT32 length; /* Byte length of this subtable. */
1341 SortedArray32Of<VariationSelectorRecord>
1342 record; /* Variation selector records; sorted
1343 * in increasing order of `varSelector'. */
1344 public:
1345 DEFINE_SIZE_ARRAY (10, record);
1346};
1347
1348struct CmapSubtable
1349{
1350 /* Note: We intentionally do NOT implement subtable formats 2 and 8. */
1351
1352 bool get_glyph (hb_codepoint_t codepoint,
1353 hb_codepoint_t *glyph) const
1354 {
1355 switch (u.format) {
1356 case 0: return u.format0 .get_glyph (codepoint, glyph);
1357 case 4: return u.format4 .get_glyph (codepoint, glyph);
1358 case 6: return u.format6 .get_glyph (codepoint, glyph);
1359 case 10: return u.format10.get_glyph (codepoint, glyph);
1360 case 12: return u.format12.get_glyph (codepoint, glyph);
1361 case 13: return u.format13.get_glyph (codepoint, glyph);
1362 case 14:
1363 default: return false;
1364 }
1365 }
1366 void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const
1367 {
1368 switch (u.format) {
1369 case 0: u.format0 .collect_unicodes (out); return;
1370 case 4: u.format4 .collect_unicodes (out); return;
1371 case 6: u.format6 .collect_unicodes (out); return;
1372 case 10: u.format10.collect_unicodes (out); return;
1373 case 12: u.format12.collect_unicodes (out, num_glyphs); return;
1374 case 13: u.format13.collect_unicodes (out, num_glyphs); return;
1375 case 14:
1376 default: return;
1377 }
1378 }
1379
1380 void collect_mapping (hb_set_t *unicodes, /* OUT */
1381 hb_map_t *mapping, /* OUT */
1382 unsigned num_glyphs = UINT_MAX) const
1383 {
1384 switch (u.format) {
1385 case 0: u.format0 .collect_mapping (unicodes, mapping); return;
1386 case 4: u.format4 .collect_mapping (unicodes, mapping); return;
1387 case 6: u.format6 .collect_mapping (unicodes, mapping); return;
1388 case 10: u.format10.collect_mapping (unicodes, mapping); return;
1389 case 12: u.format12.collect_mapping (unicodes, mapping, num_glyphs); return;
1390 case 13: u.format13.collect_mapping (unicodes, mapping, num_glyphs); return;
1391 case 14:
1392 default: return;
1393 }
1394 }
1395
1396 unsigned get_language () const
1397 {
1398 switch (u.format) {
1399 case 0: return u.format0 .get_language ();
1400 case 4: return u.format4 .get_language ();
1401 case 6: return u.format6 .get_language ();
1402 case 10: return u.format10.get_language ();
1403 case 12: return u.format12.get_language ();
1404 case 13: return u.format13.get_language ();
1405 case 14:
1406 default: return 0;
1407 }
1408 }
1409
1410 template<typename Iterator,
1411 hb_requires (hb_is_iterator (Iterator))>
1412 void serialize (hb_serialize_context_t *c,
1413 Iterator it,
1414 unsigned format,
1415 const hb_subset_plan_t *plan,
1416 const void *base)
1417 {
1418 switch (format) {
1419 case 4: return u.format4.serialize (c, it);
1420 case 12: return u.format12.serialize (c, it);
1421 case 14: return u.format14.serialize (c, &plan->unicodes, &plan->glyphs_requested, plan->glyph_map, base);
1422 default: return;
1423 }
1424 }
1425
1426 bool sanitize (hb_sanitize_context_t *c) const
1427 {
1428 TRACE_SANITIZE (this);
1429 if (!u.format.sanitize (c)) return_trace (false);
1430 switch (u.format) {
1431 case 0: return_trace (u.format0 .sanitize (c));
1432 case 4: return_trace (u.format4 .sanitize (c));
1433 case 6: return_trace (u.format6 .sanitize (c));
1434 case 10: return_trace (u.format10.sanitize (c));
1435 case 12: return_trace (u.format12.sanitize (c));
1436 case 13: return_trace (u.format13.sanitize (c));
1437 case 14: return_trace (u.format14.sanitize (c));
1438 default:return_trace (true);
1439 }
1440 }
1441
1442 public:
1443 union {
1444 HBUINT16 format; /* Format identifier */
1445 CmapSubtableFormat0 format0;
1446 CmapSubtableFormat4 format4;
1447 CmapSubtableFormat6 format6;
1448 CmapSubtableFormat10 format10;
1449 CmapSubtableFormat12 format12;
1450 CmapSubtableFormat13 format13;
1451 CmapSubtableFormat14 format14;
1452 } u;
1453 public:
1454 DEFINE_SIZE_UNION (2, format);
1455};
1456
1457
1458struct EncodingRecord
1459{
1460 int cmp (const EncodingRecord &other) const
1461 {
1462 int ret;
1463 ret = platformID.cmp (other.platformID);
1464 if (ret) return ret;
1465 ret = encodingID.cmp (other.encodingID);
1466 if (ret) return ret;
1467 return 0;
1468 }
1469
1470 bool sanitize (hb_sanitize_context_t *c, const void *base) const
1471 {
1472 TRACE_SANITIZE (this);
1473 return_trace (c->check_struct (this) &&
1474 subtable.sanitize (c, base));
1475 }
1476
1477 template<typename Iterator,
1478 hb_requires (hb_is_iterator (Iterator))>
1479 EncodingRecord* copy (hb_serialize_context_t *c,
1480 Iterator it,
1481 unsigned format,
1482 const void *base,
1483 const hb_subset_plan_t *plan,
1484 /* INOUT */ unsigned *objidx) const
1485 {
1486 TRACE_SERIALIZE (this);
1487 auto snap = c->snapshot ();
1488 auto *out = c->embed (this);
1489 if (unlikely (!out)) return_trace (nullptr);
1490 out->subtable = 0;
1491
1492 if (*objidx == 0)
1493 {
1494 CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
1495 unsigned origin_length = c->length ();
1496 cmapsubtable->serialize (c, it, format, plan, &(base+subtable));
1497 if (c->length () - origin_length > 0) *objidx = c->pop_pack ();
1498 else c->pop_discard ();
1499 }
1500
1501 if (*objidx == 0)
1502 {
1503 c->revert (snap);
1504 return_trace (nullptr);
1505 }
1506
1507 c->add_link (out->subtable, *objidx);
1508 return_trace (out);
1509 }
1510
1511 HBUINT16 platformID; /* Platform ID. */
1512 HBUINT16 encodingID; /* Platform-specific encoding ID. */
1513 Offset32To<CmapSubtable>
1514 subtable; /* Byte offset from beginning of table to the subtable for this encoding. */
1515 public:
1516 DEFINE_SIZE_STATIC (8);
1517};
1518
1519struct cmap;
1520
1521struct SubtableUnicodesCache {
1522
1523 private:
1524 hb_blob_ptr_t<cmap> base_blob;
1525 const char* base;
1526 hb_hashmap_t<unsigned, hb::unique_ptr<hb_set_t>> cached_unicodes;
1527
1528 public:
1529
1530 static SubtableUnicodesCache* create (hb_blob_ptr_t<cmap> source_table)
1531 {
1532 SubtableUnicodesCache* cache =
1533 (SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache));
1534 new (cache) SubtableUnicodesCache (source_table);
1535 return cache;
1536 }
1537
1538 static void destroy (void* value) {
1539 if (!value) return;
1540
1541 SubtableUnicodesCache* cache = (SubtableUnicodesCache*) value;
1542 cache->~SubtableUnicodesCache ();
1543 hb_free (cache);
1544 }
1545
1546 SubtableUnicodesCache(const void* cmap_base)
1547 : base_blob(),
1548 base ((const char*) cmap_base),
1549 cached_unicodes ()
1550 {}
1551
1552 SubtableUnicodesCache(hb_blob_ptr_t<cmap> base_blob_)
1553 : base_blob(base_blob_),
1554 base ((const char *) base_blob.get()),
1555 cached_unicodes ()
1556 {}
1557
1558 ~SubtableUnicodesCache()
1559 {
1560 base_blob.destroy ();
1561 }
1562
1563 bool same_base(const void* other) const
1564 {
1565 return other == (const void*) base;
1566 }
1567
1568 const hb_set_t* set_for (const EncodingRecord* record,
1569 SubtableUnicodesCache& mutable_cache) const
1570 {
1571 if (cached_unicodes.has ((unsigned) ((const char *) record - base)))
1572 return cached_unicodes.get ((unsigned) ((const char *) record - base));
1573
1574 return mutable_cache.set_for (record);
1575 }
1576
1577 const hb_set_t* set_for (const EncodingRecord* record)
1578 {
1579 if (!cached_unicodes.has ((unsigned) ((const char *) record - base)))
1580 {
1581 hb_set_t *s = hb_set_create ();
1582 if (unlikely (s->in_error ()))
1583 return hb_set_get_empty ();
1584
1585 (base+record->subtable).collect_unicodes (s);
1586
1587 if (unlikely (!cached_unicodes.set ((unsigned) ((const char *) record - base), hb::unique_ptr<hb_set_t> {s})))
1588 return hb_set_get_empty ();
1589
1590 return s;
1591 }
1592 return cached_unicodes.get ((unsigned) ((const char *) record - base));
1593 }
1594
1595};
1596
1597static inline uint_fast16_t
1598_hb_symbol_pua_map (unsigned codepoint)
1599{
1600 if (codepoint <= 0x00FFu)
1601 {
1602 /* For symbol-encoded OpenType fonts, we duplicate the
1603 * U+F000..F0FF range at U+0000..U+00FF. That's what
1604 * Windows seems to do, and that's hinted about at:
1605 * https://docs.microsoft.com/en-us/typography/opentype/spec/recom
1606 * under "Non-Standard (Symbol) Fonts". */
1607 return 0xF000u + codepoint;
1608 }
1609 return 0;
1610}
1611
1612struct cmap
1613{
1614 static constexpr hb_tag_t tableTag = HB_OT_TAG_cmap;
1615
1616
1617 static SubtableUnicodesCache* create_filled_cache(hb_blob_ptr_t<cmap> source_table) {
1618 const cmap* cmap = source_table.get();
1619 auto it =
1620 + hb_iter (cmap->encodingRecord)
1621 | hb_filter ([&](const EncodingRecord& _) {
1622 return cmap::filter_encoding_records_for_subset (cmap, _);
1623 })
1624 ;
1625
1626 SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table);
1627 for (const EncodingRecord& _ : it)
1628 cache->set_for(&_); // populate the cache for this encoding record.
1629
1630 return cache;
1631 }
1632
1633 template<typename Iterator, typename EncodingRecIter,
1634 hb_requires (hb_is_iterator (EncodingRecIter))>
1635 bool serialize (hb_serialize_context_t *c,
1636 Iterator it,
1637 EncodingRecIter encodingrec_iter,
1638 const void *base,
1639 hb_subset_plan_t *plan,
1640 bool drop_format_4 = false)
1641 {
1642 if (unlikely (!c->extend_min ((*this)))) return false;
1643 this->version = 0;
1644
1645 unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0;
1646 auto snap = c->snapshot ();
1647
1648 SubtableUnicodesCache local_unicodes_cache (base);
1649 const SubtableUnicodesCache* unicodes_cache = &local_unicodes_cache;
1650
1651 if (plan->accelerator &&
1652 plan->accelerator->cmap_cache &&
1653 plan->accelerator->cmap_cache->same_base (base))
1654 unicodes_cache = plan->accelerator->cmap_cache;
1655
1656 for (const EncodingRecord& _ : encodingrec_iter)
1657 {
1658 if (c->in_error ())
1659 return false;
1660
1661 unsigned format = (base+_.subtable).u.format;
1662 if (format != 4 && format != 12 && format != 14) continue;
1663
1664 const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache);
1665
1666 if (!drop_format_4 && format == 4)
1667 {
1668 c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 4u, base, plan, &format4objidx);
1669 if (c->in_error () && c->only_overflow ())
1670 {
1671 // cmap4 overflowed, reset and retry serialization without format 4 subtables.
1672 c->revert (snap);
1673 return serialize (c, it,
1674 encodingrec_iter,
1675 base,
1676 plan,
1677 true);
1678 }
1679 }
1680
1681 else if (format == 12)
1682 {
1683 if (_can_drop (_,
1684 *unicodes_set,
1685 base,
1686 *unicodes_cache,
1687 local_unicodes_cache,
1688 + it | hb_map (hb_first), encodingrec_iter))
1689 continue;
1690 c->copy (_, + it | hb_filter (*unicodes_set, hb_first), 12u, base, plan, &format12objidx);
1691 }
1692 else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx);
1693 }
1694 c->check_assign(this->encodingRecord.len,
1695 (c->length () - cmap::min_size)/EncodingRecord::static_size,
1696 HB_SERIALIZE_ERROR_INT_OVERFLOW);
1697
1698 // Fail if format 4 was dropped and there is no cmap12.
1699 return !drop_format_4 || format12objidx;
1700 }
1701
1702 template<typename Iterator, typename EncodingRecordIterator,
1703 hb_requires (hb_is_iterator (Iterator)),
1704 hb_requires (hb_is_iterator (EncodingRecordIterator))>
1705 bool _can_drop (const EncodingRecord& cmap12,
1706 const hb_set_t& cmap12_unicodes,
1707 const void* base,
1708 const SubtableUnicodesCache& unicodes_cache,
1709 SubtableUnicodesCache& local_unicodes_cache,
1710 Iterator subset_unicodes,
1711 EncodingRecordIterator encoding_records)
1712 {
1713 for (auto cp : + subset_unicodes | hb_filter (cmap12_unicodes))
1714 {
1715 if (cp >= 0x10000) return false;
1716 }
1717
1718 unsigned target_platform;
1719 unsigned target_encoding;
1720 unsigned target_language = (base+cmap12.subtable).get_language ();
1721
1722 if (cmap12.platformID == 0 && cmap12.encodingID == 4)
1723 {
1724 target_platform = 0;
1725 target_encoding = 3;
1726 } else if (cmap12.platformID == 3 && cmap12.encodingID == 10) {
1727 target_platform = 3;
1728 target_encoding = 1;
1729 } else {
1730 return false;
1731 }
1732
1733 for (const auto& _ : encoding_records)
1734 {
1735 if (_.platformID != target_platform
1736 || _.encodingID != target_encoding
1737 || (base+_.subtable).get_language() != target_language)
1738 continue;
1739
1740 const hb_set_t* sibling_unicodes = unicodes_cache.set_for (&_, local_unicodes_cache);
1741
1742 auto cmap12 = + subset_unicodes | hb_filter (cmap12_unicodes);
1743 auto sibling = + subset_unicodes | hb_filter (*sibling_unicodes);
1744 for (; cmap12 && sibling; cmap12++, sibling++)
1745 {
1746 unsigned a = *cmap12;
1747 unsigned b = *sibling;
1748 if (a != b) return false;
1749 }
1750
1751 return !cmap12 && !sibling;
1752 }
1753
1754 return false;
1755 }
1756
1757 void closure_glyphs (const hb_set_t *unicodes,
1758 hb_set_t *glyphset) const
1759 {
1760 + hb_iter (encodingRecord)
1761 | hb_map (&EncodingRecord::subtable)
1762 | hb_map (hb_add (this))
1763 | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; })
1764 | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); })
1765 ;
1766 }
1767
1768 bool subset (hb_subset_context_t *c) const
1769 {
1770 TRACE_SUBSET (this);
1771
1772 cmap *cmap_prime = c->serializer->start_embed<cmap> ();
1773
1774 auto encodingrec_iter =
1775 + hb_iter (encodingRecord)
1776 | hb_filter ([&](const EncodingRecord& _) {
1777 return cmap::filter_encoding_records_for_subset (this, _);
1778 })
1779 ;
1780
1781 if (unlikely (!encodingrec_iter.len ())) return_trace (false);
1782
1783 const EncodingRecord *unicode_bmp= nullptr, *unicode_ucs4 = nullptr, *ms_bmp = nullptr, *ms_ucs4 = nullptr;
1784 bool has_format12 = false;
1785
1786 for (const EncodingRecord& _ : encodingrec_iter)
1787 {
1788 unsigned format = (this + _.subtable).u.format;
1789 if (format == 12) has_format12 = true;
1790
1791 const EncodingRecord *table = std::addressof (_);
1792 if (_.platformID == 0 && _.encodingID == 3) unicode_bmp = table;
1793 else if (_.platformID == 0 && _.encodingID == 4) unicode_ucs4 = table;
1794 else if (_.platformID == 3 && _.encodingID == 1) ms_bmp = table;
1795 else if (_.platformID == 3 && _.encodingID == 10) ms_ucs4 = table;
1796 }
1797
1798 if (unlikely (!has_format12 && !unicode_bmp && !ms_bmp)) return_trace (false);
1799 if (unlikely (has_format12 && (!unicode_ucs4 && !ms_ucs4))) return_trace (false);
1800
1801 auto it =
1802 + c->plan->unicode_to_new_gid_list.iter ()
1803 | hb_filter ([&] (const hb_codepoint_pair_t _)
1804 { return (_.second != HB_MAP_VALUE_INVALID); })
1805 ;
1806
1807 return_trace (cmap_prime->serialize (c->serializer,
1808 it,
1809 encodingrec_iter,
1810 this,
1811 c->plan));
1812 }
1813
1814 const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const
1815 {
1816 if (symbol) *symbol = false;
1817
1818 const CmapSubtable *subtable;
1819
1820 /* Symbol subtable.
1821 * Prefer symbol if available.
1822 * https://github.com/harfbuzz/harfbuzz/issues/1918 */
1823 if ((subtable = this->find_subtable (3, 0)))
1824 {
1825 if (symbol) *symbol = true;
1826 return subtable;
1827 }
1828
1829 /* 32-bit subtables. */
1830 if ((subtable = this->find_subtable (3, 10))) return subtable;
1831 if ((subtable = this->find_subtable (0, 6))) return subtable;
1832 if ((subtable = this->find_subtable (0, 4))) return subtable;
1833
1834 /* 16-bit subtables. */
1835 if ((subtable = this->find_subtable (3, 1))) return subtable;
1836 if ((subtable = this->find_subtable (0, 3))) return subtable;
1837 if ((subtable = this->find_subtable (0, 2))) return subtable;
1838 if ((subtable = this->find_subtable (0, 1))) return subtable;
1839 if ((subtable = this->find_subtable (0, 0))) return subtable;
1840
1841 /* Meh. */
1842 return &Null (CmapSubtable);
1843 }
1844
1845 struct accelerator_t
1846 {
1847 using cache_t = hb_cache_t<21, 16, 8, true>;
1848
1849 accelerator_t (hb_face_t *face)
1850 {
1851 this->table = hb_sanitize_context_t ().reference_table<cmap> (face);
1852 bool symbol;
1853 this->subtable = table->find_best_subtable (&symbol);
1854 this->subtable_uvs = &Null (CmapSubtableFormat14);
1855 {
1856 const CmapSubtable *st = table->find_subtable (0, 5);
1857 if (st && st->u.format == 14)
1858 subtable_uvs = &st->u.format14;
1859 }
1860
1861 this->get_glyph_data = subtable;
1862 if (unlikely (symbol))
1863 {
1864 switch ((unsigned) face->table.OS2->get_font_page ()) {
1865 case OS2::font_page_t::FONT_PAGE_NONE:
1866 this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_symbol_pua_map>;
1867 break;
1868#ifndef HB_NO_OT_SHAPER_ARABIC_FALLBACK
1869 case OS2::font_page_t::FONT_PAGE_SIMP_ARABIC:
1870 this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_simp_map>;
1871 break;
1872 case OS2::font_page_t::FONT_PAGE_TRAD_ARABIC:
1873 this->get_glyph_funcZ = get_glyph_from_symbol<CmapSubtable, _hb_arabic_pua_trad_map>;
1874 break;
1875#endif
1876 default:
1877 this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
1878 break;
1879 }
1880 }
1881 else
1882 {
1883 switch (subtable->u.format) {
1884 /* Accelerate format 4 and format 12. */
1885 default:
1886 this->get_glyph_funcZ = get_glyph_from<CmapSubtable>;
1887 break;
1888 case 12:
1889 this->get_glyph_funcZ = get_glyph_from<CmapSubtableFormat12>;
1890 break;
1891 case 4:
1892 {
1893 this->format4_accel.init (&subtable->u.format4);
1894 this->get_glyph_data = &this->format4_accel;
1895 this->get_glyph_funcZ = this->format4_accel.get_glyph_func;
1896 break;
1897 }
1898 }
1899 }
1900 }
1901 ~accelerator_t () { this->table.destroy (); }
1902
1903 inline bool _cached_get (hb_codepoint_t unicode,
1904 hb_codepoint_t *glyph,
1905 cache_t *cache) const
1906 {
1907 unsigned v;
1908 if (cache && cache->get (unicode, &v))
1909 {
1910 *glyph = v;
1911 return true;
1912 }
1913 bool ret = this->get_glyph_funcZ (this->get_glyph_data, unicode, glyph);
1914
1915 if (cache && ret)
1916 cache->set (unicode, *glyph);
1917 return ret;
1918 }
1919
1920 bool get_nominal_glyph (hb_codepoint_t unicode,
1921 hb_codepoint_t *glyph,
1922 cache_t *cache = nullptr) const
1923 {
1924 if (unlikely (!this->get_glyph_funcZ)) return 0;
1925 return _cached_get (unicode, glyph, cache);
1926 }
1927
1928 unsigned int get_nominal_glyphs (unsigned int count,
1929 const hb_codepoint_t *first_unicode,
1930 unsigned int unicode_stride,
1931 hb_codepoint_t *first_glyph,
1932 unsigned int glyph_stride,
1933 cache_t *cache = nullptr) const
1934 {
1935 if (unlikely (!this->get_glyph_funcZ)) return 0;
1936
1937 unsigned int done;
1938 for (done = 0;
1939 done < count && _cached_get (*first_unicode, first_glyph, cache);
1940 done++)
1941 {
1942 first_unicode = &StructAtOffsetUnaligned<hb_codepoint_t> (first_unicode, unicode_stride);
1943 first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
1944 }
1945 return done;
1946 }
1947
1948 bool get_variation_glyph (hb_codepoint_t unicode,
1949 hb_codepoint_t variation_selector,
1950 hb_codepoint_t *glyph,
1951 cache_t *cache = nullptr) const
1952 {
1953 switch (this->subtable_uvs->get_glyph_variant (unicode,
1954 variation_selector,
1955 glyph))
1956 {
1957 case GLYPH_VARIANT_NOT_FOUND: return false;
1958 case GLYPH_VARIANT_FOUND: return true;
1959 case GLYPH_VARIANT_USE_DEFAULT: break;
1960 }
1961
1962 return get_nominal_glyph (unicode, glyph, cache);
1963 }
1964
1965 void collect_unicodes (hb_set_t *out, unsigned int num_glyphs) const
1966 { subtable->collect_unicodes (out, num_glyphs); }
1967 void collect_mapping (hb_set_t *unicodes, hb_map_t *mapping,
1968 unsigned num_glyphs = UINT_MAX) const
1969 { subtable->collect_mapping (unicodes, mapping, num_glyphs); }
1970 void collect_variation_selectors (hb_set_t *out) const
1971 { subtable_uvs->collect_variation_selectors (out); }
1972 void collect_variation_unicodes (hb_codepoint_t variation_selector,
1973 hb_set_t *out) const
1974 { subtable_uvs->collect_variation_unicodes (variation_selector, out); }
1975
1976 protected:
1977 typedef bool (*hb_cmap_get_glyph_func_t) (const void *obj,
1978 hb_codepoint_t codepoint,
1979 hb_codepoint_t *glyph);
1980 typedef uint_fast16_t (*hb_pua_remap_func_t) (unsigned);
1981
1982 template <typename Type>
1983 HB_INTERNAL static bool get_glyph_from (const void *obj,
1984 hb_codepoint_t codepoint,
1985 hb_codepoint_t *glyph)
1986 {
1987 const Type *typed_obj = (const Type *) obj;
1988 return typed_obj->get_glyph (codepoint, glyph);
1989 }
1990
1991 template <typename Type, hb_pua_remap_func_t remap>
1992 HB_INTERNAL static bool get_glyph_from_symbol (const void *obj,
1993 hb_codepoint_t codepoint,
1994 hb_codepoint_t *glyph)
1995 {
1996 const Type *typed_obj = (const Type *) obj;
1997 if (likely (typed_obj->get_glyph (codepoint, glyph)))
1998 return true;
1999
2000 if (hb_codepoint_t c = remap (codepoint))
2001 return typed_obj->get_glyph (c, glyph);
2002
2003 return false;
2004 }
2005
2006 private:
2007 hb_nonnull_ptr_t<const CmapSubtable> subtable;
2008 hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs;
2009
2010 hb_cmap_get_glyph_func_t get_glyph_funcZ;
2011 const void *get_glyph_data;
2012
2013 CmapSubtableFormat4::accelerator_t format4_accel;
2014
2015 public:
2016 hb_blob_ptr_t<cmap> table;
2017 };
2018
2019 protected:
2020
2021 const CmapSubtable *find_subtable (unsigned int platform_id,
2022 unsigned int encoding_id) const
2023 {
2024 EncodingRecord key;
2025 key.platformID = platform_id;
2026 key.encodingID = encoding_id;
2027
2028 const EncodingRecord &result = encodingRecord.bsearch (key);
2029 if (!result.subtable)
2030 return nullptr;
2031
2032 return &(this+result.subtable);
2033 }
2034
2035 const EncodingRecord *find_encodingrec (unsigned int platform_id,
2036 unsigned int encoding_id) const
2037 {
2038 EncodingRecord key;
2039 key.platformID = platform_id;
2040 key.encodingID = encoding_id;
2041
2042 return encodingRecord.as_array ().bsearch (key);
2043 }
2044
2045 bool find_subtable (unsigned format) const
2046 {
2047 auto it =
2048 + hb_iter (encodingRecord)
2049 | hb_map (&EncodingRecord::subtable)
2050 | hb_map (hb_add (this))
2051 | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; })
2052 ;
2053
2054 return it.len ();
2055 }
2056
2057 public:
2058
2059 bool sanitize (hb_sanitize_context_t *c) const
2060 {
2061 TRACE_SANITIZE (this);
2062 return_trace (c->check_struct (this) &&
2063 likely (version == 0) &&
2064 encodingRecord.sanitize (c, this));
2065 }
2066
2067 private:
2068
2069 static bool filter_encoding_records_for_subset(const cmap* cmap,
2070 const EncodingRecord& _)
2071 {
2072 return
2073 (_.platformID == 0 && _.encodingID == 3) ||
2074 (_.platformID == 0 && _.encodingID == 4) ||
2075 (_.platformID == 3 && _.encodingID == 1) ||
2076 (_.platformID == 3 && _.encodingID == 10) ||
2077 (cmap + _.subtable).u.format == 14;
2078 }
2079
2080 protected:
2081 HBUINT16 version; /* Table version number (0). */
2082 SortedArray16Of<EncodingRecord>
2083 encodingRecord; /* Encoding tables. */
2084 public:
2085 DEFINE_SIZE_ARRAY (4, encodingRecord);
2086};
2087
2088struct cmap_accelerator_t : cmap::accelerator_t {
2089 cmap_accelerator_t (hb_face_t *face) : cmap::accelerator_t (face) {}
2090};
2091
2092} /* namespace OT */
2093
2094
2095#endif /* HB_OT_CMAP_TABLE_HH */
2096