include!("../../generated/generated_cmap.rs");
#[cfg(feature = "std")]
use crate::collections::IntSet;
use crate::{FontRef, TableProvider};
use std::ops::Range;
const WINDOWS_SYMBOL_ENCODING: u16 = 0;
const WINDOWS_UNICODE_BMP_ENCODING: u16 = 1;
const WINDOWS_UNICODE_FULL_ENCODING: u16 = 10;
const UNICODE_1_0_ENCODING: u16 = 0;
const UNICODE_1_1_ENCODING: u16 = 1;
const UNICODE_ISO_ENCODING: u16 = 2;
const UNICODE_2_0_BMP_ENCODING: u16 = 3;
const UNICODE_2_0_FULL_ENCODING: u16 = 4;
const UNICODE_FULL_ENCODING: u16 = 6;
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum MapVariant {
UseDefault,
Variant(GlyphId),
}
impl<'a> Cmap<'a> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
let codepoint = codepoint.into();
for record in self.encoding_records() {
if let Ok(subtable) = record.subtable(self.offset_data()) {
if let Some(gid) = subtable.map_codepoint(codepoint) {
return Some(gid);
}
}
}
None
}
pub fn best_subtable(&self) -> Option<(u16, EncodingRecord, CmapSubtable<'a>)> {
let offset_data = self.offset_data();
let records = self.encoding_records();
let find = |platform_id, encoding_id| {
for (index, record) in records.iter().enumerate() {
if record.platform_id() != platform_id || record.encoding_id() != encoding_id {
continue;
}
if let Ok(subtable) = record.subtable(offset_data) {
match subtable {
CmapSubtable::Format0(_)
| CmapSubtable::Format4(_)
| CmapSubtable::Format6(_)
| CmapSubtable::Format10(_)
| CmapSubtable::Format12(_)
| CmapSubtable::Format13(_) => {
return Some((index as u16, *record, subtable))
}
_ => {}
}
}
}
None
};
find(PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
.or_else(|| find(PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING))
.or_else(|| find(PlatformId::Unicode, UNICODE_FULL_ENCODING))
.or_else(|| find(PlatformId::Unicode, UNICODE_2_0_FULL_ENCODING))
.or_else(|| find(PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING))
.or_else(|| find(PlatformId::Unicode, UNICODE_2_0_BMP_ENCODING))
.or_else(|| find(PlatformId::Unicode, UNICODE_ISO_ENCODING))
.or_else(|| find(PlatformId::Unicode, UNICODE_1_1_ENCODING))
.or_else(|| find(PlatformId::Unicode, UNICODE_1_0_ENCODING))
.or_else(|| find(PlatformId::Macintosh, 0))
}
pub fn uvs_subtable(&self) -> Option<(u16, Cmap14<'a>)> {
let offset_data = self.offset_data();
for (index, record) in self.encoding_records().iter().enumerate() {
if let Ok(CmapSubtable::Format14(cmap14)) = record.subtable(offset_data) {
return Some((index as u16, cmap14));
};
}
None
}
pub fn subtable(&self, index: u16) -> Result<CmapSubtable<'a>, ReadError> {
self.encoding_records()
.get(index as usize)
.ok_or(ReadError::OutOfBounds)
.and_then(|encoding| encoding.subtable(self.offset_data()))
}
#[cfg(feature = "std")]
pub fn closure_glyphs(&self, unicodes: &IntSet<u32>, glyph_set: &mut IntSet<GlyphId>) {
for record in self.encoding_records() {
if let Ok(subtable) = record.subtable(self.offset_data()) {
match subtable {
CmapSubtable::Format14(format14) => {
format14.closure_glyphs(unicodes, glyph_set);
return;
}
_ => {
continue;
}
}
}
}
}
}
impl EncodingRecord {
pub fn is_symbol(&self) -> bool {
self.platform_id() == PlatformId::Windows && self.encoding_id() == WINDOWS_SYMBOL_ENCODING
}
pub fn is_mac_roman(&self) -> bool {
self.platform_id() == PlatformId::Macintosh && self.encoding_id() == 0
}
}
impl<'a> CmapSubtable<'a> {
pub fn language(&self) -> u32 {
match self {
Self::Format0(item) => item.language() as u32,
Self::Format2(item) => item.language() as u32,
Self::Format4(item) => item.language() as u32,
Self::Format6(item) => item.language() as u32,
Self::Format10(item) => item.language(),
Self::Format12(item) => item.language(),
Self::Format13(item) => item.language(),
_ => 0,
}
}
#[inline]
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
match self {
Self::Format0(item) => item.map_codepoint(codepoint),
Self::Format4(item) => item.map_codepoint(codepoint),
Self::Format6(item) => item.map_codepoint(codepoint),
Self::Format10(item) => item.map_codepoint(codepoint),
Self::Format12(item) => item.map_codepoint(codepoint),
Self::Format13(item) => item.map_codepoint(codepoint),
_ => None,
}
}
pub fn iter(&self) -> CmapSubtableIter<'a> {
let limits = CmapIterLimits {
max_char: u32::MAX,
glyph_count: u32::MAX,
};
self.iter_with_limits(limits)
}
pub fn iter_with_limits(&self, limits: CmapIterLimits) -> CmapSubtableIter<'a> {
match self {
Self::Format4(item) => CmapSubtableIter::Format4(item.iter()),
Self::Format6(item) => CmapSubtableIter::Format6(item.iter()),
Self::Format10(item) => CmapSubtableIter::Format10(item.iter()),
Self::Format12(item) => CmapSubtableIter::Format12(item.iter_with_limits(limits)),
Self::Format13(item) => CmapSubtableIter::Format13(item.iter_with_limits(limits)),
_ => CmapSubtableIter::None,
}
}
}
#[derive(Clone)]
#[non_exhaustive]
pub enum CmapSubtableIter<'a> {
None,
Format4(Cmap4Iter<'a>),
Format6(Cmap6Iter<'a>),
Format10(Cmap10Iter<'a>),
Format12(Cmap12Iter<'a>),
Format13(Cmap13Iter<'a>),
}
impl Iterator for CmapSubtableIter<'_> {
type Item = (u32, GlyphId);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::None => None,
Self::Format4(iter) => iter.next(),
Self::Format6(iter) => iter.next(),
Self::Format10(iter) => iter.next(),
Self::Format12(iter) => iter.next(),
Self::Format13(iter) => iter.next(),
}
}
}
impl Cmap0<'_> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
let codepoint = codepoint.into();
self.glyph_id_array()
.get(codepoint as usize)
.map(|g| GlyphId::new(*g as u32))
}
}
impl<'a> Cmap4<'a> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
let codepoint = codepoint.into();
if codepoint > 0xFFFF {
return None;
}
let codepoint = codepoint as u16;
let mut lo = 0;
let mut hi = self.seg_count_x2() as usize / 2;
let start_codes = self.start_code();
let end_codes = self.end_code();
while lo < hi {
let i = (lo + hi) / 2;
let start_code = start_codes.get(i)?.get();
if codepoint < start_code {
hi = i;
} else if codepoint > end_codes.get(i)?.get() {
lo = i + 1;
} else {
return self.lookup_glyph_id(codepoint, i, start_code);
}
}
None
}
pub fn iter(&self) -> Cmap4Iter<'a> {
Cmap4Iter::new(self.clone())
}
fn lookup_glyph_id(&self, codepoint: u16, index: usize, start_code: u16) -> Option<GlyphId> {
let deltas = self.id_delta();
let range_offsets = self.id_range_offsets();
let delta = deltas.get(index)?.get() as i32;
let range_offset = range_offsets.get(index)?.get() as usize;
if range_offset == 0 {
return Some(GlyphId::from((codepoint as i32 + delta) as u16));
}
let mut offset = range_offset / 2 + (codepoint - start_code) as usize;
offset = offset.saturating_sub(range_offsets.len() - index);
let gid = self.glyph_id_array().get(offset)?.get();
(gid != 0).then_some(GlyphId::from((gid as i32 + delta) as u16))
}
fn code_range(&self, index: usize) -> Option<Range<u32>> {
let start = self.start_code().get(index)?.get() as u32;
let end = self.end_code().get(index)?.get() as u32;
Some(start..end + 1)
}
}
#[derive(Clone)]
pub struct Cmap4Iter<'a> {
subtable: Cmap4<'a>,
cur_range: Range<u32>,
cur_start_code: u16,
cur_range_ix: usize,
}
impl<'a> Cmap4Iter<'a> {
fn new(subtable: Cmap4<'a>) -> Self {
let cur_range = subtable.code_range(0).unwrap_or_default();
let cur_start_code = cur_range.start as u16;
Self {
subtable,
cur_range,
cur_start_code,
cur_range_ix: 0,
}
}
}
impl Iterator for Cmap4Iter<'_> {
type Item = (u32, GlyphId);
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(codepoint) = self.cur_range.next() {
let Some(glyph_id) = self.subtable.lookup_glyph_id(
codepoint as u16,
self.cur_range_ix,
self.cur_start_code,
) else {
continue;
};
return Some((codepoint, glyph_id));
} else {
self.cur_range_ix += 1;
let next_range = self.subtable.code_range(self.cur_range_ix)?;
self.cur_range = next_range.start.max(self.cur_range.end)
..next_range.end.max(self.cur_range.end);
self.cur_start_code = self.cur_range.start as u16;
}
}
}
}
impl<'a> Cmap6<'a> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
let codepoint = codepoint.into();
let first = self.first_code() as u32;
let idx = codepoint.checked_sub(first)?;
self.glyph_id_array()
.get(idx as usize)
.map(|g| GlyphId::new(g.get() as u32))
}
pub fn iter(&self) -> Cmap6Iter<'a> {
Cmap6Iter {
first: self.first_code() as u32,
glyph_ids: self.glyph_id_array(),
pos: 0,
}
}
}
#[derive(Clone)]
pub struct Cmap6Iter<'a> {
first: u32,
glyph_ids: &'a [BigEndian<u16>],
pos: u32,
}
impl Iterator for Cmap6Iter<'_> {
type Item = (u32, GlyphId);
fn next(&mut self) -> Option<Self::Item> {
let gid = self.glyph_ids.get(self.pos as usize)?.get().into();
let codepoint = self.first + self.pos;
self.pos += 1;
Some((codepoint, gid))
}
}
impl<'a> Cmap10<'a> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
let codepoint = codepoint.into();
let idx = codepoint.checked_sub(self.start_char_code())?;
self.glyph_id_array()
.get(idx as usize)
.map(|g| GlyphId::new(g.get() as u32))
}
pub fn iter(&self) -> Cmap10Iter<'a> {
Cmap10Iter {
first: self.start_char_code(),
glyph_ids: self.glyph_id_array(),
pos: 0,
}
}
}
#[derive(Clone)]
pub struct Cmap10Iter<'a> {
first: u32,
glyph_ids: &'a [BigEndian<u16>],
pos: u32,
}
impl Iterator for Cmap10Iter<'_> {
type Item = (u32, GlyphId);
fn next(&mut self) -> Option<Self::Item> {
let gid = self.glyph_ids.get(self.pos as usize)?.get().into();
let codepoint = self.first + self.pos;
self.pos += 1;
Some((codepoint, gid))
}
}
trait AnyMapGroup {
const IS_CONSTANT: bool;
fn start_char_code(&self) -> u32;
fn end_char_code(&self) -> u32;
fn ref_glyph_id(&self) -> u32;
fn compute_glyph_id(codepoint: u32, start_char_code: u32, ref_glyph_id: u32) -> GlyphId {
if Self::IS_CONSTANT {
GlyphId::new(ref_glyph_id)
} else {
GlyphId::new(ref_glyph_id.wrapping_add(codepoint.wrapping_sub(start_char_code)))
}
}
}
impl AnyMapGroup for ConstantMapGroup {
const IS_CONSTANT: bool = true;
fn start_char_code(&self) -> u32 {
self.start_char_code()
}
fn end_char_code(&self) -> u32 {
self.end_char_code()
}
fn ref_glyph_id(&self) -> u32 {
self.glyph_id()
}
}
impl AnyMapGroup for SequentialMapGroup {
const IS_CONSTANT: bool = false;
fn start_char_code(&self) -> u32 {
self.start_char_code()
}
fn end_char_code(&self) -> u32 {
self.end_char_code()
}
fn ref_glyph_id(&self) -> u32 {
self.start_glyph_id()
}
}
fn cmap1213_map_codepoint<T: AnyMapGroup>(
groups: &[T],
codepoint: impl Into<u32>,
) -> Option<GlyphId> {
let codepoint = codepoint.into();
let mut lo = 0;
let mut hi = groups.len();
while lo < hi {
let i = (lo + hi) / 2;
let group = groups.get(i)?;
if codepoint < group.start_char_code() {
hi = i;
} else if codepoint > group.end_char_code() {
lo = i + 1;
} else {
return Some(T::compute_glyph_id(
codepoint,
group.start_char_code(),
group.ref_glyph_id(),
));
}
}
None
}
#[derive(Copy, Clone, Debug)]
pub struct CmapIterLimits {
pub max_char: u32,
pub glyph_count: u32,
}
impl CmapIterLimits {
pub fn default_for_font(font: &FontRef) -> Self {
let glyph_count = font
.maxp()
.map(|maxp| maxp.num_glyphs())
.unwrap_or(u16::MAX) as u32;
Self {
max_char: char::MAX as u32,
glyph_count,
}
}
}
impl Default for CmapIterLimits {
fn default() -> Self {
Self {
max_char: char::MAX as u32,
glyph_count: u16::MAX as u32,
}
}
}
#[derive(Clone, Debug)]
struct Cmap1213IterGroup {
range: Range<u64>,
start_code: u32,
ref_glyph_id: u32,
}
fn cmap1213_iter_group<T: AnyMapGroup>(
groups: &[T],
index: usize,
limits: &Option<CmapIterLimits>,
) -> Option<Cmap1213IterGroup> {
let group = groups.get(index)?;
let start_code = group.start_char_code();
let end_code = group.end_char_code() as u64 + 1;
let start_glyph_id = group.ref_glyph_id();
let end_code = if let Some(limits) = limits {
if T::IS_CONSTANT {
end_code.min(limits.max_char as u64)
} else {
(limits.glyph_count as u64)
.saturating_sub(start_glyph_id as u64)
.saturating_add(start_code as u64)
.min(end_code.min(limits.max_char as u64))
}
} else {
end_code
};
Some(Cmap1213IterGroup {
range: start_code as u64..end_code,
start_code,
ref_glyph_id: start_glyph_id,
})
}
#[derive(Clone)]
struct Cmap1213Iter<'a, T> {
groups: &'a [T],
cur_group: Option<Cmap1213IterGroup>,
cur_group_ix: usize,
limits: Option<CmapIterLimits>,
}
impl<'a, T> Cmap1213Iter<'a, T>
where
T: AnyMapGroup,
{
fn new(groups: &'a [T], limits: Option<CmapIterLimits>) -> Self {
let cur_group = cmap1213_iter_group(groups, 0, &limits);
Self {
groups,
cur_group,
cur_group_ix: 0,
limits,
}
}
}
impl<T> Iterator for Cmap1213Iter<'_, T>
where
T: AnyMapGroup,
{
type Item = (u32, GlyphId);
fn next(&mut self) -> Option<Self::Item> {
loop {
let group = self.cur_group.as_mut()?;
if let Some(codepoint) = group.range.next() {
let codepoint = codepoint as u32;
let glyph_id = T::compute_glyph_id(codepoint, group.start_code, group.ref_glyph_id);
return Some((codepoint, glyph_id));
} else {
self.cur_group_ix += 1;
let mut next_group =
cmap1213_iter_group(self.groups, self.cur_group_ix, &self.limits)?;
if next_group.range.start < group.range.end {
next_group.range = group.range.end..next_group.range.end;
}
self.cur_group = Some(next_group);
}
}
}
}
impl<'a> Cmap12<'a> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
cmap1213_map_codepoint(self.groups(), codepoint)
}
pub fn iter(&self) -> Cmap12Iter<'a> {
Cmap12Iter::new(self.clone(), None)
}
pub fn iter_with_limits(&self, limits: CmapIterLimits) -> Cmap12Iter<'a> {
Cmap12Iter::new(self.clone(), Some(limits))
}
}
#[derive(Clone)]
pub struct Cmap12Iter<'a>(Cmap1213Iter<'a, SequentialMapGroup>);
impl<'a> Cmap12Iter<'a> {
fn new(subtable: Cmap12<'a>, limits: Option<CmapIterLimits>) -> Self {
Self(Cmap1213Iter::new(subtable.groups(), limits))
}
}
impl Iterator for Cmap12Iter<'_> {
type Item = (u32, GlyphId);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl<'a> Cmap13<'a> {
pub fn map_codepoint(&self, codepoint: impl Into<u32>) -> Option<GlyphId> {
cmap1213_map_codepoint(self.groups(), codepoint)
}
pub fn iter(&self) -> Cmap13Iter<'a> {
Cmap13Iter::new(self.clone(), None)
}
pub fn iter_with_limits(&self, limits: CmapIterLimits) -> Cmap13Iter<'a> {
Cmap13Iter::new(self.clone(), Some(limits))
}
}
#[derive(Clone)]
pub struct Cmap13Iter<'a>(Cmap1213Iter<'a, ConstantMapGroup>);
impl<'a> Cmap13Iter<'a> {
fn new(subtable: Cmap13<'a>, limits: Option<CmapIterLimits>) -> Self {
Self(Cmap1213Iter::new(subtable.groups(), limits))
}
}
impl Iterator for Cmap13Iter<'_> {
type Item = (u32, GlyphId);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
impl<'a> Cmap14<'a> {
pub fn map_variant(
&self,
codepoint: impl Into<u32>,
selector: impl Into<u32>,
) -> Option<MapVariant> {
let codepoint = codepoint.into();
let selector = selector.into();
let selector_records = self.var_selector();
let selector_record = selector_records
.binary_search_by(|rec| {
let rec_selector: u32 = rec.var_selector().into();
rec_selector.cmp(&selector)
})
.ok()
.and_then(|idx| selector_records.get(idx))?;
if let Some(Ok(default_uvs)) = selector_record.default_uvs(self.offset_data()) {
use core::cmp::Ordering;
let found_default_uvs = default_uvs
.ranges()
.binary_search_by(|range| {
let start = range.start_unicode_value().into();
if codepoint < start {
Ordering::Greater
} else if codepoint > (start + range.additional_count() as u32) {
Ordering::Less
} else {
Ordering::Equal
}
})
.is_ok();
if found_default_uvs {
return Some(MapVariant::UseDefault);
}
}
let non_default_uvs = selector_record.non_default_uvs(self.offset_data())?.ok()?;
let mapping = non_default_uvs.uvs_mapping();
let ix = mapping
.binary_search_by(|map| {
let map_codepoint: u32 = map.unicode_value().into();
map_codepoint.cmp(&codepoint)
})
.ok()?;
Some(MapVariant::Variant(GlyphId::from(
mapping.get(ix)?.glyph_id(),
)))
}
pub fn iter(&self) -> Cmap14Iter<'a> {
Cmap14Iter::new(self.clone())
}
fn selector(
&self,
index: usize,
) -> (
Option<VariationSelector>,
Option<DefaultUvs<'a>>,
Option<NonDefaultUvs<'a>>,
) {
let selector = self.var_selector().get(index).cloned();
let default_uvs = selector.as_ref().and_then(|selector| {
selector
.default_uvs(self.offset_data())
.transpose()
.ok()
.flatten()
});
let non_default_uvs = selector.as_ref().and_then(|selector| {
selector
.non_default_uvs(self.offset_data())
.transpose()
.ok()
.flatten()
});
(selector, default_uvs, non_default_uvs)
}
#[cfg(feature = "std")]
pub fn closure_glyphs(&self, unicodes: &IntSet<u32>, glyph_set: &mut IntSet<GlyphId>) {
for selector in self.var_selector() {
if !unicodes.contains(selector.var_selector().to_u32()) {
continue;
}
if let Some(non_default_uvs) = selector
.non_default_uvs(self.offset_data())
.transpose()
.ok()
.flatten()
{
glyph_set.extend(
non_default_uvs
.uvs_mapping()
.iter()
.filter(|m| unicodes.contains(m.unicode_value().to_u32()))
.map(|m| m.glyph_id().into()),
);
}
}
}
}
#[derive(Clone)]
pub struct Cmap14Iter<'a> {
subtable: Cmap14<'a>,
selector_record: Option<VariationSelector>,
default_uvs: Option<DefaultUvsIter<'a>>,
non_default_uvs: Option<NonDefaultUvsIter<'a>>,
cur_selector_ix: usize,
}
impl<'a> Cmap14Iter<'a> {
fn new(subtable: Cmap14<'a>) -> Self {
let (selector_record, default_uvs, non_default_uvs) = subtable.selector(0);
Self {
subtable,
selector_record,
default_uvs: default_uvs.map(DefaultUvsIter::new),
non_default_uvs: non_default_uvs.map(NonDefaultUvsIter::new),
cur_selector_ix: 0,
}
}
}
impl Iterator for Cmap14Iter<'_> {
type Item = (u32, u32, MapVariant);
fn next(&mut self) -> Option<Self::Item> {
loop {
let selector_record = self.selector_record.as_ref()?;
let selector: u32 = selector_record.var_selector().into();
if let Some(default_uvs) = self.default_uvs.as_mut() {
if let Some(codepoint) = default_uvs.next() {
return Some((codepoint, selector, MapVariant::UseDefault));
}
}
if let Some(non_default_uvs) = self.non_default_uvs.as_mut() {
if let Some((codepoint, variant)) = non_default_uvs.next() {
return Some((codepoint, selector, MapVariant::Variant(variant.into())));
}
}
self.cur_selector_ix += 1;
let (selector_record, default_uvs, non_default_uvs) =
self.subtable.selector(self.cur_selector_ix);
self.selector_record = selector_record;
self.default_uvs = default_uvs.map(DefaultUvsIter::new);
self.non_default_uvs = non_default_uvs.map(NonDefaultUvsIter::new);
}
}
}
#[derive(Clone)]
struct DefaultUvsIter<'a> {
ranges: std::slice::Iter<'a, UnicodeRange>,
cur_range: Range<u32>,
}
impl<'a> DefaultUvsIter<'a> {
fn new(ranges: DefaultUvs<'a>) -> Self {
let mut ranges = ranges.ranges().iter();
let cur_range = if let Some(range) = ranges.next() {
let start: u32 = range.start_unicode_value().into();
let end = start + range.additional_count() as u32 + 1;
start..end
} else {
0..0
};
Self { ranges, cur_range }
}
}
impl Iterator for DefaultUvsIter<'_> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(codepoint) = self.cur_range.next() {
return Some(codepoint);
}
let range = self.ranges.next()?;
let start: u32 = range.start_unicode_value().into();
let end = start + range.additional_count() as u32 + 1;
self.cur_range = start..end;
}
}
}
#[derive(Clone)]
struct NonDefaultUvsIter<'a> {
iter: std::slice::Iter<'a, UvsMapping>,
}
impl<'a> NonDefaultUvsIter<'a> {
fn new(uvs: NonDefaultUvs<'a>) -> Self {
Self {
iter: uvs.uvs_mapping().iter(),
}
}
}
impl Iterator for NonDefaultUvsIter<'_> {
type Item = (u32, GlyphId16);
fn next(&mut self) -> Option<Self::Item> {
let mapping = self.iter.next()?;
let codepoint: u32 = mapping.unicode_value().into();
let glyph_id = GlyphId16::new(mapping.glyph_id());
Some((codepoint, glyph_id))
}
}
#[cfg(test)]
mod tests {
use font_test_data::{be_buffer, bebuffer::BeBuffer};
use super::*;
use crate::{FontRef, GlyphId, TableProvider};
#[test]
fn map_codepoints() {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
let cmap = font.cmap().unwrap();
assert_eq!(cmap.map_codepoint('A'), Some(GlyphId::new(1)));
assert_eq!(cmap.map_codepoint('À'), Some(GlyphId::new(2)));
assert_eq!(cmap.map_codepoint('`'), Some(GlyphId::new(3)));
assert_eq!(cmap.map_codepoint('B'), None);
let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
let cmap = font.cmap().unwrap();
assert_eq!(cmap.map_codepoint(' '), Some(GlyphId::new(1)));
assert_eq!(cmap.map_codepoint(0xE_u32), Some(GlyphId::new(2)));
assert_eq!(cmap.map_codepoint('B'), None);
let cmap0_data = cmap0_data();
let cmap = Cmap::read(FontData::new(cmap0_data.data())).unwrap();
assert_eq!(cmap.map_codepoint(0u8), Some(GlyphId::new(0)));
assert_eq!(cmap.map_codepoint(b' '), Some(GlyphId::new(178)));
assert_eq!(cmap.map_codepoint(b'r'), Some(GlyphId::new(193)));
assert_eq!(cmap.map_codepoint(b'X'), Some(GlyphId::new(13)));
assert_eq!(cmap.map_codepoint(255u8), Some(GlyphId::new(3)));
let cmap6_data = be_buffer! {
0u16,
1u16,
1u16,
0u16,
12u32,
6u16,
32u16,
0u16,
32u16,
5u16,
[10u16, 15, 7, 20, 4]
};
let cmap = Cmap::read(FontData::new(cmap6_data.data())).unwrap();
assert_eq!(cmap.map_codepoint(0u8), None);
assert_eq!(cmap.map_codepoint(31u8), None);
assert_eq!(cmap.map_codepoint(33u8), Some(GlyphId::new(15)));
assert_eq!(cmap.map_codepoint(35u8), Some(GlyphId::new(20)));
assert_eq!(cmap.map_codepoint(36u8), Some(GlyphId::new(4)));
assert_eq!(cmap.map_codepoint(50u8), None);
}
#[test]
fn map_variants() {
use super::MapVariant::*;
let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
let cmap = font.cmap().unwrap();
let cmap14 = find_cmap14(&cmap).unwrap();
let selector = '\u{e0100}';
assert_eq!(cmap14.map_variant('a', selector), None);
assert_eq!(cmap14.map_variant('\u{4e00}', selector), Some(UseDefault));
assert_eq!(cmap14.map_variant('\u{4e06}', selector), Some(UseDefault));
assert_eq!(
cmap14.map_variant('\u{4e08}', selector),
Some(Variant(GlyphId::new(25)))
);
assert_eq!(
cmap14.map_variant('\u{4e09}', selector),
Some(Variant(GlyphId::new(26)))
);
}
#[test]
#[cfg(feature = "std")]
fn cmap14_closure_glyphs() {
let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
let cmap = font.cmap().unwrap();
let mut unicodes = IntSet::empty();
unicodes.insert(0x4e08_u32);
unicodes.insert(0xe0100_u32);
let mut glyph_set = IntSet::empty();
glyph_set.insert(GlyphId::new(18));
cmap.closure_glyphs(&unicodes, &mut glyph_set);
assert_eq!(glyph_set.len(), 2);
assert!(glyph_set.contains(GlyphId::new(18)));
assert!(glyph_set.contains(GlyphId::new(25)));
}
#[test]
fn cmap4_iter() {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
let mut count = 0;
for (codepoint, glyph_id) in cmap4.iter() {
assert_eq!(cmap4.map_codepoint(codepoint), Some(glyph_id));
count += 1;
}
assert_eq!(count, 4);
let font = FontRef::new(font_test_data::SIMPLE_GLYF).unwrap();
let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
let mut count = 0;
for (codepoint, glyph_id) in cmap4.iter() {
assert_eq!(cmap4.map_codepoint(codepoint), Some(glyph_id));
count += 1;
}
assert_eq!(count, 3);
}
#[test]
fn cmap4_iter_explicit_notdef() {
let font = FontRef::new(font_test_data::VAZIRMATN_VAR).unwrap();
let cmap4 = find_cmap4(&font.cmap().unwrap()).unwrap();
let mut notdef_count = 0;
for (_, glyph_id) in cmap4.iter() {
notdef_count += (glyph_id == GlyphId::NOTDEF) as i32;
}
assert!(notdef_count > 0);
assert_eq!(cmap4.map_codepoint(0xFFFF_u32), Some(GlyphId::NOTDEF));
}
#[test]
fn cmap4_iter_sparse_range() {
#[rustfmt::skip]
let cmap4_data: &[u16] = &[
4, 0, 0,
4,
0, 0, 0,
262, 0xFFFF,
0,
259, 0xFFFF,
0, 1,
4, 0,
236, 0, 0, 326,
];
let mut buf = BeBuffer::new();
for &word in cmap4_data {
buf = buf.push(word);
}
let cmap4 = Cmap4::read(FontData::new(&buf)).unwrap();
let mappings = cmap4
.iter()
.map(|(ch, gid)| (ch, gid.to_u32()))
.collect::<Vec<_>>();
assert_eq!(mappings, &[(259, 236), (262, 326), (65535, 0)]);
}
const CMAP6_PAIRS: &[(u32, u32)] = &[
(0x1723, 1),
(0x1724, 2),
(0x1725, 3),
(0x1726, 4),
(0x1727, 5),
];
#[test]
fn cmap6_map() {
let font = FontRef::new(font_test_data::CMAP6).unwrap();
let cmap = font.cmap().unwrap();
let CmapSubtable::Format6(cmap6) = cmap.subtable(0).unwrap() else {
panic!("should be a format 6 subtable");
};
for (ch, gid) in CMAP6_PAIRS {
assert_eq!(cmap6.map_codepoint(*ch).unwrap().to_u32(), *gid);
}
assert!(cmap6.map_codepoint(CMAP6_PAIRS[0].0 - 1).is_none());
assert!(cmap6
.map_codepoint(CMAP6_PAIRS.last().copied().unwrap().0 + 1)
.is_none());
}
#[test]
fn cmap6_iter() {
let font = FontRef::new(font_test_data::CMAP6).unwrap();
let cmap = font.cmap().unwrap();
let CmapSubtable::Format6(cmap6) = cmap.subtable(0).unwrap() else {
panic!("should be a format 6 subtable");
};
let pairs = cmap6
.iter()
.map(|(ch, gid)| (ch, gid.to_u32()))
.collect::<Vec<_>>();
assert_eq!(pairs, CMAP6_PAIRS);
}
const CMAP10_PAIRS: &[(u32, u32)] = &[(0x109423, 26), (0x109424, 27), (0x109425, 32)];
#[test]
fn cmap10_map() {
let font = FontRef::new(font_test_data::CMAP10).unwrap();
let cmap = font.cmap().unwrap();
let CmapSubtable::Format10(cmap10) = cmap.subtable(0).unwrap() else {
panic!("should be a format 10 subtable");
};
for (ch, gid) in CMAP10_PAIRS {
assert_eq!(cmap10.map_codepoint(*ch).unwrap().to_u32(), *gid);
}
assert!(cmap10.map_codepoint(CMAP10_PAIRS[0].0 - 1).is_none());
assert!(cmap10
.map_codepoint(CMAP10_PAIRS.last().copied().unwrap().0 + 1)
.is_none());
}
#[test]
fn cmap10_iter() {
let font = FontRef::new(font_test_data::CMAP10).unwrap();
let cmap = font.cmap().unwrap();
let CmapSubtable::Format10(cmap10) = cmap.subtable(0).unwrap() else {
panic!("should be a format 10 subtable");
};
let pairs = cmap10
.iter()
.map(|(ch, gid)| (ch, gid.to_u32()))
.collect::<Vec<_>>();
assert_eq!(pairs, CMAP10_PAIRS);
}
#[test]
fn cmap12_iter() {
let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
let cmap12 = find_cmap12(&font.cmap().unwrap()).unwrap();
let mut count = 0;
for (codepoint, glyph_id) in cmap12.iter() {
assert_eq!(cmap12.map_codepoint(codepoint), Some(glyph_id));
count += 1;
}
assert_eq!(count, 10);
}
#[test]
fn cmap12_iter_avoid_overflow() {
let data = be_buffer! {
12u16, 0u16, 0u32, 0u32, 2u32, [0xFFFFFFFA_u32, 0xFFFFFFFC, 0], [0xFFFFFFFB_u32, 0xFFFFFFFF, 0] };
let cmap12 = Cmap12::read(data.data().into()).unwrap();
let _ = cmap12.iter().count();
}
#[test]
fn cmap12_iter_avoid_timeout() {
let cmap12_data = be_buffer! {
12u16, 0u16, 0u32, 0u32, 1u32, [170u32, 1330926671, 328960] };
let cmap12 = Cmap12::read(cmap12_data.data().into()).unwrap();
assert!(
cmap12.iter_with_limits(CmapIterLimits::default()).count() <= char::MAX as usize + 1
);
}
#[test]
fn cmap12_iter_avoid_timeout2() {
let cmap12_data = be_buffer! {
12u16, 0u16, 0u32, 0u32, 3u32, [199u32, 16777271, 2],
[262u32, 262, 3],
[268u32, 268, 4]
};
let cmap12 = Cmap12::read(cmap12_data.data().into()).unwrap();
const MAX_GLYPHS: u32 = 8;
let limits = CmapIterLimits {
glyph_count: MAX_GLYPHS,
..Default::default()
};
assert_eq!(cmap12.iter_with_limits(limits).count(), MAX_GLYPHS as usize);
}
#[test]
fn cmap12_iter_glyph_limit() {
let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
let cmap12 = find_cmap12(&font.cmap().unwrap()).unwrap();
let mut limits = CmapIterLimits::default_for_font(&font);
for glyph_count in 0..=11 {
limits.glyph_count = glyph_count;
assert_eq!(
cmap12.iter_with_limits(limits).count(),
(glyph_count as usize).saturating_sub(1)
);
}
}
#[test]
fn cmap12_iter_range_clamping() {
let data = be_buffer! {
12u16, 0u16, 0u32, 0u32, 2u32, [0u32, 16777215, 0], [255u32, 0xFFFFFFFF, 0] };
let cmap12 = Cmap12::read(data.data().into()).unwrap();
let ranges = cmap12
.groups()
.iter()
.map(|group| (group.start_char_code(), group.end_char_code()))
.collect::<Vec<_>>();
assert_eq!(ranges, &[(0, 16777215), (255, u32::MAX)]);
let limits = CmapIterLimits {
glyph_count: u32::MAX,
..Default::default()
};
assert!(cmap12.iter_with_limits(limits).count() <= char::MAX as usize + 1);
}
#[test]
fn cmap12_iter_explicit_notdef() {
let data = be_buffer! {
12u16, 0u16, 0u32, 0u32, 1u32, [0_u32, 1_u32, 0] };
let cmap12 = Cmap12::read(data.data().into()).unwrap();
for (i, (codepoint, glyph_id)) in cmap12.iter().enumerate() {
assert_eq!(codepoint as usize, i);
assert_eq!(glyph_id.to_u32() as usize, i);
}
assert_eq!(cmap12.iter().next().unwrap().1, GlyphId::NOTDEF);
}
fn cmap13_data() -> Vec<u8> {
let data = be_buffer! {
13u16, 0u16, 0u32, 0u32, 2u32, [0u32, 8, 20], [42u32, 46u32, 30] };
data.to_vec()
}
#[test]
fn cmap13_map() {
let data = cmap13_data();
let cmap13 = Cmap13::read(FontData::new(&data)).unwrap();
for ch in 0u32..=8 {
assert_eq!(cmap13.map_codepoint(ch), Some(GlyphId::new(20)));
}
for ch in 9u32..42 {
assert_eq!(cmap13.map_codepoint(ch), None);
}
for ch in 42u32..=46 {
assert_eq!(cmap13.map_codepoint(ch), Some(GlyphId::new(30)));
}
for ch in 47u32..1024 {
assert_eq!(cmap13.map_codepoint(ch), None);
}
}
#[test]
fn cmap13_iter() {
let data = cmap13_data();
let cmap13 = Cmap13::read(FontData::new(&data)).unwrap();
for (ch, gid) in cmap13.iter() {
assert_eq!(cmap13.map_codepoint(ch), Some(gid));
}
}
#[test]
fn cmap14_iter() {
let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
let cmap14 = find_cmap14(&font.cmap().unwrap()).unwrap();
let mut count = 0;
for (codepoint, selector, mapping) in cmap14.iter() {
assert_eq!(cmap14.map_variant(codepoint, selector), Some(mapping));
count += 1;
}
assert_eq!(count, 7);
}
fn find_cmap4<'a>(cmap: &Cmap<'a>) -> Option<Cmap4<'a>> {
cmap.encoding_records()
.iter()
.filter_map(|record| record.subtable(cmap.offset_data()).ok())
.find_map(|subtable| match subtable {
CmapSubtable::Format4(cmap4) => Some(cmap4),
_ => None,
})
}
fn find_cmap12<'a>(cmap: &Cmap<'a>) -> Option<Cmap12<'a>> {
cmap.encoding_records()
.iter()
.filter_map(|record| record.subtable(cmap.offset_data()).ok())
.find_map(|subtable| match subtable {
CmapSubtable::Format12(cmap12) => Some(cmap12),
_ => None,
})
}
fn find_cmap14<'a>(cmap: &Cmap<'a>) -> Option<Cmap14<'a>> {
cmap.encoding_records()
.iter()
.filter_map(|record| record.subtable(cmap.offset_data()).ok())
.find_map(|subtable| match subtable {
CmapSubtable::Format14(cmap14) => Some(cmap14),
_ => None,
})
}
#[test]
fn cmap4_bad_data() {
let buf = font_test_data::cmap::repetitive_cmap4();
let cmap4 = Cmap4::read(FontData::new(buf.as_slice())).unwrap();
assert_eq!(
(6..=64).collect::<Vec<_>>(),
cmap4.iter().map(|(cp, _)| cp).collect::<Vec<_>>()
);
}
fn cmap0_data() -> BeBuffer {
be_buffer! {
0u16,
1u16,
1u16,
0u16,
12u32,
0u16,
274u16,
0u16,
[0u8, 249, 32, 2, 198, 23, 1, 4, 26, 36,
171, 168, 69, 151, 208, 238, 226, 153, 161, 138,
160, 130, 169, 223, 162, 207, 146, 227, 111, 248,
163, 79, 178, 27, 50, 234, 213, 57, 45, 63,
103, 186, 30, 105, 131, 118, 35, 140, 51, 211,
75, 172, 56, 71, 137, 99, 22, 76, 61, 125,
39, 8, 177, 117, 108, 97, 202, 92, 49, 134,
93, 43, 80, 66, 84, 54, 180, 113, 11, 176,
229, 48, 47, 17, 124, 40, 119, 21, 13, 133,
181, 224, 33, 128, 44, 46, 38, 24, 65, 152,
197, 225, 102, 251, 157, 126, 182, 242, 28, 184,
90, 170, 201, 144, 193, 189, 250, 142, 77, 221,
81, 164, 154, 60, 37, 200, 12, 53, 219, 89,
31, 209, 188, 179, 253, 220, 127, 18, 19, 64,
20, 141, 98, 173, 55, 194, 70, 107, 228, 104,
10, 9, 15, 217, 255, 222, 196, 236, 67, 165,
5, 143, 149, 100, 91, 95, 135, 235, 145, 204,
72, 114, 246, 82, 245, 233, 106, 158, 185, 212,
86, 243, 16, 195, 123, 190, 120, 187, 132, 139,
192, 239, 110, 183, 240, 214, 166, 41, 59, 231,
42, 94, 244, 83, 121, 25, 215, 96, 73, 87,
174, 136, 62, 206, 156, 175, 230, 150, 116, 147,
68, 122, 78, 112, 6, 167, 232, 254, 52, 34,
191, 85, 241, 14, 216, 155, 29, 101, 115, 210,
252, 218, 129, 247, 203, 159, 109, 74, 7, 58,
237, 199, 88, 205, 148, 3]
}
}
#[test]
fn best_subtable_full() {
let font = FontRef::new(font_test_data::VORG).unwrap();
let cmap = font.cmap().unwrap();
let (index, record, _) = cmap.best_subtable().unwrap();
assert_eq!(
(index, record.platform_id(), record.encoding_id()),
(3, PlatformId::Windows, WINDOWS_UNICODE_FULL_ENCODING)
);
}
#[test]
fn best_subtable_bmp() {
let font = FontRef::new(font_test_data::CMAP12_FONT1).unwrap();
let cmap = font.cmap().unwrap();
let (index, record, _) = cmap.best_subtable().unwrap();
assert_eq!(
(index, record.platform_id(), record.encoding_id()),
(0, PlatformId::Windows, WINDOWS_UNICODE_BMP_ENCODING)
);
}
#[test]
fn best_subtable_symbol() {
let font = FontRef::new(font_test_data::CMAP4_SYMBOL_PUA).unwrap();
let cmap = font.cmap().unwrap();
let (index, record, _) = cmap.best_subtable().unwrap();
assert!(record.is_symbol());
assert_eq!(
(index, record.platform_id(), record.encoding_id()),
(0, PlatformId::Windows, WINDOWS_SYMBOL_ENCODING)
);
}
#[test]
fn uvs_subtable() {
let font = FontRef::new(font_test_data::CMAP14_FONT1).unwrap();
let cmap = font.cmap().unwrap();
let (index, _) = cmap.uvs_subtable().unwrap();
assert_eq!(index, 0);
}
}