Skip to main content

read_fonts/ps/cff/
charset.rs

1//! CFF charset support.
2
3use crate::ps::string::Sid;
4use crate::{FontData, FontRead, GlyphId, ReadError};
5
6#[doc(inline)]
7pub use super::v1::{
8    CharsetFormat0 as Format0, CharsetFormat1 as Format1, CharsetFormat2 as Format2,
9    CharsetRange1 as Range1, CharsetRange2 as Range2, CustomCharset,
10};
11
12/// Character set for mapping from glyph to string identifiers.
13///
14/// See <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf#page=21>
15#[derive(Clone)]
16pub struct Charset<'a> {
17    kind: CharsetKind<'a>,
18    num_glyphs: u32,
19}
20
21impl<'a> Charset<'a> {
22    pub fn new(
23        cff_data: FontData<'a>,
24        charset_offset: usize,
25        num_glyphs: u32,
26    ) -> Result<Self, ReadError> {
27        let kind = match charset_offset {
28            0 => CharsetKind::IsoAdobe,
29            1 => CharsetKind::Expert,
30            2 => CharsetKind::ExpertSubset,
31            _ => {
32                let data = cff_data
33                    .split_off(charset_offset)
34                    .ok_or(ReadError::OutOfBounds)?;
35                CharsetKind::Custom(CustomCharset::read(data)?)
36            }
37        };
38        Ok(Self { kind, num_glyphs })
39    }
40
41    pub fn kind(&self) -> &CharsetKind<'a> {
42        &self.kind
43    }
44
45    pub fn num_glyphs(&self) -> u32 {
46        self.num_glyphs
47    }
48
49    /// Returns the string identifier for the given glyph identifier.
50    pub fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
51        let gid = glyph_id.to_u32();
52        if gid >= self.num_glyphs {
53            return Err(ReadError::OutOfBounds);
54        }
55        match &self.kind {
56            CharsetKind::IsoAdobe => {
57                // The ISOAdobe charset is an identity mapping of gid->sid up
58                // to 228 entries
59                // <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf#page=45>
60                if gid <= 228 {
61                    Ok(Sid::new(gid as u16))
62                } else {
63                    Err(ReadError::OutOfBounds)
64                }
65            }
66            CharsetKind::Expert => EXPERT_CHARSET
67                .get(gid as usize)
68                .copied()
69                .ok_or(ReadError::OutOfBounds)
70                .map(Sid::new),
71            CharsetKind::ExpertSubset => EXPERT_SUBSET_CHARSET
72                .get(gid as usize)
73                .copied()
74                .ok_or(ReadError::OutOfBounds)
75                .map(Sid::new),
76            CharsetKind::Custom(custom) => match custom {
77                CustomCharset::Format0(fmt) => fmt.string_id(glyph_id),
78                CustomCharset::Format1(fmt) => fmt.string_id(glyph_id),
79                CustomCharset::Format2(fmt) => fmt.string_id(glyph_id),
80            },
81        }
82    }
83
84    /// Returns the glyph identifier for the given string identifier.
85    pub fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
86        let sid = string_id.to_u16();
87        match &self.kind {
88            CharsetKind::IsoAdobe => {
89                // The ISOAdobe charset is an identity mapping of gid->sid up
90                // to 228 entries
91                // <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf#page=45>
92                if sid <= 228 {
93                    Ok(GlyphId::from(sid))
94                } else {
95                    Err(ReadError::OutOfBounds)
96                }
97            }
98            CharsetKind::Expert => EXPERT_CHARSET
99                .iter()
100                .position(|n| *n == sid)
101                .map(|pos| GlyphId::new(pos as u32))
102                .ok_or(ReadError::OutOfBounds),
103            CharsetKind::ExpertSubset => EXPERT_SUBSET_CHARSET
104                .iter()
105                .position(|n| *n == sid)
106                .map(|pos| GlyphId::new(pos as u32))
107                .ok_or(ReadError::OutOfBounds),
108            CharsetKind::Custom(custom) => match custom {
109                CustomCharset::Format0(fmt) => fmt.glyph_id(string_id),
110                CustomCharset::Format1(fmt) => fmt.glyph_id(string_id),
111                CustomCharset::Format2(fmt) => fmt.glyph_id(string_id),
112            },
113        }
114    }
115
116    /// Returns an iterator over all of the glyph and string identifier
117    /// mappings.
118    pub fn iter(&self) -> Iter<'a> {
119        match &self.kind {
120            CharsetKind::IsoAdobe
121            | CharsetKind::Expert
122            | CharsetKind::ExpertSubset
123            | CharsetKind::Custom(CustomCharset::Format0(_)) => {
124                Iter(IterKind::Simple(self.clone(), 0))
125            }
126            CharsetKind::Custom(CustomCharset::Format1(custom)) => Iter(IterKind::Custom1(
127                RangeIter::new(custom.ranges(), self.num_glyphs),
128            )),
129            CharsetKind::Custom(CustomCharset::Format2(custom)) => Iter(IterKind::Custom2(
130                RangeIter::new(custom.ranges(), self.num_glyphs),
131            )),
132        }
133    }
134}
135
136/// Predefined and custom character sets.
137#[derive(Clone)]
138pub enum CharsetKind<'a> {
139    IsoAdobe,
140    Expert,
141    ExpertSubset,
142    Custom(CustomCharset<'a>),
143}
144
145impl Format0<'_> {
146    fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
147        let gid = glyph_id.to_u32() as usize;
148        if gid == 0 {
149            Ok(Sid::new(0))
150        } else {
151            self.glyph()
152                .get(gid - 1)
153                .map(|id| Sid::new(id.get()))
154                .ok_or(ReadError::OutOfBounds)
155        }
156    }
157
158    fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
159        if string_id.to_u16() == 0 {
160            return Ok(GlyphId::NOTDEF);
161        }
162        self.glyph()
163            .iter()
164            .position(|n| n.get() == string_id.to_u16())
165            .map(|n| GlyphId::from((n as u16).saturating_add(1)))
166            .ok_or(ReadError::OutOfBounds)
167    }
168}
169
170impl Format1<'_> {
171    fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
172        string_id_from_ranges(self.ranges(), glyph_id)
173    }
174
175    fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
176        glyph_id_from_ranges(self.ranges(), string_id)
177    }
178}
179
180impl Format2<'_> {
181    fn string_id(&self, glyph_id: GlyphId) -> Result<Sid, ReadError> {
182        string_id_from_ranges(self.ranges(), glyph_id)
183    }
184
185    fn glyph_id(&self, string_id: Sid) -> Result<GlyphId, ReadError> {
186        glyph_id_from_ranges(self.ranges(), string_id)
187    }
188}
189
190fn string_id_from_ranges<T: CharsetRange>(
191    ranges: &[T],
192    glyph_id: GlyphId,
193) -> Result<Sid, ReadError> {
194    let mut gid = glyph_id.to_u32();
195    // The notdef glyph isn't explicitly mapped so we need to special case
196    // it and add -1 and +1 at a few places when processing ranges
197    if gid == 0 {
198        return Ok(Sid::new(0));
199    }
200    gid -= 1;
201    let mut end = 0u32;
202    // Each range provides the string ids for `n_left + 1` glyphs with
203    // the sequence of string ids starting at `first`. Since the counts
204    // are cumulative, we must scan them all in order until we find
205    // the range that contains our requested glyph.
206    for range in ranges {
207        let next_end = range
208            .n_left()
209            .checked_add(1)
210            .and_then(|span| end.checked_add(span))
211            .ok_or(ReadError::OutOfBounds)?;
212        if gid < next_end {
213            return (gid - end)
214                .checked_add(range.first())
215                .and_then(|sid| sid.try_into().ok())
216                .ok_or(ReadError::OutOfBounds)
217                .map(Sid::new);
218        }
219        end = next_end;
220    }
221    Err(ReadError::OutOfBounds)
222}
223
224fn glyph_id_from_ranges<T: CharsetRange>(
225    ranges: &[T],
226    string_id: Sid,
227) -> Result<GlyphId, ReadError> {
228    let sid = string_id.to_u16() as u32;
229    // notdef glyph is not explicitly mapped
230    if sid == 0 {
231        return Ok(GlyphId::NOTDEF);
232    }
233    let mut gid = 1u32;
234    for range in ranges {
235        let first = range.first();
236        let n_left = range.n_left();
237        let last = first.checked_add(n_left).ok_or(ReadError::OutOfBounds)?;
238        if first <= sid && sid <= last {
239            gid = gid.checked_add(sid - first).ok_or(ReadError::OutOfBounds)?;
240            return Ok(GlyphId::new(gid));
241        }
242        gid = n_left
243            .checked_add(1)
244            .and_then(|span| gid.checked_add(span))
245            .ok_or(ReadError::OutOfBounds)?;
246    }
247    Err(ReadError::OutOfBounds)
248}
249
250/// Trait that unifies ranges for formats 1 and 2 so that we can implement
251/// the tricky search logic once.
252trait CharsetRange {
253    fn first(&self) -> u32;
254    fn n_left(&self) -> u32;
255}
256
257impl CharsetRange for Range1 {
258    fn first(&self) -> u32 {
259        self.first.get() as u32
260    }
261
262    fn n_left(&self) -> u32 {
263        self.n_left as u32
264    }
265}
266
267impl CharsetRange for Range2 {
268    fn first(&self) -> u32 {
269        self.first.get() as u32
270    }
271
272    fn n_left(&self) -> u32 {
273        self.n_left.get() as u32
274    }
275}
276
277/// Iterator over the glyph and string identifier mappings in a character set.
278#[derive(Clone)]
279pub struct Iter<'a>(IterKind<'a>);
280
281impl Iterator for Iter<'_> {
282    type Item = (GlyphId, Sid);
283
284    fn next(&mut self) -> Option<Self::Item> {
285        match &mut self.0 {
286            IterKind::Simple(charset, cur) => {
287                let gid = GlyphId::new(*cur);
288                let sid = charset.string_id(gid).ok()?;
289                *cur = cur.checked_add(1)?;
290                Some((gid, sid))
291            }
292            IterKind::Custom1(custom) => custom.next(),
293            IterKind::Custom2(custom) => custom.next(),
294        }
295    }
296}
297
298#[derive(Clone)]
299enum IterKind<'a> {
300    /// Predefined sets and custom format 0 are just array lookups so we use
301    /// the builtin mapping function.
302    Simple(Charset<'a>, u32),
303    Custom1(RangeIter<'a, Range1>),
304    Custom2(RangeIter<'a, Range2>),
305}
306
307/// Custom iterator for range based formats.
308///
309/// Each individual lookup requires a linear scan through the ranges so this
310/// provides a more efficient code path for iteration.
311#[derive(Clone)]
312struct RangeIter<'a, T> {
313    ranges: std::slice::Iter<'a, T>,
314    num_glyphs: u32,
315    gid: u32,
316    first: u32,
317    end: u32,
318    prev_end: u32,
319}
320
321impl<'a, T> RangeIter<'a, T>
322where
323    T: CharsetRange,
324{
325    fn new(ranges: &'a [T], num_glyphs: u32) -> Self {
326        let mut ranges = ranges.iter();
327        let (first, end) = next_range(&mut ranges).unwrap_or_default();
328        Self {
329            ranges,
330            num_glyphs,
331            gid: 0,
332            first,
333            end,
334            prev_end: 0,
335        }
336    }
337
338    fn next(&mut self) -> Option<(GlyphId, Sid)> {
339        if self.gid >= self.num_glyphs {
340            return None;
341        }
342        // The notdef glyph isn't explicitly mapped so we need to special case
343        // it and add -1 and +1 at a few places when processing ranges
344        if self.gid == 0 {
345            self.gid += 1;
346            return Some((GlyphId::new(0), Sid::new(0)));
347        }
348        let gid = self.gid - 1;
349        self.gid = self.gid.checked_add(1)?;
350        while gid >= self.end {
351            let (first, end) = next_range(&mut self.ranges)?;
352            self.prev_end = self.end;
353            self.first = first;
354            self.end = self.prev_end.checked_add(end)?;
355        }
356        let sid = self
357            .first
358            .checked_add(gid.checked_sub(self.prev_end)?)?
359            .try_into()
360            .ok()?;
361        Some((GlyphId::new(gid + 1), Sid::new(sid)))
362    }
363}
364
365fn next_range<T: CharsetRange>(ranges: &mut std::slice::Iter<T>) -> Option<(u32, u32)> {
366    ranges.next().and_then(|range| {
367        range
368            .n_left()
369            .checked_add(1)
370            .map(|span| (range.first(), span))
371    })
372}
373
374/// See "Expert" charset at <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf#page=47>
375#[rustfmt::skip]
376const EXPERT_CHARSET: &[u16] = &[
377      0,    1,  229,  230,  231,  232,  233,  234,  235,  236,  237,  238,   13,   14,   15,   99,
378    239,  240,  241,  242,  243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  252,
379    253,  254,  255,  256,  257,  258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,
380    267,  268,  269,  270,  271,  272,  273,  274,  275,  276,  277,  278,  279,  280,  281,  282,
381    283,  284,  285,  286,  287,  288,  289,  290,  291,  292,  293,  294,  295,  296,  297,  298,
382    299,  300,  301,  302,  303,  304,  305,  306,  307,  308,  309,  310,  311,  312,  313,  314,
383    315,  316,  317,  318,  158,  155,  163,  319,  320,  321,  322,  323,  324,  325,  326,  150,
384    164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,  340,
385    341,  342,  343,  344,  345,  346,  347,  348,  349,  350,  351,  352,  353,  354,  355,  356,
386    357,  358,  359,  360,  361,  362,  363,  364,  365,  366,  367,  368,  369,  370,  371,  372,
387    373,  374,  375,  376,  377,  378,
388];
389
390/// See "Expert Subset" charset at <https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf#page=49>
391#[rustfmt::skip]
392const EXPERT_SUBSET_CHARSET: &[u16] = &[
393      0,    1,  231,  232,  235,  236,  237,  238,   13,   14,   15,   99,  239,  240,  241,  242,
394    243,  244,  245,  246,  247,  248,   27,   28,  249,  250,  251,  253,  254,  255,  256,  257,
395    258,  259,  260,  261,  262,  263,  264,  265,  266,  109,  110,  267,  268,  269,  270,  272,
396    300,  301,  302,  305,  314,  315,  158,  155,  163,  320,  321,  322,  323,  324,  325,  326,
397    150,  164,  169,  327,  328,  329,  330,  331,  332,  333,  334,  335,  336,  337,  338,  339,
398    340,  341,  342,  343,  344,  345,  346
399];
400
401#[cfg(test)]
402mod tests {
403    use super::*;
404    use font_test_data::bebuffer::BeBuffer;
405
406    #[derive(Clone, Copy)]
407    struct TestRange {
408        first: u32,
409        n_left: u32,
410    }
411
412    impl CharsetRange for TestRange {
413        fn first(&self) -> u32 {
414            self.first
415        }
416
417        fn n_left(&self) -> u32 {
418            self.n_left
419        }
420    }
421
422    #[test]
423    fn iso_adobe_charset() {
424        // Offset of 0 signifies the ISOAdobe charset
425        let charset_offset = 0;
426        let num_glyphs = 64;
427        // This is an identity mapping
428        let expected = |gid: GlyphId| Some(gid.to_u32());
429        test_simple_mapping(charset_offset, num_glyphs, expected);
430    }
431
432    #[test]
433    fn expert_charset() {
434        // Offset 1 signifies the expert charset
435        let charset_offset = 1;
436        let num_glyphs = 64;
437        // This is an array based mapping
438        let expected = |gid: GlyphId| {
439            EXPERT_CHARSET
440                .get(gid.to_u32() as usize)
441                .map(|id| *id as u32)
442        };
443        test_simple_mapping(charset_offset, num_glyphs, expected);
444    }
445
446    #[test]
447    fn expert_subset_charset() {
448        // Offset 2 signifies the expert subset charset
449        let charset_offset = 2;
450        let num_glyphs = 64;
451        // This is an array based mapping
452        let expected = |gid: GlyphId| {
453            EXPERT_SUBSET_CHARSET
454                .get(gid.to_u32() as usize)
455                .map(|id| *id as u32)
456        };
457        test_simple_mapping(charset_offset, num_glyphs, expected);
458    }
459
460    // Common test setup for identity or array based charset mappings
461    fn test_simple_mapping(
462        charset_offset: usize,
463        num_glyphs: u32,
464        expected: impl Fn(GlyphId) -> Option<u32>,
465    ) {
466        let charset = Charset::new(FontData::new(&[]), charset_offset, num_glyphs).unwrap();
467        for gid in 0..num_glyphs {
468            let gid = GlyphId::new(gid);
469            let sid = expected(gid).unwrap();
470            assert_eq!(charset.string_id(gid).unwrap().to_u16() as u32, sid);
471            assert_eq!(charset.glyph_id(Sid::new(sid as _)).unwrap(), gid);
472        }
473        // Don't map glyphs beyond num_glyphs
474        for gid in num_glyphs..u16::MAX as u32 {
475            assert_eq!(charset.string_id(GlyphId::new(gid)).ok(), None);
476        }
477    }
478
479    #[test]
480    fn custom_mapping_format0() {
481        let mut buf = BeBuffer::new();
482        let num_glyphs = 6;
483        // Add some padding so we can generate an offset greater than 2
484        buf = buf.extend([0u8; 4]);
485        // format 0
486        buf = buf.push(0u8);
487        // glyph array: each sid is gid * 2
488        buf = buf.extend([2u16, 4, 6, 8, 10]);
489        let charset = Charset::new(FontData::new(buf.data()), 4, num_glyphs).unwrap();
490        // Test lookup code path
491        for gid in 0..num_glyphs {
492            assert_eq!(
493                charset.string_id(GlyphId::new(gid)).unwrap().to_u16() as u32,
494                gid * 2
495            )
496        }
497        // Test iterator code path
498        for (gid, sid) in charset.iter() {
499            assert_eq!(sid.to_u16() as u32, gid.to_u32() * 2);
500        }
501        assert_eq!(charset.iter().count() as u32, num_glyphs);
502        // Test out of bounds glyphs
503        for gid in num_glyphs..u16::MAX as u32 {
504            assert_eq!(charset.string_id(GlyphId::new(gid)).ok(), None);
505        }
506    }
507
508    #[test]
509    fn custom_mapping_format1() {
510        let mut buf = BeBuffer::new();
511        let num_glyphs = 7;
512        // Add some padding so we can generate an offset greater than 2
513        buf = buf.extend([0u8; 4]);
514        // format 1
515        buf = buf.push(1u8);
516        // Three disjoint range mappings
517        buf = buf.push(8u16).push(2u8);
518        buf = buf.push(1200u16).push(0u8);
519        buf = buf.push(20u16).push(1u8);
520        let expected_sids = [0, 8, 9, 10, 1200, 20, 21];
521        test_range_mapping(buf.data(), num_glyphs, &expected_sids);
522    }
523
524    #[test]
525    fn custom_mapping_format2() {
526        let mut buf = BeBuffer::new();
527        // Add some padding so we can generate an offset greater than 2
528        buf = buf.extend([0u8; 4]);
529        // format 2
530        buf = buf.push(2u8);
531        // Three disjoint range mappings
532        buf = buf.push(8u16).push(2u16);
533        buf = buf.push(1200u16).push(0u16);
534        buf = buf.push(20u16).push(800u16);
535        let mut expected_sids = vec![0, 8, 9, 10, 1200];
536        for i in 0..=800 {
537            expected_sids.push(i + 20);
538        }
539        let num_glyphs = expected_sids.len() as u32;
540        test_range_mapping(buf.data(), num_glyphs, &expected_sids);
541    }
542
543    // Common code for testing range based mappings
544    fn test_range_mapping(data: &[u8], num_glyphs: u32, expected_sids: &[u32]) {
545        let charset = Charset::new(FontData::new(data), 4, num_glyphs).unwrap();
546        // Test lookup code path
547        for (gid, sid) in expected_sids.iter().enumerate() {
548            assert_eq!(
549                charset.string_id(GlyphId::new(gid as _)).unwrap().to_u16() as u32,
550                *sid
551            )
552        }
553        // Test iterator code path
554        assert!(charset.iter().eq(expected_sids
555            .iter()
556            .enumerate()
557            .map(|(gid, sid)| (GlyphId::new(gid as u32), Sid::new(*sid as u16)))));
558        assert_eq!(charset.iter().count() as u32, num_glyphs);
559        // Test out of bounds glyphs
560        for gid in num_glyphs..u16::MAX as u32 {
561            assert_eq!(charset.string_id(GlyphId::new(gid)).ok(), None);
562        }
563        // Test reverse mapping
564        for (gid, sid) in expected_sids.iter().enumerate() {
565            assert_eq!(
566                charset.glyph_id(Sid::new(*sid as u16)),
567                Ok(GlyphId::new(gid as u32))
568            );
569        }
570    }
571
572    #[test]
573    fn string_id_from_ranges_overflow_does_not_panic() {
574        // Don't panic on overflow!
575        let ranges = [TestRange {
576            first: 42,
577            n_left: u32::MAX,
578        }];
579        assert_eq!(
580            string_id_from_ranges(&ranges, GlyphId::new(1)),
581            Err(ReadError::OutOfBounds)
582        );
583    }
584
585    #[test]
586    fn glyph_id_from_ranges_overflow_does_not_panic() {
587        // Don't panic on overflow!
588        let ranges = [
589            TestRange {
590                first: 2,
591                n_left: 0,
592            },
593            TestRange {
594                first: 2,
595                n_left: u32::MAX - 2,
596            },
597        ];
598        assert_eq!(
599            glyph_id_from_ranges(&ranges, Sid::new(1)),
600            Err(ReadError::OutOfBounds)
601        );
602    }
603
604    #[test]
605    fn next_range_overflow_does_not_panic() {
606        // Don't panic on overflow!
607        let ranges = [TestRange {
608            first: 7,
609            n_left: u32::MAX,
610        }];
611        let mut iter = ranges.iter();
612        assert_eq!(next_range(&mut iter), None);
613    }
614}