#[cfg(feature = "std")]
mod closure;
use super::variations::{DeltaSetIndexMap, ItemVariationStore};
include!("../../generated/generated_colr.rs");
pub type PaintId = usize;
impl<'a> Colr<'a> {
pub fn v0_base_glyph(&self, glyph_id: GlyphId) -> Option<Range<usize>> {
let records = self.base_glyph_records()?.ok()?;
let glyph_id = glyph_id.try_into().ok()?;
let ix = records
.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id))
.ok()?;
let record = records.get(ix)?;
let start = record.first_layer_index() as usize;
Some(start..start.checked_add(record.num_layers() as usize)?)
}
pub fn v0_layer(&self, index: usize) -> Option<(GlyphId16, u16)> {
let layer = self.layer_records()?.ok()?.get(index)?;
Some((layer.glyph_id(), layer.palette_index()))
}
pub fn v1_base_glyph(&self, glyph_id: GlyphId) -> Option<(Paint<'a>, PaintId)> {
let glyph_id = glyph_id.try_into().ok()?;
let list = self.base_glyph_list()?.ok()?;
let records = list.base_glyph_paint_records();
let ix = records
.binary_search_by(|rec| rec.glyph_id().cmp(&glyph_id))
.ok()?;
let record = records.get(ix)?;
let offset_data = list.offset_data();
let id = record.paint_offset().to_u32() as usize + offset_data.as_ref().as_ptr() as usize;
Some((record.paint(offset_data).ok()?, id))
}
pub fn v1_layer(&self, index: usize) -> Option<(Paint<'a>, PaintId)> {
let list = self.layer_list()?.ok()?;
let offset = list.paint_offsets().get(index)?.get();
let offset_data = list.offset_data();
let id = offset.to_u32() as usize + offset_data.as_ref().as_ptr() as usize;
Some((offset.resolve(offset_data).ok()?, id))
}
pub fn v1_clip_box(&self, glyph_id: GlyphId) -> Option<ClipBox<'a>> {
use core::cmp::Ordering;
let glyph_id: GlyphId16 = glyph_id.try_into().ok()?;
let list = self.clip_list()?.ok()?;
let clips = list.clips();
let ix = clips
.binary_search_by(|clip| {
if glyph_id < clip.start_glyph_id() {
Ordering::Greater
} else if glyph_id > clip.end_glyph_id() {
Ordering::Less
} else {
Ordering::Equal
}
})
.ok()?;
let clip = clips.get(ix)?;
clip.clip_box(list.offset_data()).ok()
}
}
#[cfg(test)]
mod tests {
use super::*;
use font_test_data::bebuffer::BeBuffer;
#[test]
fn paint_glyph2_reads_full_glyph_id() {
for gid in [0, 0xffff, 0x10000, 0x123456, 0xffffff] {
let data = BeBuffer::new()
.push(33u8)
.push(Offset24::new(Uint24::new(7)))
.push(GlyphId24::new(gid))
.extend([2u8, 0, 7, 0x40, 0]);
let Paint::Glyph2(paint) = Paint::read(FontData::new(&data)).unwrap() else {
panic!("expected PaintGlyph2");
};
assert_eq!(paint.glyph_id().to_u32(), gid);
assert_eq!(paint.paint_offset().to_u32(), 7);
let Paint::Solid(solid) = paint.paint().unwrap() else {
panic!("expected PaintSolid");
};
assert_eq!(solid.palette_index(), 7);
assert_eq!(solid.alpha(), F2Dot14::ONE);
}
}
#[test]
fn paint_glyph2_rejects_truncated_header_and_child() {
let data = font_test_data::colr::PAINT_GLYPH2;
for len in 0..7 {
assert!(Paint::read(FontData::new(&data[..len])).is_err());
}
for len in 7..data.len() {
let Paint::Glyph2(paint) = Paint::read(FontData::new(&data[..len])).unwrap() else {
panic!("expected PaintGlyph2");
};
assert!(paint.paint().is_err());
}
}
}