1include!("../../generated/generated_cmap.rs");
4
5#[cfg(feature = "std")]
6use crate::collections::IntSet;
7use crate::{FontRef, TableProvider};
8use std::ops::Range;
9
10const WINDOWS_SYMBOL_ENCODING: u16 = 0;
12const WINDOWS_UNICODE_BMP_ENCODING: u16 = 1;
13const WINDOWS_UNICODE_FULL_ENCODING: u16 = 10;
14
15const UNICODE_1_0_ENCODING: u16 = 0;
17const UNICODE_1_1_ENCODING: u16 = 1;
18const UNICODE_ISO_ENCODING: u16 = 2;
19const UNICODE_2_0_BMP_ENCODING: u16 = 3;
20const UNICODE_2_0_FULL_ENCODING: u16 = 4;
21const UNICODE_FULL_ENCODING: u16 = 6;
22
23#[derive(Copy, Clone, PartialEq, Eq, Debug)]
25pub enum MapVariant {
26 UseDefault,
29 Variant(GlyphId),
32}
33
34impl<'a> Cmap<'a> {
35 pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
44 let codepoint = codepoint.into();
45 for record in self.encoding_records() {
46 if let Ok(subtable) = record.subtable(self.offset_data()) {
47 if let Some(gid) = match subtable {
48 CmapSubtable::Format0(format0) => format0.map_codepoint(codepoint),
49 CmapSubtable::Format4(format4) => format4.map_codepoint(codepoint),
50 CmapSubtable::Format6(format6) => format6.map_codepoint(codepoint),
51 CmapSubtable::Format12(format12) => format12.map_codepoint(codepoint),
52 CmapSubtable::Format13(format13) => format13.map_codepoint(codepoint),
53 _ => None,
54 } {
55 return Some(gid);
56 }
57 }
58 }
59 None
60 }
61
62 pub fn best_subtable(&self) -> Option<(u16, EncodingRecord, CmapSubtable<'a>)> {
71 let offset_data = self.offset_data();
74 let records = self.encoding_records();
75 let find = |platform_id, encoding_id| {
76 for (index, record) in records.iter().enumerate() {
77 if record.platform_id() != platform_id || record.encoding_id() != encoding_id {
78 continue;
79 }
80 if let Ok(subtable) = record.subtable(offset_data) {
81 match subtable {
82 CmapSubtable::Format0(_)
83 | CmapSubtable::Format4(_)
84 | CmapSubtable::Format6(_)
85 | CmapSubtable::Format10(_)
86 | CmapSubtable::Format12(_)
87 | CmapSubtable::Format13(_) => {
88 return Some((index as u16, *record, subtable))
89 }
90 _ => {}
91 }
92 }
93 }
94 None
95 };
96 find(PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
100 .or_else(|| find(PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING))
102 .or_else(|| find(PlatformId::Unicode, UNICODE_FULL_ENCODING))
103 .or_else(|| find(PlatformId::Unicode, UNICODE_2_0_FULL_ENCODING))
104 .or_else(|| find(PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING))
106 .or_else(|| find(PlatformId::Unicode, UNICODE_2_0_BMP_ENCODING))
107 .or_else(|| find(PlatformId::Unicode, UNICODE_ISO_ENCODING))
108 .or_else(|| find(PlatformId::Unicode, UNICODE_1_1_ENCODING))
109 .or_else(|| find(PlatformId::Unicode, UNICODE_1_0_ENCODING))
110 .or_else(|| find(PlatformId::Macintosh, 0))
112 }
113
114 pub fn uvs_subtable(&self) -> Option<(u16, Cmap14<'a>)> {
120 let offset_data = self.offset_data();
121 for (index, record) in self.encoding_records().iter().enumerate() {
122 if let Ok(CmapSubtable::Format14(cmap14)) = record.subtable(offset_data) {
123 return Some((index as u16, cmap14));
124 };
125 }
126 None
127 }
128
129 pub fn subtable(&self, index: u16) -> Result<CmapSubtable<'a>, ReadError> {
131 self.encoding_records()
132 .get(index as usize)
133 .ok_or(ReadError::OutOfBounds)
134 .and_then(|encoding| encoding.subtable(self.offset_data()))
135 }
136
137 #[cfg(feature = "std")]
138 pub fn closure_glyphs(&self, unicodes: &IntSet<u32>, glyph_set: &mut IntSet<GlyphId>) {
139 for record in self.encoding_records() {
140 if let Ok(subtable) = record.subtable(self.offset_data()) {
141 match subtable {
142 CmapSubtable::Format14(format14) => {
143 format14.closure_glyphs(unicodes, glyph_set);
144 return;
145 }
146 _ => {
147 continue;
148 }
149 }
150 }
151 }
152 }
153}
154
155impl EncodingRecord {
156 pub fn is_symbol(&self) -> bool {
157 self.platform_id() == PlatformId::Windows && self.encoding_id() == WINDOWS_SYMBOL_ENCODING
158 }
159
160 pub fn is_mac_roman(&self) -> bool {
161 self.platform_id() == PlatformId::Macintosh && self.encoding_id() == 0
162 }
163}
164
165impl CmapSubtable<'_> {
166 pub fn language(&self) -> u32 {
167 match self {
168 Self::Format0(item) => item.language() as u32,
169 Self::Format2(item) => item.language() as u32,
170 Self::Format4(item) => item.language() as u32,
171 Self::Format6(item) => item.language() as u32,
172 Self::Format10(item) => item.language(),
173 Self::Format12(item) => item.language(),
174 Self::Format13(item) => item.language(),
175 _ => 0,
176 }
177 }
178}
179
180impl Cmap0<'_> {
181 pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
182 let codepoint = codepoint.into();
183
184 self.glyph_id_array()
185 .get(codepoint as usize)
186 .map(|g| GlyphId::new(*g as u32))
187 }
188}
189
190impl<'a> Cmap4<'a> {
191 pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
193 let codepoint = codepoint.into();
194 if codepoint > 0xFFFF {
195 return None;
196 }
197 let codepoint = codepoint as u16;
198 let mut lo = 0;
199 let mut hi = self.seg_count_x2() as usize / 2;
200 let start_codes = self.start_code();
201 let end_codes = self.end_code();
202 while lo < hi {
203 let i = (lo + hi) / 2;
204 let start_code = start_codes.get(i)?.get();
205 if codepoint < start_code {
206 hi = i;
207 } else if codepoint > end_codes.get(i)?.get() {
208 lo = i + 1;
209 } else {
210 return self.lookup_glyph_id(codepoint, i, start_code);
211 }
212 }
213 None
214 }
215
216 pub fn iter(&self) -> Cmap4Iter<'a> {
219 Cmap4Iter::new(self.clone())
220 }
221
222 fn lookup_glyph_id(&self, codepoint: u16, index: usize, start_code: u16) -> Option<GlyphId> {
226 let deltas = self.id_delta();
227 let range_offsets = self.id_range_offsets();
228 let delta = deltas.get(index)?.get() as i32;
229 let range_offset = range_offsets.get(index)?.get() as usize;
230 if range_offset == 0 {
231 return Some(GlyphId::from((codepoint as i32 + delta) as u16));
232 }
233 let mut offset = range_offset / 2 + (codepoint - start_code) as usize;
234 offset = offset.saturating_sub(range_offsets.len() - index);
235 let gid = self.glyph_id_array().get(offset)?.get();
236 (gid != 0).then_some(GlyphId::from((gid as i32 + delta) as u16))
237 }
238
239 fn code_range(&self, index: usize) -> Option<Range<u32>> {
241 let start = self.start_code().get(index)?.get() as u32;
244 let end = self.end_code().get(index)?.get() as u32;
245 Some(start..end + 1)
247 }
248}
249
250#[derive(Clone)]
253pub struct Cmap4Iter<'a> {
254 subtable: Cmap4<'a>,
255 cur_range: Range<u32>,
256 cur_start_code: u16,
257 cur_range_ix: usize,
258}
259
260impl<'a> Cmap4Iter<'a> {
261 fn new(subtable: Cmap4<'a>) -> Self {
262 let cur_range = subtable.code_range(0).unwrap_or_default();
263 let cur_start_code = cur_range.start as u16;
264 Self {
265 subtable,
266 cur_range,
267 cur_start_code,
268 cur_range_ix: 0,
269 }
270 }
271}
272
273impl Iterator for Cmap4Iter<'_> {
274 type Item = (u32, GlyphId);
275
276 fn next(&mut self) -> Option<Self::Item> {
277 loop {
278 if let Some(codepoint) = self.cur_range.next() {
279 let Some(glyph_id) = self.subtable.lookup_glyph_id(
280 codepoint as u16,
281 self.cur_range_ix,
282 self.cur_start_code,
283 ) else {
284 continue;
285 };
286 return Some((codepoint, glyph_id));
287 } else {
288 self.cur_range_ix += 1;
289 let next_range = self.subtable.code_range(self.cur_range_ix)?;
290 self.cur_range = next_range.start.max(self.cur_range.end)
298 ..next_range.end.max(self.cur_range.end);
299 self.cur_start_code = self.cur_range.start as u16;
300 }
301 }
302 }
303}
304
305impl Cmap6<'_> {
306 pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
307 let codepoint = codepoint.into();
308
309 let first = self.first_code() as u32;
310 let idx = codepoint.checked_sub(first)?;
311 self.glyph_id_array()
312 .get(idx as usize)
313 .map(|g| GlyphId::new(g.get() as u32))
314 }
315}
316
317trait AnyMapGroup {
319 const IS_CONSTANT: bool;
320
321 fn start_char_code(&self) -> u32;
322 fn end_char_code(&self) -> u32;
323 fn ref_glyph_id(&self) -> u32;
326
327 fn compute_glyph_id(codepoint: u32, start_char_code: u32, ref_glyph_id: u32) -> GlyphId {
328 if Self::IS_CONSTANT {
329 GlyphId::new(ref_glyph_id)
330 } else {
331 GlyphId::new(ref_glyph_id.wrapping_add(codepoint.wrapping_sub(start_char_code)))
332 }
333 }
334}
335
336impl AnyMapGroup for ConstantMapGroup {
337 const IS_CONSTANT: bool = true;
338
339 fn start_char_code(&self) -> u32 {
340 self.start_char_code()
341 }
342
343 fn end_char_code(&self) -> u32 {
344 self.end_char_code()
345 }
346
347 fn ref_glyph_id(&self) -> u32 {
348 self.glyph_id()
349 }
350}
351
352impl AnyMapGroup for SequentialMapGroup {
353 const IS_CONSTANT: bool = false;
354
355 fn start_char_code(&self) -> u32 {
356 self.start_char_code()
357 }
358
359 fn end_char_code(&self) -> u32 {
360 self.end_char_code()
361 }
362
363 fn ref_glyph_id(&self) -> u32 {
364 self.start_glyph_id()
365 }
366}
367
368fn cmap1213_map_codepoint<T: AnyMapGroup>(
370 groups: &[T],
371 codepoint: impl Into<u32>,
372) -> Option<GlyphId> {
373 let codepoint = codepoint.into();
374 let mut lo = 0;
375 let mut hi = groups.len();
376 while lo < hi {
377 let i = (lo + hi) / 2;
378 let group = groups.get(i)?;
379 if codepoint < group.start_char_code() {
380 hi = i;
381 } else if codepoint > group.end_char_code() {
382 lo = i + 1;
383 } else {
384 return Some(T::compute_glyph_id(
385 codepoint,
386 group.start_char_code(),
387 group.ref_glyph_id(),
388 ));
389 }
390 }
391 None
392}
393
394#[derive(Copy, Clone, Debug)]
396pub struct CmapIterLimits {
397 pub max_char: u32,
399 pub glyph_count: u32,
401}
402
403impl CmapIterLimits {
404 pub fn default_for_font(font: &FontRef) -> Self {
410 let glyph_count = font
411 .maxp()
412 .map(|maxp| maxp.num_glyphs())
413 .unwrap_or(u16::MAX) as u32;
414 Self {
415 max_char: char::MAX as u32,
418 glyph_count,
419 }
420 }
421}
422
423impl Default for CmapIterLimits {
424 fn default() -> Self {
425 Self {
426 max_char: char::MAX as u32,
427 glyph_count: u16::MAX as u32,
429 }
430 }
431}
432
433#[derive(Clone, Debug)]
435struct Cmap1213IterGroup {
436 range: Range<u64>,
437 start_code: u32,
438 ref_glyph_id: u32,
439}
440
441fn cmap1213_iter_group<T: AnyMapGroup>(
443 groups: &[T],
444 index: usize,
445 limits: &Option<CmapIterLimits>,
446) -> Option<Cmap1213IterGroup> {
447 let group = groups.get(index)?;
448 let start_code = group.start_char_code();
449 let end_code = group.end_char_code() as u64 + 1;
452 let start_glyph_id = group.ref_glyph_id();
453 let end_code = if let Some(limits) = limits {
454 if T::IS_CONSTANT {
457 end_code.min(limits.max_char as u64)
458 } else {
459 (limits.glyph_count as u64)
460 .saturating_sub(start_glyph_id as u64)
461 .saturating_add(start_code as u64)
462 .min(end_code.min(limits.max_char as u64))
463 }
464 } else {
465 end_code
466 };
467 Some(Cmap1213IterGroup {
468 range: start_code as u64..end_code,
469 start_code,
470 ref_glyph_id: start_glyph_id,
471 })
472}
473
474#[derive(Clone)]
476struct Cmap1213Iter<'a, T> {
477 groups: &'a [T],
478 cur_group: Option<Cmap1213IterGroup>,
479 cur_group_ix: usize,
480 limits: Option<CmapIterLimits>,
481}
482
483impl<'a, T> Cmap1213Iter<'a, T>
484where
485 T: AnyMapGroup,
486{
487 fn new(groups: &'a [T], limits: Option<CmapIterLimits>) -> Self {
488 let cur_group = cmap1213_iter_group(groups, 0, &limits);
489 Self {
490 groups,
491 cur_group,
492 cur_group_ix: 0,
493 limits,
494 }
495 }
496}
497
498impl<T> Iterator for Cmap1213Iter<'_, T>
499where
500 T: AnyMapGroup,
501{
502 type Item = (u32, GlyphId);
503
504 fn next(&mut self) -> Option<Self::Item> {
505 loop {
506 let group = self.cur_group.as_mut()?;
507 if let Some(codepoint) = group.range.next() {
508 let codepoint = codepoint as u32;
509 let glyph_id = T::compute_glyph_id(codepoint, group.start_code, group.ref_glyph_id);
510 return Some((codepoint, glyph_id));
511 } else {
512 self.cur_group_ix += 1;
513 let mut next_group =
514 cmap1213_iter_group(self.groups, self.cur_group_ix, &self.limits)?;
515 if next_group.range.start < group.range.end {
519 next_group.range = group.range.end..next_group.range.end;
520 }
521 self.cur_group = Some(next_group);
522 }
523 }
524 }
525}
526
527impl<'a> Cmap12<'a> {
528 pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
530 cmap1213_map_codepoint(self.groups(), codepoint)
531 }
532
533 pub fn iter(&self) -> Cmap12Iter<'a> {
540 Cmap12Iter::new(self.clone(), None)
541 }
542
543 pub fn iter_with_limits(&self, limits: CmapIterLimits) -> Cmap12Iter<'a> {
546 Cmap12Iter::new(self.clone(), Some(limits))
547 }
548}
549
550#[derive(Clone)]
553pub struct Cmap12Iter<'a>(Cmap1213Iter<'a, SequentialMapGroup>);
554
555impl<'a> Cmap12Iter<'a> {
556 fn new(subtable: Cmap12<'a>, limits: Option<CmapIterLimits>) -> Self {
557 Self(Cmap1213Iter::new(subtable.groups(), limits))
558 }
559}
560
561impl Iterator for Cmap12Iter<'_> {
562 type Item = (u32, GlyphId);
563
564 fn next(&mut self) -> Option<Self::Item> {
565 self.0.next()
566 }
567}
568
569impl<'a> Cmap13<'a> {
570 pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
572 cmap1213_map_codepoint(self.groups(), codepoint)
573 }
574
575 pub fn iter(&self) -> Cmap13Iter<'a> {
582 Cmap13Iter::new(self.clone(), None)
583 }
584
585 pub fn iter_with_limits(&self, limits: CmapIterLimits) -> Cmap13Iter<'a> {
588 Cmap13Iter::new(self.clone(), Some(limits))
589 }
590}
591
592#[derive(Clone)]
595pub struct Cmap13Iter<'a>(Cmap1213Iter<'a, ConstantMapGroup>);
596
597impl<'a> Cmap13Iter<'a> {
598 fn new(subtable: Cmap13<'a>, limits: Option<CmapIterLimits>) -> Self {
599 Self(Cmap1213Iter::new(subtable.groups(), limits))
600 }
601}
602
603impl Iterator for Cmap13Iter<'_> {
604 type Item = (u32, GlyphId);
605
606 fn next(&mut self) -> Option<Self::Item> {
607 self.0.next()
608 }
609}
610
611impl<'a> Cmap14<'a> {
612 pub fn map_variant(
614 &self,
615 codepoint: impl Into<u32>,
616 selector: impl Into<u32>,
617 ) -> Option<MapVariant> {
618 let codepoint = codepoint.into();
619 let selector = selector.into();
620 let selector_records = self.var_selector();
621 let selector_record = selector_records
624 .binary_search_by(|rec| {
625 let rec_selector: u32 = rec.var_selector().into();
626 rec_selector.cmp(&selector)
627 })
628 .ok()
629 .and_then(|idx| selector_records.get(idx))?;
630 if let Some(Ok(default_uvs)) = selector_record.default_uvs(self.offset_data()) {
635 use core::cmp::Ordering;
636 let found_default_uvs = default_uvs
637 .ranges()
638 .binary_search_by(|range| {
639 let start = range.start_unicode_value().into();
640 if codepoint < start {
641 Ordering::Greater
642 } else if codepoint > (start + range.additional_count() as u32) {
643 Ordering::Less
644 } else {
645 Ordering::Equal
646 }
647 })
648 .is_ok();
649 if found_default_uvs {
650 return Some(MapVariant::UseDefault);
651 }
652 }
653 let non_default_uvs = selector_record.non_default_uvs(self.offset_data())?.ok()?;
655 let mapping = non_default_uvs.uvs_mapping();
656 let ix = mapping
657 .binary_search_by(|map| {
658 let map_codepoint: u32 = map.unicode_value().into();
659 map_codepoint.cmp(&codepoint)
660 })
661 .ok()?;
662 Some(MapVariant::Variant(GlyphId::from(
663 mapping.get(ix)?.glyph_id(),
664 )))
665 }
666
667 pub fn iter(&self) -> Cmap14Iter<'a> {
670 Cmap14Iter::new(self.clone())
671 }
672
673 fn selector(
674 &self,
675 index: usize,
676 ) -> (
677 Option<VariationSelector>,
678 Option<DefaultUvs<'a>>,
679 Option<NonDefaultUvs<'a>>,
680 ) {
681 let selector = self.var_selector().get(index).cloned();
682 let default_uvs = selector.as_ref().and_then(|selector| {
683 selector
684 .default_uvs(self.offset_data())
685 .transpose()
686 .ok()
687 .flatten()
688 });
689 let non_default_uvs = selector.as_ref().and_then(|selector| {
690 selector
691 .non_default_uvs(self.offset_data())
692 .transpose()
693 .ok()
694 .flatten()
695 });
696 (selector, default_uvs, non_default_uvs)
697 }
698
699 #[cfg(feature = "std")]
700 pub fn closure_glyphs(&self, unicodes: &IntSet<u32>, glyph_set: &mut IntSet<GlyphId>) {
701 for selector in self.var_selector() {
702 if !unicodes.contains(selector.var_selector().to_u32()) {
703 continue;
704 }
705 if let Some(non_default_uvs) = selector
706 .non_default_uvs(self.offset_data())
707 .transpose()
708 .ok()
709 .flatten()
710 {
711 glyph_set.extend(
712 non_default_uvs
713 .uvs_mapping()
714 .iter()
715 .filter(|m| unicodes.contains(m.unicode_value().to_u32()))
716 .map(|m| m.glyph_id().into()),
717 );
718 }
719 }
720 }
721}
722
723#[derive(Clone)]
726pub struct Cmap14Iter<'a> {
727 subtable: Cmap14<'a>,
728 selector_record: Option<VariationSelector>,
729 default_uvs: Option<DefaultUvsIter<'a>>,
730 non_default_uvs: Option<NonDefaultUvsIter<'a>>,
731 cur_selector_ix: usize,
732}
733
734impl<'a> Cmap14Iter<'a> {
735 fn new(subtable: Cmap14<'a>) -> Self {
736 let (selector_record, default_uvs, non_default_uvs) = subtable.selector(0);
737 Self {
738 subtable,
739 selector_record,
740 default_uvs: default_uvs.map(DefaultUvsIter::new),
741 non_default_uvs: non_default_uvs.map(NonDefaultUvsIter::new),
742 cur_selector_ix: 0,
743 }
744 }
745}
746
747impl Iterator for Cmap14Iter<'_> {
748 type Item = (u32, u32, MapVariant);
749
750 fn next(&mut self) -> Option<Self::Item> {
751 loop {
752 let selector_record = self.selector_record.as_ref()?;
753 let selector: u32 = selector_record.var_selector().into();
754 if let Some(default_uvs) = self.default_uvs.as_mut() {
755 if let Some(codepoint) = default_uvs.next() {
756 return Some((codepoint, selector, MapVariant::UseDefault));
757 }
758 }
759 if let Some(non_default_uvs) = self.non_default_uvs.as_mut() {
760 if let Some((codepoint, variant)) = non_default_uvs.next() {
761 return Some((codepoint, selector, MapVariant::Variant(variant.into())));
762 }
763 }
764 self.cur_selector_ix += 1;
765 let (selector_record, default_uvs, non_default_uvs) =
766 self.subtable.selector(self.cur_selector_ix);
767 self.selector_record = selector_record;
768 self.default_uvs = default_uvs.map(DefaultUvsIter::new);
769 self.non_default_uvs = non_default_uvs.map(NonDefaultUvsIter::new);
770 }
771 }
772}
773
774#[derive(Clone)]
775struct DefaultUvsIter<'a> {
776 ranges: std::slice::Iter<'a, UnicodeRange>,
777 cur_range: Range<u32>,
778}
779
780impl<'a> DefaultUvsIter<'a> {
781 fn new(ranges: DefaultUvs<'a>) -> Self {
782 let mut ranges = ranges.ranges().iter();
783 let cur_range = if let Some(range) = ranges.next() {
784 let start: u32 = range.start_unicode_value().into();
785 let end = start + range.additional_count() as u32 + 1;
786 start..end
787 } else {
788 0..0
789 };
790 Self { ranges, cur_range }
791 }
792}
793
794impl Iterator for DefaultUvsIter<'_> {
795 type Item = u32;
796
797 fn next(&mut self) -> Option<Self::Item> {
798 loop {
799 if let Some(codepoint) = self.cur_range.next() {
800 return Some(codepoint);
801 }
802 let range = self.ranges.next()?;
803 let start: u32 = range.start_unicode_value().into();
804 let end = start + range.additional_count() as u32 + 1;
805 self.cur_range = start..end;
806 }
807 }
808}
809
810#[derive(Clone)]
811struct NonDefaultUvsIter<'a> {
812 iter: std::slice::Iter<'a, UvsMapping>,
813}
814
815impl<'a> NonDefaultUvsIter<'a> {
816 fn new(uvs: NonDefaultUvs<'a>) -> Self {
817 Self {
818 iter: uvs.uvs_mapping().iter(),
819 }
820 }
821}
822
823impl Iterator for NonDefaultUvsIter<'_> {
824 type Item = (u32, GlyphId16);
825
826 fn next(&mut self) -> Option<Self::Item> {
827 let mapping = self.iter.next()?;
828 let codepoint: u32 = mapping.unicode_value().into();
829 let glyph_id = GlyphId16::new(mapping.glyph_id());
830 Some((codepoint, glyph_id))
831 }
832}
833
834#[cfg(test)]
835mod tests {
836 use font_test_data::{be_buffer, bebuffer::BeBuffer};
837
838 use super::*;
839 use crate::{FontRef, GlyphId, TableProvider};
840
841 #[test]
842 fn map_codepoints() {
843 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
844 let cmap = font.cmap().unwrap();
845 assert_eq!(cmap.map_codepoint('A'), Some(GlyphId::new(1)));
846 assert_eq!(cmap.map_codepoint('À'), Some(GlyphId::new(2)));
847 assert_eq!(cmap.map_codepoint('`'), Some(GlyphId::new(3)));
848 assert_eq!(cmap.map_codepoint('B'), None);
849
850 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
851 let cmap = font.cmap().unwrap();
852 assert_eq!(cmap.map_codepoint(' '), Some(GlyphId::new(1)));
853 assert_eq!(cmap.map_codepoint(0xE_u32), Some(GlyphId::new(2)));
854 assert_eq!(cmap.map_codepoint('B'), None);
855
856 let cmap0_data = cmap0_data();
857 let cmap = Cmap::read(FontData::new(cmap0_data.data())).unwrap();
858
859 assert_eq!(cmap.map_codepoint(0u8), Some(GlyphId::new(0)));
860 assert_eq!(cmap.map_codepoint(b' '), Some(GlyphId::new(178)));
861 assert_eq!(cmap.map_codepoint(b'r'), Some(GlyphId::new(193)));
862 assert_eq!(cmap.map_codepoint(b'X'), Some(GlyphId::new(13)));
863 assert_eq!(cmap.map_codepoint(255u8), Some(GlyphId::new(3)));
864
865 let cmap6_data = be_buffer! {
866 0u16,
868 1u16,
870 1u16,
872 0u16,
874 12u32,
876 6u16,
878 32u16,
880 0u16,
882 32u16,
884 5u16,
886 [10u16, 15, 7, 20, 4]
888 };
889
890 let cmap = Cmap::read(FontData::new(cmap6_data.data())).unwrap();
891
892 assert_eq!(cmap.map_codepoint(0u8), None);
893 assert_eq!(cmap.map_codepoint(31u8), None);
894 assert_eq!(cmap.map_codepoint(33u8), Some(GlyphId::new(15)));
895 assert_eq!(cmap.map_codepoint(35u8), Some(GlyphId::new(20)));
896 assert_eq!(cmap.map_codepoint(36u8), Some(GlyphId::new(4)));
897 assert_eq!(cmap.map_codepoint(50u8), None);
898 }
899
900 #[test]
901 fn map_variants() {
902 use super::MapVariant::*;
903 let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
904 let cmap = font.cmap().unwrap();
905 let cmap14 = find_cmap14(&cmap).unwrap();
906 let selector = '\u{e0100}';
907 assert_eq!(cmap14.map_variant('a', selector), None);
908 assert_eq!(cmap14.map_variant('\u{4e00}', selector), Some(UseDefault));
909 assert_eq!(cmap14.map_variant('\u{4e06}', selector), Some(UseDefault));
910 assert_eq!(
911 cmap14.map_variant('\u{4e08}', selector),
912 Some(Variant(GlyphId::new(25)))
913 );
914 assert_eq!(
915 cmap14.map_variant('\u{4e09}', selector),
916 Some(Variant(GlyphId::new(26)))
917 );
918 }
919
920 #[test]
921 #[cfg(feature = "std")]
922 fn cmap14_closure_glyphs() {
923 let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
924 let cmap = font.cmap().unwrap();
925 let mut unicodes = IntSet::empty();
926 unicodes.insert(0x4e08_u32);
927 unicodes.insert(0xe0100_u32);
928
929 let mut glyph_set = IntSet::empty();
930 glyph_set.insert(GlyphId::new(18));
931 cmap.closure_glyphs(&unicodes, &mut glyph_set);
932
933 assert_eq!(glyph_set.len(), 2);
934 assert!(glyph_set.contains(GlyphId::new(18)));
935 assert!(glyph_set.contains(GlyphId::new(25)));
936 }
937
938 #[test]
939 fn cmap4_iter() {
940 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
941 let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
942 let mut count = 0;
943 for (codepoint, glyph_id) in cmap4.iter() {
944 assert_eq!(cmap4.map_codepoint(codepoint), Some(glyph_id));
945 count += 1;
946 }
947 assert_eq!(count, 4);
948 let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
949 let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
950 let mut count = 0;
951 for (codepoint, glyph_id) in cmap4.iter() {
952 assert_eq!(cmap4.map_codepoint(codepoint), Some(glyph_id));
953 count += 1;
954 }
955 assert_eq!(count, 3);
956 }
957
958 #[test]
959 fn cmap4_iter_explicit_notdef() {
960 let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
961 let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
962 let mut notdef_count = 0;
963 for (_, glyph_id) in cmap4.iter() {
964 notdef_count += (glyph_id == GlyphId::NOTDEF) as i32;
965 }
966 assert!(notdef_count > 0);
967 assert_eq!(cmap4.map_codepoint(0xFFFF_u32), Some(GlyphId::NOTDEF));
968 }
969
970 #[test]
974 fn cmap4_iter_sparse_range() {
975 #[rustfmt::skip]
976 let cmap4_data: &[u16] = &[
977 4, 0, 0,
979 4,
981 0, 0, 0,
983 262, 0xFFFF,
985 0,
987 259, 0xFFFF,
989 0, 1,
991 4, 0,
993 236, 0, 0, 326,
995 ];
996 let mut buf = BeBuffer::new();
997 for &word in cmap4_data {
998 buf = buf.push(word);
999 }
1000 let cmap4 = Cmap4::read(FontData::new(&buf)).unwrap();
1001 let mappings = cmap4
1002 .iter()
1003 .map(|(ch, gid)| (ch, gid.to_u32()))
1004 .collect::<Vec<_>>();
1005 assert_eq!(mappings, &[(259, 236), (262, 326), (65535, 0)]);
1006 }
1007
1008 #[test]
1009 fn cmap12_iter() {
1010 let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
1011 let cmap12 = find_cmap12(&font.cmap().unwrap()).unwrap();
1012 let mut count = 0;
1013 for (codepoint, glyph_id) in cmap12.iter() {
1014 assert_eq!(cmap12.map_codepoint(codepoint), Some(glyph_id));
1015 count += 1;
1016 }
1017 assert_eq!(count, 10);
1018 }
1019
1020 #[test]
1024 fn cmap12_iter_avoid_overflow() {
1025 let data = be_buffer! {
1027 12u16, 0u16, 0u32, 0u32, 2u32, [0xFFFFFFFA_u32, 0xFFFFFFFC, 0], [0xFFFFFFFB_u32, 0xFFFFFFFF, 0] };
1036 let cmap12 = Cmap12::read(data.data().into()).unwrap();
1037 let _ = cmap12.iter().count();
1038 }
1039
1040 #[test]
1044 fn cmap12_iter_avoid_timeout() {
1045 let cmap12_data = be_buffer! {
1047 12u16, 0u16, 0u32, 0u32, 1u32, [170u32, 1330926671, 328960] };
1055 let cmap12 = Cmap12::read(cmap12_data.data().into()).unwrap();
1056 assert!(
1057 cmap12.iter_with_limits(CmapIterLimits::default()).count() <= char::MAX as usize + 1
1058 );
1059 }
1060
1061 #[test]
1064 fn cmap12_iter_avoid_timeout2() {
1065 let cmap12_data = be_buffer! {
1066 12u16, 0u16, 0u32, 0u32, 3u32, [199u32, 16777271, 2],
1073 [262u32, 262, 3],
1074 [268u32, 268, 4]
1075 };
1076 let cmap12 = Cmap12::read(cmap12_data.data().into()).unwrap();
1077 const MAX_GLYPHS: u32 = 8;
1079 let limits = CmapIterLimits {
1080 glyph_count: MAX_GLYPHS,
1081 ..Default::default()
1082 };
1083 assert_eq!(cmap12.iter_with_limits(limits).count(), MAX_GLYPHS as usize);
1084 }
1085
1086 #[test]
1087 fn cmap12_iter_glyph_limit() {
1088 let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
1089 let cmap12 = find_cmap12(&font.cmap().unwrap()).unwrap();
1090 let mut limits = CmapIterLimits::default_for_font(&font);
1091 for glyph_count in 0..=11 {
1094 limits.glyph_count = glyph_count;
1095 assert_eq!(
1096 cmap12.iter_with_limits(limits).count(),
1097 (glyph_count as usize).saturating_sub(1)
1100 );
1101 }
1102 }
1103
1104 #[test]
1105 fn cmap12_iter_range_clamping() {
1106 let data = be_buffer! {
1107 12u16, 0u16, 0u32, 0u32, 2u32, [0u32, 16777215, 0], [255u32, 0xFFFFFFFF, 0] };
1116 let cmap12 = Cmap12::read(data.data().into()).unwrap();
1117 let ranges = cmap12
1118 .groups()
1119 .iter()
1120 .map(|group| (group.start_char_code(), group.end_char_code()))
1121 .collect::<Vec<_>>();
1122 assert_eq!(ranges, &[(0, 16777215), (255, u32::MAX)]);
1124 let limits = CmapIterLimits {
1126 glyph_count: u32::MAX,
1127 ..Default::default()
1128 };
1129 assert!(cmap12.iter_with_limits(limits).count() <= char::MAX as usize + 1);
1130 }
1131
1132 #[test]
1133 fn cmap12_iter_explicit_notdef() {
1134 let data = be_buffer! {
1135 12u16, 0u16, 0u32, 0u32, 1u32, [0_u32, 1_u32, 0] };
1143 let cmap12 = Cmap12::read(data.data().into()).unwrap();
1144 for (i, (codepoint, glyph_id)) in cmap12.iter().enumerate() {
1145 assert_eq!(codepoint as usize, i);
1146 assert_eq!(glyph_id.to_u32() as usize, i);
1147 }
1148 assert_eq!(cmap12.iter().next().unwrap().1, GlyphId::NOTDEF);
1149 }
1150
1151 fn cmap13_data() -> Vec<u8> {
1152 let data = be_buffer! {
1153 13u16, 0u16, 0u32, 0u32, 2u32, [0u32, 8, 20], [42u32, 46u32, 30] };
1162 data.to_vec()
1163 }
1164
1165 #[test]
1166 fn cmap13_map() {
1167 let data = cmap13_data();
1168 let cmap13 = Cmap13::read(FontData::new(&data)).unwrap();
1169 for ch in 0u32..=8 {
1170 assert_eq!(cmap13.map_codepoint(ch), Some(GlyphId::new(20)));
1171 }
1172 for ch in 9u32..42 {
1173 assert_eq!(cmap13.map_codepoint(ch), None);
1174 }
1175 for ch in 42u32..=46 {
1176 assert_eq!(cmap13.map_codepoint(ch), Some(GlyphId::new(30)));
1177 }
1178 for ch in 47u32..1024 {
1179 assert_eq!(cmap13.map_codepoint(ch), None);
1180 }
1181 }
1182
1183 #[test]
1184 fn cmap13_iter() {
1185 let data = cmap13_data();
1186 let cmap13 = Cmap13::read(FontData::new(&data)).unwrap();
1187 for (ch, gid) in cmap13.iter() {
1188 assert_eq!(cmap13.map_codepoint(ch), Some(gid));
1189 }
1190 }
1191
1192 #[test]
1193 fn cmap14_iter() {
1194 let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
1195 let cmap14 = find_cmap14(&font.cmap().unwrap()).unwrap();
1196 let mut count = 0;
1197 for (codepoint, selector, mapping) in cmap14.iter() {
1198 assert_eq!(cmap14.map_variant(codepoint, selector), Some(mapping));
1199 count += 1;
1200 }
1201 assert_eq!(count, 7);
1202 }
1203
1204 fn find_cmap4<'a>(cmap: &Cmap<'a>) -> Option<Cmap4<'a>> {
1205 cmap.encoding_records()
1206 .iter()
1207 .filter_map(|record| record.subtable(cmap.offset_data()).ok())
1208 .find_map(|subtable| match subtable {
1209 CmapSubtable::Format4(cmap4) => Some(cmap4),
1210 _ => None,
1211 })
1212 }
1213
1214 fn find_cmap12<'a>(cmap: &Cmap<'a>) -> Option<Cmap12<'a>> {
1215 cmap.encoding_records()
1216 .iter()
1217 .filter_map(|record| record.subtable(cmap.offset_data()).ok())
1218 .find_map(|subtable| match subtable {
1219 CmapSubtable::Format12(cmap12) => Some(cmap12),
1220 _ => None,
1221 })
1222 }
1223
1224 fn find_cmap14<'a>(cmap: &Cmap<'a>) -> Option<Cmap14<'a>> {
1225 cmap.encoding_records()
1226 .iter()
1227 .filter_map(|record| record.subtable(cmap.offset_data()).ok())
1228 .find_map(|subtable| match subtable {
1229 CmapSubtable::Format14(cmap14) => Some(cmap14),
1230 _ => None,
1231 })
1232 }
1233
1234 #[test]
1239 fn cmap4_bad_data() {
1240 let buf = font_test_data::cmap::repetitive_cmap4();
1241 let cmap4 = Cmap4::read(FontData::new(buf.as_slice())).unwrap();
1242
1243 assert_eq!(
1245 (6..=64).collect::<Vec<_>>(),
1246 cmap4.iter().map(|(cp, _)| cp).collect::<Vec<_>>()
1247 );
1248 }
1249
1250 fn cmap0_data() -> BeBuffer {
1251 be_buffer! {
1252 0u16,
1254 1u16,
1256 1u16,
1258 0u16,
1260 12u32,
1262 0u16,
1264 274u16,
1266 0u16,
1268 [0u8, 249, 32, 2, 198, 23, 1, 4, 26, 36,
1270 171, 168, 69, 151, 208, 238, 226, 153, 161, 138,
1271 160, 130, 169, 223, 162, 207, 146, 227, 111, 248,
1272 163, 79, 178, 27, 50, 234, 213, 57, 45, 63,
1273 103, 186, 30, 105, 131, 118, 35, 140, 51, 211,
1274 75, 172, 56, 71, 137, 99, 22, 76, 61, 125,
1275 39, 8, 177, 117, 108, 97, 202, 92, 49, 134,
1276 93, 43, 80, 66, 84, 54, 180, 113, 11, 176,
1277 229, 48, 47, 17, 124, 40, 119, 21, 13, 133,
1278 181, 224, 33, 128, 44, 46, 38, 24, 65, 152,
1279 197, 225, 102, 251, 157, 126, 182, 242, 28, 184,
1280 90, 170, 201, 144, 193, 189, 250, 142, 77, 221,
1281 81, 164, 154, 60, 37, 200, 12, 53, 219, 89,
1282 31, 209, 188, 179, 253, 220, 127, 18, 19, 64,
1283 20, 141, 98, 173, 55, 194, 70, 107, 228, 104,
1284 10, 9, 15, 217, 255, 222, 196, 236, 67, 165,
1285 5, 143, 149, 100, 91, 95, 135, 235, 145, 204,
1286 72, 114, 246, 82, 245, 233, 106, 158, 185, 212,
1287 86, 243, 16, 195, 123, 190, 120, 187, 132, 139,
1288 192, 239, 110, 183, 240, 214, 166, 41, 59, 231,
1289 42, 94, 244, 83, 121, 25, 215, 96, 73, 87,
1290 174, 136, 62, 206, 156, 175, 230, 150, 116, 147,
1291 68, 122, 78, 112, 6, 167, 232, 254, 52, 34,
1292 191, 85, 241, 14, 216, 155, 29, 101, 115, 210,
1293 252, 218, 129, 247, 203, 159, 109, 74, 7, 58,
1294 237, 199, 88, 205, 148, 3]
1295 }
1296 }
1297
1298 #[test]
1299 fn best_subtable_full() {
1300 let font = FontRef::new(font_test_data::VORG).unwrap();
1301 let cmap = font.cmap().unwrap();
1302 let (index, record, _) = cmap.best_subtable().unwrap();
1303 assert_eq!(
1304 (index, record.platform_id(), record.encoding_id()),
1305 (3, PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING)
1306 );
1307 }
1308
1309 #[test]
1310 fn best_subtable_bmp() {
1311 let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
1312 let cmap = font.cmap().unwrap();
1313 let (index, record, _) = cmap.best_subtable().unwrap();
1314 assert_eq!(
1315 (index, record.platform_id(), record.encoding_id()),
1316 (0, PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING)
1317 );
1318 }
1319
1320 #[test]
1321 fn best_subtable_symbol() {
1322 let font = FontRef::new(font_test_data::CMAP4_SYMBOL_PUA).unwrap();
1323 let cmap = font.cmap().unwrap();
1324 let (index, record, _) = cmap.best_subtable().unwrap();
1325 assert!(record.is_symbol());
1326 assert_eq!(
1327 (index, record.platform_id(), record.encoding_id()),
1328 (0, PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
1329 );
1330 }
1331
1332 #[test]
1333 fn uvs_subtable() {
1334 let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
1335 let cmap = font.cmap().unwrap();
1336 let (index, _) = cmap.uvs_subtable().unwrap();
1337 assert_eq!(index, 0);
1338 }
1339}