use crate::ps::string::Sid;
use crate::{FontData, FontRead, GlyphId, ReadError};
#[doc(inline)]
pub use super::v1::{
CharsetFormat0 as Format0, CharsetFormat1 as Format1, CharsetFormat2 as Format2,
CharsetRange1 as Range1, CharsetRange2 as Range2, CustomCharset,
};
#[derive(Clone)]
pub struct Charset<'a> {
kind: CharsetKind<'a>,
num_glyphs: u32,
}
impl<'a> Charset<'a> {
pub fn new(
cff_data: FontData<'a>,
charset_offset: usize,
num_glyphs: u32,
) -> Result<Self, ReadError> {
let kind = match charset_offset {
0 => CharsetKind::IsoAdobe,
1 => CharsetKind::Expert,
2 => CharsetKind::ExpertSubset,
_ => {
let data = cff_data
.split_off(charset_offset)
.ok_or(ReadError::OutOfBounds)?;
CharsetKind::Custom(CustomCharset::read(data)?)
}
};
Ok(Self { kind, num_glyphs })
}
pub fn kind(&self) -> &CharsetKind<'a> {
&self.kind
}
pub fn num_glyphs(&self) -> u32 {
self.num_glyphs
}
pub fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
let gid = glyph_id.to_u32();
if gid >= self.num_glyphs {
return Err(ReadError::OutOfBounds);
}
match &self.kind {
CharsetKind::IsoAdobe => {
if gid <= 228 {
Ok(Sid::new(gid as u16))
} else {
Err(ReadError::OutOfBounds)
}
}
CharsetKind::Expert => EXPERT_CHARSET
.get(gid as usize)
.copied()
.ok_or(ReadError::OutOfBounds)
.map(Sid::new),
CharsetKind::ExpertSubset => EXPERT_SUBSET_CHARSET
.get(gid as usize)
.copied()
.ok_or(ReadError::OutOfBounds)
.map(Sid::new),
CharsetKind::Custom(custom) => match custom {
CustomCharset::Format0(fmt) => fmt.string_id(glyph_id),
CustomCharset::Format1(fmt) => fmt.string_id(glyph_id),
CustomCharset::Format2(fmt) => fmt.string_id(glyph_id),
},
}
}
pub fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
let sid = string_id.to_u16();
match &self.kind {
CharsetKind::IsoAdobe => {
if sid <= 228 {
Ok(GlyphId::from(sid))
} else {
Err(ReadError::OutOfBounds)
}
}
CharsetKind::Expert => EXPERT_CHARSET
.iter()
.position(|n| *n == sid)
.map(|pos| GlyphId::new(pos as u32))
.ok_or(ReadError::OutOfBounds),
CharsetKind::ExpertSubset => EXPERT_SUBSET_CHARSET
.iter()
.position(|n| *n == sid)
.map(|pos| GlyphId::new(pos as u32))
.ok_or(ReadError::OutOfBounds),
CharsetKind::Custom(custom) => match custom {
CustomCharset::Format0(fmt) => fmt.glyph_id(string_id),
CustomCharset::Format1(fmt) => fmt.glyph_id(string_id),
CustomCharset::Format2(fmt) => fmt.glyph_id(string_id),
},
}
}
pub fn iter(&self) -> Iter<'a> {
match &self.kind {
CharsetKind::IsoAdobe
| CharsetKind::Expert
| CharsetKind::ExpertSubset
| CharsetKind::Custom(CustomCharset::Format0(_)) => {
Iter(IterKind::Simple(self.clone(), 0))
}
CharsetKind::Custom(CustomCharset::Format1(custom)) => Iter(IterKind::Custom1(
RangeIter::new(custom.ranges(), self.num_glyphs),
)),
CharsetKind::Custom(CustomCharset::Format2(custom)) => Iter(IterKind::Custom2(
RangeIter::new(custom.ranges(), self.num_glyphs),
)),
}
}
}
#[derive(Clone)]
pub enum CharsetKind<'a> {
IsoAdobe,
Expert,
ExpertSubset,
Custom(CustomCharset<'a>),
}
impl Format0<'_> {
fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
let gid = glyph_id.to_u32() as usize;
if gid == 0 {
Ok(Sid::new(0))
} else {
self.glyph()
.get(gid - 1)
.map(|id| Sid::new(id.get()))
.ok_or(ReadError::OutOfBounds)
}
}
fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
if string_id.to_u16() == 0 {
return Ok(GlyphId::NOTDEF);
}
self.glyph()
.iter()
.position(|n| n.get() == string_id.to_u16())
.map(|n| GlyphId::from((n as u16).saturating_add(1)))
.ok_or(ReadError::OutOfBounds)
}
}
impl Format1<'_> {
fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
string_id_from_ranges(self.ranges(), glyph_id)
}
fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
glyph_id_from_ranges(self.ranges(), string_id)
}
}
impl Format2<'_> {
fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
string_id_from_ranges(self.ranges(), glyph_id)
}
fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
glyph_id_from_ranges(self.ranges(), string_id)
}
}
fn string_id_from_ranges<T: CharsetRange>(
ranges: &[T],
glyph_id: GlyphId,
) -> Result<Sid, ReadError> {
let mut gid = glyph_id.to_u32();
if gid == 0 {
return Ok(Sid::new(0));
}
gid -= 1;
let mut end = 0u32;
for range in ranges {
let next_end = end
.checked_add(range.n_left() + 1)
.ok_or(ReadError::OutOfBounds)?;
if gid < next_end {
return (gid - end)
.checked_add(range.first())
.and_then(|sid| sid.try_into().ok())
.ok_or(ReadError::OutOfBounds)
.map(Sid::new);
}
end = next_end;
}
Err(ReadError::OutOfBounds)
}
fn glyph_id_from_ranges<T: CharsetRange>(
ranges: &[T],
string_id: Sid,
) -> Result<GlyphId, ReadError> {
let sid = string_id.to_u16() as u32;
if sid == 0 {
return Ok(GlyphId::NOTDEF);
}
let mut gid = 1u32;
for range in ranges {
let first = range.first();
let n_left = range.n_left();
if first <= sid && sid <= (first + n_left) {
gid += sid - first;
return Ok(GlyphId::new(gid));
}
gid += n_left + 1;
}
Err(ReadError::OutOfBounds)
}
trait CharsetRange {
fn first(&self) -> u32;
fn n_left(&self) -> u32;
}
impl CharsetRange for Range1 {
fn first(&self) -> u32 {
self.first.get() as u32
}
fn n_left(&self) -> u32 {
self.n_left as u32
}
}
impl CharsetRange for Range2 {
fn first(&self) -> u32 {
self.first.get() as u32
}
fn n_left(&self) -> u32 {
self.n_left.get() as u32
}
}
#[derive(Clone)]
pub struct Iter<'a>(IterKind<'a>);
impl Iterator for Iter<'_> {
type Item = (GlyphId, Sid);
fn next(&mut self) -> Option<Self::Item> {
match &mut self.0 {
IterKind::Simple(charset, cur) => {
let gid = GlyphId::new(*cur);
let sid = charset.string_id(gid).ok()?;
*cur = cur.checked_add(1)?;
Some((gid, sid))
}
IterKind::Custom1(custom) => custom.next(),
IterKind::Custom2(custom) => custom.next(),
}
}
}
#[derive(Clone)]
enum IterKind<'a> {
Simple(Charset<'a>, u32),
Custom1(RangeIter<'a, Range1>),
Custom2(RangeIter<'a, Range2>),
}
#[derive(Clone)]
struct RangeIter<'a, T> {
ranges: std::slice::Iter<'a, T>,
num_glyphs: u32,
gid: u32,
first: u32,
end: u32,
prev_end: u32,
}
impl<'a, T> RangeIter<'a, T>
where
T: CharsetRange,
{
fn new(ranges: &'a [T], num_glyphs: u32) -> Self {
let mut ranges = ranges.iter();
let (first, end) = next_range(&mut ranges).unwrap_or_default();
Self {
ranges,
num_glyphs,
gid: 0,
first,
end,
prev_end: 0,
}
}
fn next(&mut self) -> Option<(GlyphId, Sid)> {
if self.gid >= self.num_glyphs {
return None;
}
if self.gid == 0 {
self.gid += 1;
return Some((GlyphId::new(0), Sid::new(0)));
}
let gid = self.gid - 1;
self.gid = self.gid.checked_add(1)?;
while gid >= self.end {
let (first, end) = next_range(&mut self.ranges)?;
self.prev_end = self.end;
self.first = first;
self.end = self.prev_end.checked_add(end)?;
}
let sid = self
.first
.checked_add(gid.checked_sub(self.prev_end)?)?
.try_into()
.ok()?;
Some((GlyphId::new(gid + 1), Sid::new(sid)))
}
}
fn next_range<T: CharsetRange>(ranges: &mut std::slice::Iter<T>) -> Option<(u32, u32)> {
ranges
.next()
.map(|range| (range.first(), range.n_left() + 1))
}
#[rustfmt::skip]
const EXPERT_CHARSET: &[u16] = &[
0, 1, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 13, 14, 15, 99,
239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 252,
253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110,
267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282,
283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298,
299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
315, 316, 317, 318, 158, 155, 163, 319, 320, 321, 322, 323, 324, 325, 326, 150,
164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356,
357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372,
373, 374, 375, 376, 377, 378,
];
#[rustfmt::skip]
const EXPERT_SUBSET_CHARSET: &[u16] = &[
0, 1, 231, 232, 235, 236, 237, 238, 13, 14, 15, 99, 239, 240, 241, 242,
243, 244, 245, 246, 247, 248, 27, 28, 249, 250, 251, 253, 254, 255, 256, 257,
258, 259, 260, 261, 262, 263, 264, 265, 266, 109, 110, 267, 268, 269, 270, 272,
300, 301, 302, 305, 314, 315, 158, 155, 163, 320, 321, 322, 323, 324, 325, 326,
150, 164, 169, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339,
340, 341, 342, 343, 344, 345, 346
];
#[cfg(test)]
mod tests {
use super::*;
use font_test_data::bebuffer::BeBuffer;
#[test]
fn iso_adobe_charset() {
let charset_offset = 0;
let num_glyphs = 64;
let expected = |gid: GlyphId| Some(gid.to_u32());
test_simple_mapping(charset_offset, num_glyphs, expected);
}
#[test]
fn expert_charset() {
let charset_offset = 1;
let num_glyphs = 64;
let expected = |gid: GlyphId| {
EXPERT_CHARSET
.get(gid.to_u32() as usize)
.map(|id| *id as u32)
};
test_simple_mapping(charset_offset, num_glyphs, expected);
}
#[test]
fn expert_subset_charset() {
let charset_offset = 2;
let num_glyphs = 64;
let expected = |gid: GlyphId| {
EXPERT_SUBSET_CHARSET
.get(gid.to_u32() as usize)
.map(|id| *id as u32)
};
test_simple_mapping(charset_offset, num_glyphs, expected);
}
fn test_simple_mapping(
charset_offset: usize,
num_glyphs: u32,
expected: impl Fn(GlyphId) -> Option<u32>,
) {
let charset = Charset::new(FontData::new(&[]), charset_offset, num_glyphs).unwrap();
for gid in 0..num_glyphs {
let gid = GlyphId::new(gid);
let sid = expected(gid).unwrap();
assert_eq!(charset.string_id(gid).unwrap().to_u16() as u32, sid);
assert_eq!(charset.glyph_id(Sid::new(sid as _)).unwrap(), gid);
}
for gid in num_glyphs..u16::MAX as u32 {
assert_eq!(charset.string_id(GlyphId::new(gid)).ok(), None);
}
}
#[test]
fn custom_mapping_format0() {
let mut buf = BeBuffer::new();
let num_glyphs = 6;
buf = buf.extend([0u8; 4]);
buf = buf.push(0u8);
buf = buf.extend([2u16, 4, 6, 8, 10]);
let charset = Charset::new(FontData::new(buf.data()), 4, num_glyphs).unwrap();
for gid in 0..num_glyphs {
assert_eq!(
charset.string_id(GlyphId::new(gid)).unwrap().to_u16() as u32,
gid * 2
)
}
for (gid, sid) in charset.iter() {
assert_eq!(sid.to_u16() as u32, gid.to_u32() * 2);
}
assert_eq!(charset.iter().count() as u32, num_glyphs);
for gid in num_glyphs..u16::MAX as u32 {
assert_eq!(charset.string_id(GlyphId::new(gid)).ok(), None);
}
}
#[test]
fn custom_mapping_format1() {
let mut buf = BeBuffer::new();
let num_glyphs = 7;
buf = buf.extend([0u8; 4]);
buf = buf.push(1u8);
buf = buf.push(8u16).push(2u8);
buf = buf.push(1200u16).push(0u8);
buf = buf.push(20u16).push(1u8);
let expected_sids = [0, 8, 9, 10, 1200, 20, 21];
test_range_mapping(buf.data(), num_glyphs, &expected_sids);
}
#[test]
fn custom_mapping_format2() {
let mut buf = BeBuffer::new();
buf = buf.extend([0u8; 4]);
buf = buf.push(2u8);
buf = buf.push(8u16).push(2u16);
buf = buf.push(1200u16).push(0u16);
buf = buf.push(20u16).push(800u16);
let mut expected_sids = vec![0, 8, 9, 10, 1200];
for i in 0..=800 {
expected_sids.push(i + 20);
}
let num_glyphs = expected_sids.len() as u32;
test_range_mapping(buf.data(), num_glyphs, &expected_sids);
}
fn test_range_mapping(data: &[u8], num_glyphs: u32, expected_sids: &[u32]) {
let charset = Charset::new(FontData::new(data), 4, num_glyphs).unwrap();
for (gid, sid) in expected_sids.iter().enumerate() {
assert_eq!(
charset.string_id(GlyphId::new(gid as _)).unwrap().to_u16() as u32,
*sid
)
}
assert!(charset.iter().eq(expected_sids
.iter()
.enumerate()
.map(|(gid, sid)| (GlyphId::new(gid as u32), Sid::new(*sid as u16)))));
assert_eq!(charset.iter().count() as u32, num_glyphs);
for gid in num_glyphs..u16::MAX as u32 {
assert_eq!(charset.string_id(GlyphId::new(gid)).ok(), None);
}
for (gid, sid) in expected_sids.iter().enumerate() {
assert_eq!(
charset.glyph_id(Sid::new(*sid as u16)),
Ok(GlyphId::new(gid as u32))
);
}
}
}