pinot/colr/
mod.rs

1//! Color table.
2
3mod paint;
4
5use super::parse_prelude::*;
6use super::var::item::*;
7use core::ops::Range;
8
9pub use paint::*;
10
11/// Tag for the `COLR` table.
12pub const COLR: Tag = Tag::new(b"COLR");
13
14/// Color table.
15///
16/// <https://docs.microsoft.com/en-us/typography/opentype/spec/colr>
17#[derive(Copy, Clone)]
18pub struct Colr<'a> {
19    data: Buffer<'a>,
20    version: u16,
21}
22
23impl<'a> Colr<'a> {
24    /// Creates a new color table from a byte slice containing the
25    /// table data.
26    pub fn new(data: &'a [u8]) -> Self {
27        let data = Buffer::new(data);
28        let version = data.read_or_default(0);
29        Self { data, version }
30    }
31
32    /// Returns the version of the table.
33    pub fn version(&self) -> u16 {
34        self.version
35    }
36
37    /// Returns the number of glyphs in the table.
38    pub fn num_glyphs(&self) -> u16 {
39        self.data.read_u16(2).unwrap_or_default()
40    }
41
42    /// Returns the glyph at the specified index.
43    pub fn glyph(&self, index: u16) -> Option<Glyph<'a>> {
44        if index >= self.num_glyphs() {
45            return None;
46        }
47        let d = &self.data;
48        let offset = d.read_u32(4)? as usize + index as usize * 6;
49        let gid = d.read_u16(offset)?;
50        let first = d.read_u16(offset + 2)? as usize;
51        let offset = d.read_u32(8)? as usize + first * 4;
52        let len = d.read_u16(offset + 4)? as usize;
53        let layers = d.read_slice(offset, len)?;
54        Some(Glyph { gid, layers })
55    }
56
57    /// Returns the glyph for the specified identifier.
58    pub fn find_glyph(&self, gid: GlyphId) -> Option<Glyph<'a>> {
59        let d = &self.data;
60        let base_offset = d.read_u32(4)? as usize;
61        let mut lo = 0;
62        let mut hi = self.num_glyphs() as usize;
63        while lo < hi {
64            use core::cmp::Ordering::*;
65            let i = (lo + hi) / 2;
66            let offset = base_offset + i * 6;
67            let id = d.read_u16(offset)?;
68            match gid.cmp(&id) {
69                Less => hi = i,
70                Greater => lo = i + 1,
71                Equal => {
72                    let first = d.read_u16(offset + 2)? as usize;
73                    let offset = d.read_u32(8)? as usize + first * 4;
74                    let len = d.read_u16(offset + 4)? as usize;
75                    let layers = d.read_slice(offset, len)?;
76                    return Some(Glyph { gid, layers });
77                }
78            }
79        }
80        None
81    }
82
83    /// Returns an iterator over the glyphs in the table.
84    pub fn glyphs(&self) -> impl Iterator<Item = Glyph<'a>> + 'a + Clone {
85        let copy = *self;
86        (0..self.num_glyphs()).filter_map(move |i| copy.glyph(i))
87    }
88
89    /// Returns the number of base paints.
90    pub fn num_base_paints(&self) -> u32 {
91        if self.version < 1 {
92            return 0;
93        }
94        let base = self.data.read_u32(14).unwrap_or_default() as usize;
95        if base == 0 {
96            return 0;
97        }
98        self.data.read_u32(base).unwrap_or_default()
99    }
100
101    /// Returns the glyph identifier and base paint at the specified index.
102    pub fn base_paint(&self, index: u32) -> Option<(GlyphId, Paint<'a>)> {
103        if self.version < 1 {
104            return None;
105        }
106        let index = index as usize;
107        let base = self.data.read_u32(14)? as usize;
108        let len = self.data.read_u32(base)? as usize;
109        if index >= len {
110            return None;
111        }
112        let record_base = base + 4 + index * 6;
113        let id = self.data.read_u16(record_base)?;
114        let paint_offset = base + self.data.read_u32(record_base + 2)? as usize;
115        Some((id, PaintRef::new(self.data, paint_offset as u32)?.get()?))
116    }
117
118    /// Returns the base paint for the specified glyph identifier.
119    pub fn find_base_paint(&self, gid: GlyphId) -> Option<Paint<'a>> {
120        if self.version < 1 {
121            return None;
122        }
123        let base = self.data.read_u32(14)? as usize;
124        let len = self.data.read_u32(base)? as usize;
125        let mut lo = 0;
126        let mut hi = len;
127        while lo < hi {
128            use core::cmp::Ordering::*;
129            let i = (lo + hi) / 2;
130            let record_base = base + 4 + i * 6;
131            let id = self.data.read_u16(record_base)?;
132            match gid.cmp(&id) {
133                Less => hi = i,
134                Greater => lo = i + 1,
135                Equal => {
136                    let paint_offset = base + self.data.read_u32(record_base + 2)? as usize;
137                    return PaintRef::new(self.data, paint_offset as u32)?.get();
138                }
139            }
140        }
141        None
142    }
143
144    /// Returns an iterator over the collection of base paints in the table.
145    pub fn base_paints(&self) -> impl Iterator<Item = (GlyphId, Paint<'a>)> + 'a + Clone {
146        let copy = *self;
147        (0..self.num_base_paints()).filter_map(move |i| copy.base_paint(i))
148    }
149
150    /// Returns the number of paint layers in the table.
151    pub fn num_paint_layers(&self) -> u32 {
152        if self.version < 1 {
153            return 0;
154        }
155        let base = self.data.read_u32(18).unwrap_or_default() as usize;
156        if base == 0 {
157            return 0;
158        }
159        self.data.read_u32(base).unwrap_or_default()
160    }
161
162    /// Returns the paint layer at the specified index.
163    pub fn paint_layer(&self, index: u32) -> Option<Paint<'a>> {
164        if self.version < 1 {
165            return None;
166        }
167        let index = index as usize;
168        let base = self.data.read_u32(18)? as usize;
169        let len = self.data.read_u32(base)? as usize;
170        if index >= len {
171            return None;
172        }
173        let record_base = base + 4 + index * 4;
174        let paint_offset = base + self.data.read_u32(record_base)? as usize;
175        PaintRef::new(self.data, paint_offset as u32)?.get()
176    }
177
178    /// Returns an iterator over the paint layers in the table.
179    pub fn paint_layers(&self) -> impl Iterator<Item = Paint<'a>> + 'a + Clone {
180        let copy = *self;
181        (0..self.num_paint_layers()).filter_map(move |i| copy.paint_layer(i))
182    }
183
184    /// Returns the number of clip boxes in the table.
185    pub fn num_clip_boxes(&self) -> u32 {
186        if self.version < 1 {
187            return 0;
188        }
189        let base = self.data.read_u32(22).unwrap_or_default() as usize;
190        if base == 0 {
191            return 0;
192        }
193        self.data.read_u32(base + 1).unwrap_or_default()
194    }
195
196    /// Returns the glyph identifier range and clip box for the specified
197    /// index.
198    pub fn clip_box(&self, index: u32) -> Option<(Range<GlyphId>, ClipBox)> {
199        if self.version < 1 {
200            return None;
201        }
202        let d = &self.data;
203        let base = d.read_u32(22)? as usize;
204        if base == 0 {
205            return None;
206        }
207        let len = d.read_u32(base + 1)? as usize;
208        let index = index as usize;
209        if index >= len {
210            return None;
211        }
212        let record_base = base + 5 + index * 7;
213        let start = d.read_u16(record_base)?;
214        let end = d.read_u16(record_base + 2)? + 1;
215        let offset = d.read_u24(record_base + 4)?;
216        if offset == 0 {
217            return None;
218        }
219        let clip_base = record_base + offset as usize;
220        let format = d.read_u8(clip_base)?;
221        let x_min = f2dot14_to_f32(d.read_i16(clip_base + 1)?);
222        let y_min = f2dot14_to_f32(d.read_i16(clip_base + 5)?);
223        let x_max = f2dot14_to_f32(d.read_i16(clip_base + 9)?);
224        let y_max = f2dot14_to_f32(d.read_i16(clip_base + 13)?);
225        let var_index = if format == 2 {
226            Some(d.read_u32(clip_base + 17)?)
227        } else {
228            None
229        };
230        Some((
231            start..end,
232            ClipBox {
233                x_min,
234                y_min,
235                x_max,
236                y_max,
237                var_index,
238            },
239        ))
240    }
241
242    /// Returns the clip box for the specified glyph identifier.
243    pub fn find_clip_box(&self, gid: GlyphId) -> Option<ClipBox> {
244        if self.version < 1 {
245            return None;
246        }
247        let d = &self.data;
248        let base = d.read_u32(22)? as usize;
249        if base == 0 {
250            return None;
251        }
252        let len = d.read_u32(base + 1)? as usize;
253        let mut lo = 0;
254        let mut hi = len;
255        while lo < hi {
256            let i = (lo + hi) / 2;
257            let record_base = base + 5 + i * 7;
258            let start = d.read_u16(record_base)?;
259            if gid < start {
260                lo = i + 1;
261            } else if gid > d.read_u16(record_base + 2)? {
262                hi = i;
263            } else {
264                let offset = d.read_u24(record_base + 4)?;
265                if offset == 0 {
266                    return None;
267                }
268                let clip_base = record_base + offset as usize;
269                let format = d.read_u8(clip_base)?;
270                let x_min = f2dot14_to_f32(d.read_i16(clip_base + 1)?);
271                let y_min = f2dot14_to_f32(d.read_i16(clip_base + 5)?);
272                let x_max = f2dot14_to_f32(d.read_i16(clip_base + 9)?);
273                let y_max = f2dot14_to_f32(d.read_i16(clip_base + 13)?);
274                let var_index = if format == 2 {
275                    Some(d.read_u32(clip_base + 17)?)
276                } else {
277                    None
278                };
279                return Some(ClipBox {
280                    x_min,
281                    y_min,
282                    x_max,
283                    y_max,
284                    var_index,
285                });
286            }
287        }
288        None
289    }
290
291    /// Returns an iterator over the clip boxes in the table.
292    pub fn clip_boxes(&self) -> impl Iterator<Item = (Range<GlyphId>, ClipBox)> + 'a + Clone {
293        let copy = *self;
294        (0..self.num_clip_boxes()).filter_map(move |i| copy.clip_box(i))
295    }
296
297    /// Returns the mapping for variation indices.
298    pub fn var_mapping(&self) -> Option<DeltaSetIndexMap<'a>> {
299        if self.version < 1 {
300            return None;
301        }
302        DeltaSetIndexMap::new(self.data, self.data.read_offset32(26, 0)?)
303    }
304
305    /// Returns the item variation store.
306    pub fn ivs(&self) -> Option<ItemVariationStore<'a>> {
307        if self.version < 1 {
308            return None;
309        }
310        ItemVariationStore::new(self.data, self.data.read_offset32(30, 0)?)
311    }
312}
313
314/// Sequence of layers that define a color outline.
315#[derive(Copy, Clone)]
316pub struct Glyph<'a> {
317    /// Glyph identifier for the outline.
318    pub gid: GlyphId,
319    /// Color layers that define the outline.
320    pub layers: Slice<'a, Layer>,
321}
322
323/// Single layer in a color outline.
324#[derive(Copy, Clone)]
325pub struct Layer {
326    /// Glyph that contains the outline for the layer.
327    pub gid: GlyphId,
328    /// Index value to use with the selected color palette.
329    pub palette_index: Option<u16>,
330}
331
332impl ReadData for Layer {
333    const SIZE: usize = 4;
334
335    unsafe fn read_data_unchecked(buf: &[u8], offset: usize) -> Self {
336        let gid = u16::read_data_unchecked(buf, offset);
337        let index = u16::read_data_unchecked(buf, offset + 2);
338        Self {
339            gid,
340            palette_index: if index == 0xFFFF { None } else { Some(index) },
341        }
342    }
343}
344
345fn fixed_to_f32(x: i32) -> f32 {
346    const SCALE: f32 = 1. / 65536.;
347    x as f32 * SCALE
348}
349
350fn f2dot14_to_f32(x: i16) -> f32 {
351    const SCALE: f32 = 1. / 65536.;
352    (x as i32 * 4) as f32 * SCALE
353}