1 //! The [COLR](https://docs.microsoft.com/en-us/typography/opentype/spec/colr) table 2 3 use super::variations::{DeltaSetIndexMap, ItemVariationStore}; 4 5 include!("../../generated/generated_colr.rs"); 6 7 /// Unique paint identifier used for detecting cycles in the paint graph. 8 pub type PaintId = usize; 9 10 impl<'a> Colr<'a> { 11 /// Returns the COLRv0 base glyph for the given glyph identifier. 12 /// 13 /// The return value is a range of layer indices that can be passed to 14 /// [`v0_layer`](Self::v0_layer) to retrieve the layer glyph identifiers 15 /// and palette color indices. v0_base_glyph(&self, glyph_id: GlyphId) -> Result<Option<Range<usize>>, ReadError>16 pub fn v0_base_glyph(&self, glyph_id: GlyphId) -> Result<Option<Range<usize>>, ReadError> { 17 let records = self.base_glyph_records().ok_or(ReadError::NullOffset)??; 18 let record = match records.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id)) { 19 Ok(ix) => &records[ix], 20 _ => return Ok(None), 21 }; 22 let start = record.first_layer_index() as usize; 23 let end = start + record.num_layers() as usize; 24 Ok(Some(start..end)) 25 } 26 27 /// Returns the COLRv0 layer at the given index. 28 /// 29 /// The layer is represented by a tuple containing the glyph identifier of 30 /// the associated outline and the palette color index. v0_layer(&self, index: usize) -> Result<(GlyphId, u16), ReadError>31 pub fn v0_layer(&self, index: usize) -> Result<(GlyphId, u16), ReadError> { 32 let layers = self.layer_records().ok_or(ReadError::NullOffset)??; 33 let layer = layers.get(index).ok_or(ReadError::OutOfBounds)?; 34 Ok((layer.glyph_id(), layer.palette_index())) 35 } 36 37 /// Returns the COLRv1 base glyph for the given glyph identifier. 38 /// 39 /// The second value in the tuple is a unique identifier for the paint that 40 /// may be used to detect recursion in the paint graph. v1_base_glyph( &self, glyph_id: GlyphId, ) -> Result<Option<(Paint<'a>, PaintId)>, ReadError>41 pub fn v1_base_glyph( 42 &self, 43 glyph_id: GlyphId, 44 ) -> Result<Option<(Paint<'a>, PaintId)>, ReadError> { 45 let list = self.base_glyph_list().ok_or(ReadError::NullOffset)??; 46 let records = list.base_glyph_paint_records(); 47 let record = match records.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id)) { 48 Ok(ix) => &records[ix], 49 _ => return Ok(None), 50 }; 51 let offset_data = list.offset_data(); 52 // Use the address of the paint as an identifier for the recursion 53 // blacklist. 54 let id = record.paint_offset().to_u32() as usize + offset_data.as_ref().as_ptr() as usize; 55 Ok(Some((record.paint(offset_data)?, id))) 56 } 57 58 /// Returns the COLRv1 layer at the given index. 59 /// 60 /// The second value in the tuple is a unique identifier for the paint that 61 /// may be used to detect recursion in the paint graph. v1_layer(&self, index: usize) -> Result<(Paint<'a>, PaintId), ReadError>62 pub fn v1_layer(&self, index: usize) -> Result<(Paint<'a>, PaintId), ReadError> { 63 let list = self.layer_list().ok_or(ReadError::NullOffset)??; 64 let offset = list 65 .paint_offsets() 66 .get(index) 67 .ok_or(ReadError::OutOfBounds)? 68 .get(); 69 let offset_data = list.offset_data(); 70 // Use the address of the paint as an identifier for the recursion 71 // blacklist. 72 let id = offset.to_u32() as usize + offset_data.as_ref().as_ptr() as usize; 73 Ok((offset.resolve(offset_data)?, id)) 74 } 75 76 /// Returns the COLRv1 clip box for the given glyph identifier. v1_clip_box(&self, glyph_id: GlyphId) -> Result<Option<ClipBox<'a>>, ReadError>77 pub fn v1_clip_box(&self, glyph_id: GlyphId) -> Result<Option<ClipBox<'a>>, ReadError> { 78 use std::cmp::Ordering; 79 let list = self.clip_list().ok_or(ReadError::NullOffset)??; 80 let clips = list.clips(); 81 let clip = match clips.binary_search_by(|clip| { 82 if glyph_id < clip.start_glyph_id() { 83 Ordering::Greater 84 } else if glyph_id > clip.end_glyph_id() { 85 Ordering::Less 86 } else { 87 Ordering::Equal 88 } 89 }) { 90 Ok(ix) => &clips[ix], 91 _ => return Ok(None), 92 }; 93 Ok(Some(clip.clip_box(list.offset_data())?)) 94 } 95 } 96