Skip to main content

cff_parser/
lib.rs

1// Forked from https://github.com/jrmuizel/cff-parser (MIT OR Apache-2.0).
2// Pre-existing upstream lint issues are suppressed here to avoid diff noise.
3#![allow(
4    unused_imports,
5    unused_variables,
6    clippy::get_first,
7    clippy::clone_on_copy,
8    clippy::identity_op,
9    clippy::manual_is_multiple_of,
10    clippy::field_reassign_with_default,
11    clippy::needless_borrow,
12    clippy::needless_range_loop,
13    clippy::manual_range_contains,
14    clippy::upper_case_acronyms,
15    clippy::expl_impl_clone_on_copy,
16    clippy::unnecessary_cast,
17    clippy::derivable_impls,
18    mismatched_lifetime_syntaxes
19)]
20
21mod argstack;
22mod cff;
23pub mod charset;
24mod charstring;
25mod dict;
26mod encoding;
27mod index;
28mod parser;
29mod std_names;
30
31pub use cff::{string_by_id, Table};
32pub use encoding::{Encoding, EncodingKind, Format1Range, STANDARD_ENCODING};
33use parser::{FromData, Stream, TryNumFrom};
34pub use std_names::STANDARD_NAMES;
35
36/// A type-safe wrapper for string ID.
37#[derive(Clone, Copy, PartialEq, PartialOrd, Debug)]
38pub struct StringId(pub u16);
39
40impl FromData for StringId {
41    const SIZE: usize = 2;
42
43    #[inline]
44    fn parse(data: &[u8]) -> Option<Self> {
45        u16::parse(data).map(StringId)
46    }
47}
48
49trait IsEven {
50    fn is_odd(&self) -> bool;
51}
52impl IsEven for usize {
53    fn is_odd(&self) -> bool {
54        self & 1 == 1
55    }
56}
57
58/// A list of errors that can occur during a CFF table parsing.
59#[derive(Clone, Copy, PartialEq, Debug)]
60pub enum CFFError {
61    NoGlyph,
62    ReadOutOfBounds,
63    ZeroBBox,
64    InvalidOperator,
65    UnsupportedOperator,
66    MissingEndChar,
67    DataAfterEndChar,
68    NestingLimitReached,
69    ArgumentsStackLimitReached,
70    InvalidArgumentsStackLength,
71    BboxOverflow,
72    MissingMoveTo,
73    InvalidSubroutineIndex,
74    NoLocalSubroutines,
75    InvalidSeacCode,
76}
77
78#[inline]
79pub fn f64_abs(n: f64) -> f64 {
80    n.abs()
81}
82
83#[inline]
84pub fn conv_subroutine_index(index: f64, bias: u16) -> Result<u32, CFFError> {
85    conv_subroutine_index_impl(index, bias).ok_or(CFFError::InvalidSubroutineIndex)
86}
87
88#[inline]
89fn conv_subroutine_index_impl(index: f64, bias: u16) -> Option<u32> {
90    let index = i32::try_num_from(index)?;
91    let bias = i32::from(bias);
92
93    let index = index.checked_add(bias)?;
94    u32::try_from(index).ok()
95}
96
97// Adobe Technical Note #5176, Chapter 16 "Local / Global Subrs INDEXes"
98#[inline]
99pub fn calc_subroutine_bias(len: u32) -> u16 {
100    if len < 1240 {
101        107
102    } else if len < 33900 {
103        1131
104    } else {
105        32768
106    }
107}
108
109/// A trait for glyph outline construction.
110pub trait OutlineBuilder {
111    /// Appends a MoveTo segment.
112    ///
113    /// Start of a contour.
114    fn move_to(&mut self, x: f32, y: f32);
115
116    /// Appends a LineTo segment.
117    fn line_to(&mut self, x: f32, y: f32);
118
119    /// Appends a QuadTo segment.
120    fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32);
121
122    /// Appends a CurveTo segment.
123    fn curve_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, x: f32, y: f32);
124
125    /// Appends a ClosePath segment.
126    ///
127    /// End of a contour.
128    fn close(&mut self);
129}
130
131struct DummyOutline;
132impl OutlineBuilder for DummyOutline {
133    fn move_to(&mut self, _: f32, _: f32) {}
134    fn line_to(&mut self, _: f32, _: f32) {}
135    fn quad_to(&mut self, _: f32, _: f32, _: f32, _: f32) {}
136    fn curve_to(&mut self, _: f32, _: f32, _: f32, _: f32, _: f32, _: f32) {}
137    fn close(&mut self) {}
138}
139
140pub(crate) struct Builder<'a> {
141    builder: &'a mut dyn OutlineBuilder,
142    bbox: RectF,
143}
144
145impl<'a> Builder<'a> {
146    #[inline]
147    fn move_to(&mut self, x: f64, y: f64) {
148        let x = x as f32;
149        let y = y as f32;
150        self.bbox.extend_by(x, y);
151        self.builder.move_to(x, y);
152    }
153
154    #[inline]
155    fn line_to(&mut self, x: f64, y: f64) {
156        let x = x as f32;
157        let y = y as f32;
158        self.bbox.extend_by(x, y);
159        self.builder.line_to(x, y);
160    }
161
162    #[inline]
163    fn curve_to(&mut self, x1: f64, y1: f64, x2: f64, y2: f64, x: f64, y: f64) {
164        let x1 = x1 as f32;
165        let y1 = y1 as f32;
166        let x2 = x2 as f32;
167        let y2 = y2 as f32;
168        let x = x as f32;
169        let y = y as f32;
170        self.bbox.extend_by(x1, y1);
171        self.bbox.extend_by(x2, y2);
172        self.bbox.extend_by(x, y);
173        self.builder.curve_to(x1, y1, x2, y2, x, y);
174    }
175
176    #[inline]
177    fn close(&mut self) {
178        self.builder.close();
179    }
180}
181/// A rectangle.
182///
183/// Doesn't guarantee that `x_min` <= `x_max` and/or `y_min` <= `y_max`.
184#[repr(C)]
185#[allow(missing_docs)]
186#[derive(Clone, Copy, PartialEq, Eq, Debug)]
187pub struct Rect {
188    pub x_min: i16,
189    pub y_min: i16,
190    pub x_max: i16,
191    pub y_max: i16,
192}
193
194impl Rect {
195    #[inline]
196    fn zero() -> Self {
197        Self {
198            x_min: 0,
199            y_min: 0,
200            x_max: 0,
201            y_max: 0,
202        }
203    }
204
205    /// Returns rect's width.
206    #[inline]
207    pub fn width(&self) -> i16 {
208        self.x_max - self.x_min
209    }
210
211    /// Returns rect's height.
212    #[inline]
213    pub fn height(&self) -> i16 {
214        self.y_max - self.y_min
215    }
216}
217
218/// A rectangle described by the left-lower and upper-right points.
219#[derive(Clone, Copy, Debug, PartialEq)]
220pub struct RectF {
221    /// The horizontal minimum of the rect.
222    pub x_min: f32,
223    /// The vertical minimum of the rect.
224    pub y_min: f32,
225    /// The horizontal maximum of the rect.
226    pub x_max: f32,
227    /// The vertical maximum of the rect.
228    pub y_max: f32,
229}
230
231impl RectF {
232    #[inline]
233    fn new() -> Self {
234        RectF {
235            x_min: f32::MAX,
236            y_min: f32::MAX,
237            x_max: f32::MIN,
238            y_max: f32::MIN,
239        }
240    }
241
242    #[inline]
243    fn is_default(&self) -> bool {
244        self.x_min == f32::MAX
245            && self.y_min == f32::MAX
246            && self.x_max == f32::MIN
247            && self.y_max == f32::MIN
248    }
249
250    #[inline]
251    fn extend_by(&mut self, x: f32, y: f32) {
252        self.x_min = self.x_min.min(x);
253        self.y_min = self.y_min.min(y);
254        self.x_max = self.x_max.max(x);
255        self.y_max = self.y_max.max(y);
256    }
257
258    #[inline]
259    fn to_rect(self) -> Option<Rect> {
260        Some(Rect {
261            x_min: i16::try_num_from(self.x_min)?,
262            y_min: i16::try_num_from(self.y_min)?,
263            x_max: i16::try_num_from(self.x_max)?,
264            y_max: i16::try_num_from(self.y_max)?,
265        })
266    }
267}
268
269/// A type-safe wrapper for glyph ID.
270#[repr(transparent)]
271#[derive(Clone, Copy, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
272pub struct GlyphId(pub u16);
273
274impl FromData for GlyphId {
275    const SIZE: usize = 2;
276
277    #[inline]
278    fn parse(data: &[u8]) -> Option<Self> {
279        u16::parse(data).map(GlyphId)
280    }
281}