#![deny(missing_docs)]
use super::{
BitDepth, Bitmap, BitmapGlyph, EncapsulatedBitmap, EncapsulatedFormat, Metrics, OriginOffset,
};
use crate::binary::read::{ReadArray, ReadBinaryDep, ReadCtxt, ReadScope};
use crate::binary::U32Be;
use crate::error::ParseError;
use crate::tag;
use crate::SafeFrom;
pub struct Sbix<'a> {
pub flags: u16,
pub strikes: Vec<SbixStrike<'a>>,
}
pub struct SbixStrike<'a> {
scope: ReadScope<'a>,
pub ppem: u16,
pub ppi: u16,
glyph_data_offsets: ReadArray<'a, U32Be>,
}
pub struct SbixGlyph<'a> {
pub origin_offset_x: i16,
pub origin_offset_y: i16,
pub graphic_type: u32,
pub data: &'a [u8],
}
impl ReadBinaryDep for Sbix<'_> {
type Args<'a> = usize; type HostType<'a> = Sbix<'a>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
num_glyphs: usize,
) -> Result<Self::HostType<'a>, ParseError> {
let scope = ctxt.scope();
let version = ctxt.read_u16be()?;
ctxt.check(version == 1)?;
let flags = ctxt.read_u16be()?;
let num_strikes = usize::try_from(ctxt.read_u32be()?)?;
let strike_offsets = ctxt.read_array::<U32Be>(num_strikes)?;
let strikes = strike_offsets
.iter()
.map(|offset| {
let offset = usize::try_from(offset)?;
scope.offset(offset).read_dep::<SbixStrike<'_>>(num_glyphs)
})
.collect::<Result<Vec<_>, _>>()?;
Ok(Sbix { flags, strikes })
}
}
impl ReadBinaryDep for SbixStrike<'_> {
type Args<'a> = usize; type HostType<'a> = SbixStrike<'a>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
num_glyphs: usize,
) -> Result<Self::HostType<'a>, ParseError> {
let scope = ctxt.scope();
let ppem = ctxt.read_u16be()?;
let ppi = ctxt.read_u16be()?;
let glyph_data_offsets = ctxt.read_array::<U32Be>(num_glyphs + 1)?;
Ok(SbixStrike {
scope,
ppem,
ppi,
glyph_data_offsets,
})
}
}
impl ReadBinaryDep for SbixGlyph<'_> {
type Args<'a> = usize; type HostType<'a> = SbixGlyph<'a>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
length: usize,
) -> Result<Self::HostType<'a>, ParseError> {
let origin_offset_x = ctxt.read_i16be()?;
let origin_offset_y = ctxt.read_i16be()?;
let graphic_type = ctxt.read_u32be()?;
let data = ctxt.read_slice(length - 8)?;
Ok(SbixGlyph {
origin_offset_x,
origin_offset_y,
graphic_type,
data,
})
}
}
impl<'a> Sbix<'a> {
pub fn find_strike(
&self,
glyph_index: u16,
target_ppem: u16,
_max_bit_depth: BitDepth,
) -> Option<&SbixStrike<'a>> {
let target_ppem = i32::from(target_ppem);
let candidates = self
.strikes
.iter()
.filter(|strike| strike.contains_glyph(glyph_index));
let mut best = None;
for strike in candidates {
let strike_ppem = i32::from(strike.ppem);
let difference = strike_ppem - target_ppem;
match best {
Some((current_best_difference, _)) => {
if super::bigger_or_closer_to_zero(strike_ppem, current_best_difference) {
best = Some((difference, strike))
}
}
None => best = Some((difference, strike)),
}
}
best.map(|(_, strike)| strike)
}
}
impl<'a> SbixStrike<'a> {
pub fn read_glyph(&self, glyph_index: u16) -> Result<Option<SbixGlyph<'a>>, ParseError> {
let (offset, end) = self.glyph_offset_end(glyph_index)?;
match end.checked_sub(offset) {
Some(0) => {
Ok(None)
}
Some(length) => {
let data = self.scope.offset_length(offset, length)?;
data.read_dep::<SbixGlyph<'_>>(length).map(Some)
}
None => Err(ParseError::BadOffset),
}
}
fn glyph_offset_end(&self, glyph_index: u16) -> Result<(usize, usize), ParseError> {
let glyph_index = usize::from(glyph_index);
let offset = self
.glyph_data_offsets
.get_item(glyph_index)
.map(SafeFrom::safe_from)
.ok_or(ParseError::BadIndex)?;
let end = self
.glyph_data_offsets
.get_item(glyph_index + 1)
.map(SafeFrom::safe_from)
.ok_or(ParseError::BadIndex)?;
Ok((offset, end))
}
fn contains_glyph(&self, glyph_index: u16) -> bool {
self.glyph_offset_end(glyph_index)
.map(|(offset, end)| end.saturating_sub(offset) > 0)
.unwrap_or(false)
}
}
impl<'a> From<(&SbixStrike<'a>, &SbixGlyph<'a>, u16, bool)> for BitmapGlyph {
fn from(
(strike, glyph, bitmap_id, should_flip_hori): (&SbixStrike<'a>, &SbixGlyph<'a>, u16, bool),
) -> Self {
let encapsulated = EncapsulatedBitmap {
format: EncapsulatedFormat::from(glyph.graphic_type),
data: Box::from(glyph.data),
};
BitmapGlyph {
bitmap: Bitmap::Encapsulated(encapsulated),
bitmap_id,
metrics: Metrics::HmtxVmtx(OriginOffset::from(glyph)),
ppem_x: Some(strike.ppem),
ppem_y: Some(strike.ppem),
should_flip_hori,
}
}
}
impl From<u32> for EncapsulatedFormat {
fn from(tag: u32) -> Self {
match tag {
tag::JPG => EncapsulatedFormat::Jpeg,
tag::PNG => EncapsulatedFormat::Png,
tag::TIFF => EncapsulatedFormat::Tiff,
_ => EncapsulatedFormat::Other(tag),
}
}
}
impl From<&SbixGlyph<'_>> for OriginOffset {
fn from(glyph: &SbixGlyph<'_>) -> Self {
OriginOffset {
x: glyph.origin_offset_x,
y: glyph.origin_offset_y,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary::read::ReadScope;
use crate::font_data::FontData;
use crate::tables::{FontTableProvider, MaxpTable};
use crate::tag;
use crate::tests::read_fixture;
#[test]
fn test_read_sbix() {
let buffer = read_fixture("tests/fonts/woff1/chromacheck-sbix.woff");
let scope = ReadScope::new(&buffer);
let font_file = scope
.read::<FontData<'_>>()
.expect("unable to parse font file");
let table_provider = font_file
.table_provider(0)
.expect("unable to create font provider");
let maxp_data = table_provider
.read_table_data(tag::MAXP)
.expect("unable to read maxp table data");
let maxp = ReadScope::new(&maxp_data).read::<MaxpTable>().unwrap();
let sbix_data = table_provider
.read_table_data(tag::SBIX)
.expect("unable to read sbix table data");
let sbix = ReadScope::new(&sbix_data)
.read_dep::<Sbix<'_>>(usize::try_from(maxp.num_glyphs).unwrap())
.unwrap();
assert_eq!(sbix.flags, 1);
assert_eq!(sbix.strikes.len(), 1);
let strike = &sbix.strikes[0];
assert_eq!(strike.ppem, 300);
assert_eq!(strike.ppi, 72);
let glyphs = (0..maxp.num_glyphs)
.map(|glyph_index| strike.read_glyph(glyph_index))
.collect::<Result<Vec<_>, _>>()
.expect("unable to read glyph");
assert_eq!(glyphs.len(), 2);
assert!(glyphs[0].is_none());
if let Some(ref glyph) = glyphs[1] {
assert_eq!(glyph.origin_offset_x, 0);
assert_eq!(glyph.origin_offset_y, 0);
assert_eq!(glyph.graphic_type, tag::PNG);
assert_eq!(glyph.data.len(), 224);
assert_eq!(*glyph.data.last().unwrap(), 0x82);
} else {
panic!("expected Some(SbixGlyph) got None");
}
}
}