include!("../../generated/generated_bitmap.rs");
impl BitmapSize {
pub fn location(
&self,
offset_data: FontData,
glyph_id: GlyphId,
) -> Result<BitmapLocation, ReadError> {
if !(self.start_glyph_index()..=self.end_glyph_index()).contains(&glyph_id) {
return Err(ReadError::OutOfBounds);
}
let mut location = BitmapLocation {
bit_depth: self.bit_depth,
..BitmapLocation::default()
};
for ix in 0..self.number_of_index_subtables() {
let subtable = self.subtable(offset_data, ix)?;
if !(subtable.first_glyph_index..=subtable.last_glyph_index).contains(&glyph_id) {
continue;
}
let glyph_ix =
glyph_id.to_u32() as usize - subtable.first_glyph_index.to_u32() as usize;
match &subtable.kind {
IndexSubtable::Format1(st) => {
location.format = st.image_format();
let start = st.image_data_offset() as usize
+ st.sbit_offsets()
.get(glyph_ix)
.ok_or(ReadError::OutOfBounds)?
.get() as usize;
let end = st.image_data_offset() as usize
+ st.sbit_offsets()
.get(glyph_ix + 1)
.ok_or(ReadError::OutOfBounds)?
.get() as usize;
location.data_offset = start;
if end < start {
return Err(ReadError::OutOfBounds);
}
location.data_size = end - start;
}
IndexSubtable::Format2(st) => {
location.format = st.image_format();
let data_size = st.image_size() as usize;
location.data_size = data_size;
location.data_offset = st.image_data_offset() as usize + glyph_ix * data_size;
location.metrics = Some(st.big_metrics()[0]);
}
IndexSubtable::Format3(st) => {
location.format = st.image_format();
let start = st.image_data_offset() as usize
+ st.sbit_offsets()
.get(glyph_ix)
.ok_or(ReadError::OutOfBounds)?
.get() as usize;
let end = st.image_data_offset() as usize
+ st.sbit_offsets()
.get(glyph_ix + 1)
.ok_or(ReadError::OutOfBounds)?
.get() as usize;
location.data_offset = start;
if end < start {
return Err(ReadError::OutOfBounds);
}
location.data_size = end - start;
}
IndexSubtable::Format4(st) => {
location.format = st.image_format();
let array = st.glyph_array();
let array_ix = match array
.binary_search_by(|x| x.glyph_id().to_u32().cmp(&glyph_id.to_u32()))
{
Ok(ix) => ix,
_ => return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u32())),
};
let start = array[array_ix].sbit_offset() as usize;
let end = array
.get(array_ix + 1)
.ok_or(ReadError::OutOfBounds)?
.sbit_offset() as usize;
location.data_offset = start;
if end < start {
return Err(ReadError::OutOfBounds);
}
location.data_size = end - start;
}
IndexSubtable::Format5(st) => {
location.format = st.image_format();
let array = st.glyph_array();
let array_ix = match array
.binary_search_by(|gid| gid.get().to_u32().cmp(&glyph_id.to_u32()))
{
Ok(ix) => ix,
_ => return Err(ReadError::InvalidCollectionIndex(glyph_id.to_u32())),
};
let data_size = st.image_size() as usize;
location.data_size = data_size;
location.data_offset = st.image_data_offset() as usize + array_ix * data_size;
location.metrics = Some(st.big_metrics()[0]);
}
}
return Ok(location);
}
Err(ReadError::OutOfBounds)
}
fn subtable<'a>(
&self,
offset_data: FontData<'a>,
index: u32,
) -> Result<BitmapSizeSubtable<'a>, ReadError> {
let base_offset = self.index_subtable_array_offset() as usize;
const SUBTABLE_HEADER_SIZE: usize = 8;
let header_offset = base_offset + index as usize * SUBTABLE_HEADER_SIZE;
let header_data = offset_data
.slice(header_offset..)
.ok_or(ReadError::OutOfBounds)?;
let header = IndexSubtableArray::read(header_data)?;
let subtable_offset = base_offset + header.additional_offset_to_index_subtable() as usize;
let subtable_data = offset_data
.slice(subtable_offset..)
.ok_or(ReadError::OutOfBounds)?;
let subtable = IndexSubtable::read(subtable_data)?;
Ok(BitmapSizeSubtable {
first_glyph_index: header.first_glyph_index(),
last_glyph_index: header.last_glyph_index(),
kind: subtable,
})
}
}
struct BitmapSizeSubtable<'a> {
pub first_glyph_index: GlyphId16,
pub last_glyph_index: GlyphId16,
pub kind: IndexSubtable<'a>,
}
#[derive(Clone, Default)]
pub struct BitmapLocation {
pub format: u16,
pub data_offset: usize,
pub data_size: usize,
pub bit_depth: u8,
pub metrics: Option<BigGlyphMetrics>,
}
impl BitmapLocation {
pub fn is_empty(&self) -> bool {
self.data_size == 0
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum BitmapDataFormat {
BitAligned,
ByteAligned,
Png,
}
#[derive(Clone)]
pub enum BitmapMetrics {
Small(SmallGlyphMetrics),
Big(BigGlyphMetrics),
}
#[derive(Clone)]
pub struct BitmapData<'a> {
pub metrics: BitmapMetrics,
pub content: BitmapContent<'a>,
}
#[derive(Clone)]
pub enum BitmapContent<'a> {
Data(BitmapDataFormat, &'a [u8]),
Composite(&'a [BdtComponent]),
}
pub(crate) fn bitmap_data<'a>(
offset_data: FontData<'a>,
location: &BitmapLocation,
is_color: bool,
) -> Result<BitmapData<'a>, ReadError> {
let mut image_data = offset_data
.slice(location.data_offset..location.data_offset + location.data_size)
.ok_or(ReadError::OutOfBounds)?
.cursor();
match location.format {
1 => {
let metrics = read_small_metrics(&mut image_data)?;
let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8;
let height = metrics.height as usize;
let data = image_data.read_array::<u8>(pitch * height)?;
Ok(BitmapData {
metrics: BitmapMetrics::Small(metrics),
content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
})
}
2 => {
let metrics = read_small_metrics(&mut image_data)?;
let width = metrics.width as usize * location.bit_depth as usize;
let height = metrics.height as usize;
let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
Ok(BitmapData {
metrics: BitmapMetrics::Small(metrics),
content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
})
}
5 => {
let metrics = location.metrics.ok_or(ReadError::MalformedData(
"expected metrics from location table",
))?;
let width = metrics.width as usize * location.bit_depth as usize;
let height = metrics.height as usize;
let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
Ok(BitmapData {
metrics: BitmapMetrics::Big(metrics),
content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
})
}
6 => {
let metrics = read_big_metrics(&mut image_data)?;
let pitch = (metrics.width as usize * location.bit_depth as usize + 7) / 8;
let height = metrics.height as usize;
let data = image_data.read_array::<u8>(pitch * height)?;
Ok(BitmapData {
metrics: BitmapMetrics::Big(metrics),
content: BitmapContent::Data(BitmapDataFormat::ByteAligned, data),
})
}
7 => {
let metrics = read_big_metrics(&mut image_data)?;
let width = metrics.width as usize * location.bit_depth as usize;
let height = metrics.height as usize;
let data = image_data.read_array::<u8>((width * height + 7) / 8)?;
Ok(BitmapData {
metrics: BitmapMetrics::Big(metrics),
content: BitmapContent::Data(BitmapDataFormat::BitAligned, data),
})
}
8 => {
let metrics = read_small_metrics(&mut image_data)?;
let _pad = image_data.read::<u8>()?;
let count = image_data.read::<u16>()? as usize;
let components = image_data.read_array::<BdtComponent>(count)?;
Ok(BitmapData {
metrics: BitmapMetrics::Small(metrics),
content: BitmapContent::Composite(components),
})
}
9 => {
let metrics = read_big_metrics(&mut image_data)?;
let count = image_data.read::<u16>()? as usize;
let components = image_data.read_array::<BdtComponent>(count)?;
Ok(BitmapData {
metrics: BitmapMetrics::Big(metrics),
content: BitmapContent::Composite(components),
})
}
17 if is_color => {
let metrics = read_small_metrics(&mut image_data)?;
let data_len = image_data.read::<u32>()? as usize;
let data = image_data.read_array::<u8>(data_len)?;
Ok(BitmapData {
metrics: BitmapMetrics::Small(metrics),
content: BitmapContent::Data(BitmapDataFormat::Png, data),
})
}
18 if is_color => {
let metrics = read_big_metrics(&mut image_data)?;
let data_len = image_data.read::<u32>()? as usize;
let data = image_data.read_array::<u8>(data_len)?;
Ok(BitmapData {
metrics: BitmapMetrics::Big(metrics),
content: BitmapContent::Data(BitmapDataFormat::Png, data),
})
}
19 if is_color => {
let metrics = location.metrics.ok_or(ReadError::MalformedData(
"expected metrics from location table",
))?;
let data_len = image_data.read::<u32>()? as usize;
let data = image_data.read_array::<u8>(data_len)?;
Ok(BitmapData {
metrics: BitmapMetrics::Big(metrics),
content: BitmapContent::Data(BitmapDataFormat::Png, data),
})
}
_ => Err(ReadError::MalformedData("unexpected bitmap data format")),
}
}
fn read_small_metrics(cursor: &mut Cursor) -> Result<SmallGlyphMetrics, ReadError> {
Ok(cursor.read_array::<SmallGlyphMetrics>(1)?[0])
}
fn read_big_metrics(cursor: &mut Cursor) -> Result<BigGlyphMetrics, ReadError> {
Ok(cursor.read_array::<BigGlyphMetrics>(1)?[0])
}
#[cfg(feature = "experimental_traverse")]
impl SbitLineMetrics {
pub(crate) fn traversal_type<'a>(&self, data: FontData<'a>) -> FieldType<'a> {
FieldType::Record(self.traverse(data))
}
}