#![deny(missing_docs)]
use core::convert::TryFrom;
use core::fmt;
use alloc::vec::Vec;
use alloc::boxed::Box;
use bitreader::{BitReader, BitReaderError};
use super::BitDepth;
use crate::binary::read::{
CheckIndex, ReadArray, ReadBinary, ReadBinaryDep, ReadCtxt, ReadFixedSizeDep, ReadFrom,
ReadScope,
};
use crate::binary::{U16Be, U32Be, I8, U8};
use crate::bitmap::{
Bitmap, BitmapGlyph, BitmapMetrics, EmbeddedBitmap, EmbeddedMetrics, EncapsulatedBitmap,
EncapsulatedFormat, Metrics,
};
use crate::error::ParseError;
use crate::size;
const HORIZONTAL_METRICS: i8 = 1;
const VERTICAL_METRICS: i8 = 2;
pub struct CBLCTable<'a> {
pub major_version: u16,
pub minor_version: u16,
pub bitmap_sizes: Vec<BitmapSize<'a>>,
}
pub struct BitmapSize<'a> {
pub inner: BitmapInfo,
index_sub_table_records: ReadArray<'a, IndexSubTableRecord>,
index_sub_tables: Vec<IndexSubTable<'a>>,
}
#[allow(missing_docs)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct SbitLineMetrics {
pub ascender: i8,
pub descender: i8,
pub width_max: u8,
pub caret_slope_numerator: i8,
pub caret_slope_denominator: i8,
pub caret_offset: i8,
pub min_origin_sb: i8,
pub min_advance_sb: i8,
pub max_before_bl: i8,
pub min_after_bl: i8,
pub pad1: i8,
pub pad2: i8,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct BitmapInfo {
pub hori: SbitLineMetrics,
pub vert: SbitLineMetrics,
pub start_glyph_index: u16,
pub end_glyph_index: u16,
pub ppem_x: u8,
pub ppem_y: u8,
pub bit_depth: BitDepth,
pub flags: i8,
}
struct IndexSubTableRecord {
pub first_glyph_index: u16,
pub last_glyph_index: u16,
additional_offset_to_index_sub_table: u32,
}
enum IndexSubTable<'a> {
Format1 {
image_format: ImageFormat,
image_data_offset: u32,
offsets: ReadArray<'a, U32Be>,
},
Format2 {
image_format: ImageFormat,
image_data_offset: u32,
image_size: u32,
big_metrics: BigGlyphMetrics,
},
Format3 {
image_format: ImageFormat,
image_data_offset: u32,
offsets: ReadArray<'a, U16Be>,
},
Format4 {
image_format: ImageFormat,
image_data_offset: u32,
glyph_array: ReadArray<'a, GlyphOffsetPair>,
},
Format5 {
image_format: ImageFormat,
image_data_offset: u32,
image_size: u32,
big_metrics: BigGlyphMetrics,
glyph_id_array: ReadArray<'a, U16Be>,
},
}
#[allow(missing_docs)]
#[derive(Copy, Clone)]
pub enum ImageFormat {
Format1,
Format2,
Format5,
Format6,
Format7,
Format8,
Format9,
Format17,
Format18,
Format19,
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub struct SmallGlyphMetrics {
pub height: u8,
pub width: u8,
pub bearing_x: i8,
pub bearing_y: i8,
pub advance: u8,
}
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone)]
pub struct BigGlyphMetrics {
pub height: u8,
pub width: u8,
pub hori_bearing_x: i8,
pub hori_bearing_y: i8,
pub hori_advance: u8,
pub vert_bearing_x: i8,
pub vert_bearing_y: i8,
pub vert_advance: u8,
}
enum MetricsDirection {
Horizontal,
Vertical,
Unknown,
}
struct GlyphOffsetPair {
pub glyph_id: u16,
pub offset: u16,
}
pub struct CBDTTable<'a> {
pub major_version: u16,
pub minor_version: u16,
data: ReadScope<'a>,
}
pub enum GlyphBitmapData<'a> {
Format1 {
small_metrics: SmallGlyphMetrics,
data: &'a [u8],
},
Format2 {
small_metrics: SmallGlyphMetrics,
data: &'a [u8],
},
Format5 {
big_metrics: BigGlyphMetrics,
data: &'a [u8],
},
Format6 {
big_metrics: BigGlyphMetrics,
data: &'a [u8],
},
Format7 {
big_metrics: BigGlyphMetrics,
data: &'a [u8],
},
Format8 {
small_metrics: SmallGlyphMetrics,
components: ReadArray<'a, EbdtComponent>,
},
Format9 {
big_metrics: BigGlyphMetrics,
components: ReadArray<'a, EbdtComponent>,
},
Format17 {
small_metrics: SmallGlyphMetrics,
data: &'a [u8],
},
Format18 {
big_metrics: BigGlyphMetrics,
data: &'a [u8],
},
Format19 {
big_metrics: BigGlyphMetrics,
data: &'a [u8],
},
}
pub struct EbdtComponent {
pub glyph_id: u16,
pub x_offset: i8,
pub y_offset: i8,
}
pub struct MatchingStrike<'a, 'b> {
pub(crate) bitmap_size: &'a BitmapSize<'b>,
index_subtable_index: usize,
}
pub fn lookup<'a, 'b>(
glyph_id: u16,
matching_strike: &MatchingStrike<'_, '_>,
cbdt: &CBDTTable<'b>,
) -> Result<Option<GlyphBitmapData<'b>>, ParseError> {
let index_sub_table_header: &IndexSubTableRecord = &matching_strike
.bitmap_size
.index_sub_table_records
.get_item(matching_strike.index_subtable_index);
match &matching_strike.bitmap_size.index_sub_tables[matching_strike.index_subtable_index] {
IndexSubTable::Format1 {
image_format,
image_data_offset,
offsets,
} => {
let glyph_index = usize::from(glyph_id - index_sub_table_header.first_glyph_index);
offsets.check_index(glyph_index + 1)?;
let start = usize::try_from(offsets.get_item(glyph_index))?;
let end = usize::try_from(offsets.get_item(glyph_index + 1))?;
let length = end - start;
if length == 0 {
return Ok(None);
}
let offset = usize::try_from(*image_data_offset)? + start;
let mut ctxt = cbdt.data.offset_length(offset, length)?.ctxt();
let bitmap = ctxt.read_dep::<ImageFormat>((*image_format, None))?;
Ok(Some(bitmap))
}
IndexSubTable::Format2 {
image_format,
image_data_offset,
image_size,
big_metrics,
} => {
let glyph_index = u32::from(glyph_id - index_sub_table_header.first_glyph_index);
let offset = usize::try_from(image_data_offset + (glyph_index * image_size))?;
let mut ctxt = cbdt
.data
.offset_length(offset, usize::try_from(*image_size)?)?
.ctxt();
let bitmap = ctxt.read_dep::<ImageFormat>((*image_format, Some(*big_metrics)))?;
Ok(Some(bitmap))
}
IndexSubTable::Format3 {
image_format,
image_data_offset,
offsets,
} => {
let glyph_index = usize::from(glyph_id - index_sub_table_header.first_glyph_index);
offsets.check_index(glyph_index + 1)?;
let start = usize::from(offsets.get_item(glyph_index));
let end = usize::from(offsets.get_item(glyph_index + 1));
let length = end - start;
if length == 0 {
return Ok(None);
}
let offset = usize::try_from(*image_data_offset)? + start;
let mut ctxt = cbdt.data.offset_length(offset, length)?.ctxt();
let bitmap = ctxt.read_dep::<ImageFormat>((*image_format, None))?;
Ok(Some(bitmap))
}
IndexSubTable::Format4 {
image_format,
image_data_offset,
glyph_array,
} => {
for (glyph_index, glyph_offset_pair) in glyph_array.iter().enumerate() {
if glyph_offset_pair.glyph_id == glyph_id {
let offset = usize::try_from(*image_data_offset)?
+ usize::from(glyph_offset_pair.offset);
glyph_array.check_index(glyph_index + 1)?;
let end = glyph_array.get_item(glyph_index + 1);
let length = usize::from(end.offset - glyph_offset_pair.offset);
let mut ctxt = cbdt.data.offset_length(offset, length)?.ctxt();
let bitmap = ctxt.read_dep::<ImageFormat>((*image_format, None))?;
return Ok(Some(bitmap));
} else if glyph_offset_pair.glyph_id > glyph_id {
return Ok(None);
}
}
Ok(None)
}
IndexSubTable::Format5 {
image_format,
image_data_offset,
image_size,
big_metrics,
glyph_id_array,
} => {
for (glyph_index, this_glyph_id) in glyph_id_array.iter().enumerate() {
if this_glyph_id == glyph_id {
let offset =
usize::try_from(image_data_offset + (glyph_index as u32 * image_size))?;
let mut ctxt = cbdt
.data
.offset_length(offset, usize::try_from(*image_size)?)?
.ctxt();
let bitmap =
ctxt.read_dep::<ImageFormat>((*image_format, Some(*big_metrics)))?;
return Ok(Some(bitmap));
} else if this_glyph_id > glyph_id {
return Ok(None);
}
}
Ok(None)
}
}
}
impl<'a> ReadBinaryDep<'a> for ImageFormat {
type Args = (ImageFormat, Option<BigGlyphMetrics>);
type HostType = GlyphBitmapData<'a>;
fn read_dep(
ctxt: &mut ReadCtxt<'a>,
(format, metrics): Self::Args,
) -> Result<Self::HostType, ParseError> {
match format {
ImageFormat::Format1 => {
let small_metrics = ctxt.read::<SmallGlyphMetrics>()?;
let data = ctxt.scope().data();
Ok(GlyphBitmapData::Format1 {
small_metrics,
data,
})
}
ImageFormat::Format2 => {
let small_metrics = ctxt.read::<SmallGlyphMetrics>()?;
let data = ctxt.scope().data();
Ok(GlyphBitmapData::Format2 {
small_metrics,
data,
})
}
ImageFormat::Format5 => Ok(GlyphBitmapData::Format5 {
big_metrics: metrics.ok_or(ParseError::MissingValue)?,
data: ctxt.scope().data(),
}),
ImageFormat::Format6 => {
let big_metrics = ctxt.read::<BigGlyphMetrics>()?;
let data = ctxt.scope().data();
Ok(GlyphBitmapData::Format6 { big_metrics, data })
}
ImageFormat::Format7 => {
let big_metrics = ctxt.read::<BigGlyphMetrics>()?;
let data = ctxt.scope().data();
Ok(GlyphBitmapData::Format7 { big_metrics, data })
}
ImageFormat::Format8 => {
let small_metrics = ctxt.read::<SmallGlyphMetrics>()?;
let _pad = ctxt.read_u8()?;
let num_components = usize::from(ctxt.read_u16be()?);
let components = ctxt.read_array::<EbdtComponent>(num_components)?;
Ok(GlyphBitmapData::Format8 {
small_metrics,
components,
})
}
ImageFormat::Format9 => {
let big_metrics = ctxt.read::<BigGlyphMetrics>()?;
let num_components = usize::from(ctxt.read_u16be()?);
let components = ctxt.read_array::<EbdtComponent>(num_components)?;
Ok(GlyphBitmapData::Format9 {
big_metrics,
components,
})
}
ImageFormat::Format17 => {
let small_metrics = ctxt.read::<SmallGlyphMetrics>()?;
let data_len = usize::try_from(ctxt.read_u32be()?)?;
let data = ctxt.read_slice(data_len)?;
Ok(GlyphBitmapData::Format17 {
small_metrics,
data,
})
}
ImageFormat::Format18 => {
let big_metrics = ctxt.read::<BigGlyphMetrics>()?;
let data_len = usize::try_from(ctxt.read_u32be()?)?;
let data = ctxt.read_slice(data_len)?;
Ok(GlyphBitmapData::Format18 { big_metrics, data })
}
ImageFormat::Format19 => {
let data_len = usize::try_from(ctxt.read_u32be()?)?;
let data = ctxt.read_slice(data_len)?;
Ok(GlyphBitmapData::Format19 {
big_metrics: metrics.ok_or(ParseError::MissingValue)?,
data,
})
}
}
}
}
impl<'a> CBLCTable<'a> {
pub fn find_strike(
&self,
glyph_id: u16,
target_ppem: u8,
max_bit_depth: BitDepth,
) -> Option<MatchingStrike<'_, 'a>> {
let candidates = self.bitmap_sizes.iter().filter_map(|bitmap_size| {
bitmap_size
.index_sub_table_index(glyph_id)
.and_then(|index| {
if bitmap_size.inner.bit_depth <= max_bit_depth {
Some((bitmap_size, index))
} else {
None
}
})
});
let size_ppem = i16::from(target_ppem);
let mut best: Option<(i16, &BitmapSize<'a>, usize)> = None;
for (bitmap_size, index) in candidates {
let difference = i16::from(bitmap_size.inner.ppem_x) - size_ppem;
match best {
Some((current_best_difference, current_best_bitmap_size, _))
if same_size_higher_bit_depth(
difference,
current_best_difference,
bitmap_size.inner.bit_depth,
current_best_bitmap_size.inner.bit_depth,
) =>
{
best = Some((difference, bitmap_size, index))
}
Some((current_best_difference, _, _))
if super::bigger_or_closer_to_zero(difference, current_best_difference) =>
{
best = Some((difference, bitmap_size, index))
}
None => best = Some((difference, bitmap_size, index)),
_ => (),
}
}
best.map(|(_, bitmap_size, index)| MatchingStrike {
bitmap_size,
index_subtable_index: index,
})
}
}
fn same_size_higher_bit_depth(
difference: i16,
current_best_difference: i16,
candiate_bit_depth: BitDepth,
current_best_bit_depth: BitDepth,
) -> bool {
difference == current_best_difference && candiate_bit_depth > current_best_bit_depth
}
impl<'a> ReadBinary<'a> for CBLCTable<'a> {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let table = ctxt.scope();
let major_version = ctxt.read_u16be()?;
ctxt.check_version(major_version >= 2 && major_version <= 3)?;
let minor_version = ctxt.read_u16be()?;
let num_sizes = ctxt.read_u32be()?;
let bitmap_sizes = ctxt
.read_array_dep::<BitmapSize<'_>>(usize::try_from(num_sizes)?, table)?
.iter_res()
.collect::<Result<Vec<_>, _>>()?;
Ok(CBLCTable {
major_version,
minor_version,
bitmap_sizes,
})
}
}
impl<'a> ReadBinary<'a> for CBDTTable<'a> {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let data = ctxt.scope();
let major_version = ctxt.read_u16be()?;
ctxt.check_version(major_version >= 2 && major_version <= 3)?;
let minor_version = ctxt.read_u16be()?;
Ok(CBDTTable {
major_version,
minor_version,
data,
})
}
}
impl<'a> BitmapSize<'a> {
fn index_sub_table_index(&self, glyph_id: u16) -> Option<usize> {
if (self.inner.start_glyph_index..=self.inner.end_glyph_index).contains(&glyph_id) {
self.index_sub_table_records
.iter()
.position(|record| record.contains_glyph(glyph_id))
} else {
None
}
}
}
impl<'a, 'b> MatchingStrike<'a, 'b> {
pub fn bit_depth(&self) -> BitDepth {
self.bitmap_size.inner.bit_depth
}
}
impl<'a> ReadBinaryDep<'a> for BitmapSize<'a> {
type Args = ReadScope<'a>;
type HostType = Self;
fn read_dep(ctxt: &mut ReadCtxt<'a>, cblc_scope: Self::Args) -> Result<Self, ParseError> {
let index_sub_table_array_offset = usize::try_from(ctxt.read_u32be()?)?;
let _index_tables_size = ctxt.read_u32be()?;
let number_of_index_sub_tables = ctxt.read_u32be()?;
let _color_ref = ctxt.read_u32be()?;
let hori = ctxt.read::<SbitLineMetrics>()?;
let vert = ctxt.read::<SbitLineMetrics>()?;
let start_glyph_index = ctxt.read_u16be()?;
let end_glyph_index = ctxt.read_u16be()?;
let ppem_x = ctxt.read_u8()?;
let ppem_y = ctxt.read_u8()?;
let bit_depth = BitDepth::try_from(ctxt.read_u8()?)?;
let flags = ctxt.read_i8()?;
let index_sub_table_records: ReadArray<'_, IndexSubTableRecord> = cblc_scope
.offset(index_sub_table_array_offset)
.ctxt()
.read_array::<IndexSubTableRecord>(usize::try_from(number_of_index_sub_tables)?)?;
let mut index_sub_tables = Vec::with_capacity(usize::try_from(number_of_index_sub_tables)?);
for index_sub_table_record in index_sub_table_records.iter() {
let offset = index_sub_table_array_offset
.checked_add(usize::try_from(
index_sub_table_record.additional_offset_to_index_sub_table,
)?)
.ok_or(ParseError::BadOffset)?;
let index_sub_table = cblc_scope
.offset(offset)
.ctxt()
.read_dep::<IndexSubTable<'_>>((
index_sub_table_record.first_glyph_index,
index_sub_table_record.last_glyph_index,
))?;
index_sub_tables.push(index_sub_table);
}
Ok(BitmapSize {
inner: BitmapInfo {
hori,
vert,
start_glyph_index,
end_glyph_index,
ppem_x,
ppem_y,
bit_depth,
flags,
},
index_sub_table_records,
index_sub_tables,
})
}
}
impl<'a> ReadFixedSizeDep<'a> for BitmapSize<'a> {
fn size(_: Self::Args) -> usize {
(4 * size::U32)
+ (2 * SbitLineMetrics::size(()))
+ (2 * size::U16)
+ 4
}
}
impl<'a> ReadBinary<'a> for SbitLineMetrics {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let ascender = ctxt.read_i8()?;
let descender = ctxt.read_i8()?;
let width_max = ctxt.read_u8()?;
let caret_slope_numerator = ctxt.read_i8()?;
let caret_slope_denominator = ctxt.read_i8()?;
let caret_offset = ctxt.read_i8()?;
let min_origin_sb = ctxt.read_i8()?;
let min_advance_sb = ctxt.read_i8()?;
let max_before_bl = ctxt.read_i8()?;
let min_after_bl = ctxt.read_i8()?;
let pad1 = ctxt.read_i8()?;
let pad2 = ctxt.read_i8()?;
Ok(SbitLineMetrics {
ascender,
descender,
width_max,
caret_slope_numerator,
caret_slope_denominator,
caret_offset,
min_origin_sb,
min_advance_sb,
max_before_bl,
min_after_bl,
pad1,
pad2,
})
}
}
impl<'a> ReadFixedSizeDep<'a> for SbitLineMetrics {
fn size(_scope: Self::Args) -> usize {
12
}
}
impl TryFrom<u8> for BitDepth {
type Error = ParseError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(BitDepth::One),
2 => Ok(BitDepth::Two),
4 => Ok(BitDepth::Four),
8 => Ok(BitDepth::Eight),
32 => Ok(BitDepth::ThirtyTwo),
_ => Err(ParseError::BadValue),
}
}
}
impl<'a> IndexSubTableRecord {
fn contains_glyph(&self, glyph_id: u16) -> bool {
(self.first_glyph_index..=self.last_glyph_index).contains(&glyph_id)
}
}
impl<'a> ReadFrom<'a> for IndexSubTableRecord {
type ReadType = (U16Be, U16Be, U32Be);
fn from(
(first_glyph_index, last_glyph_index, additional_offset_to_index_sub_table): (
u16,
u16,
u32,
),
) -> Self {
IndexSubTableRecord {
first_glyph_index,
last_glyph_index,
additional_offset_to_index_sub_table,
}
}
}
impl<'a> ReadBinaryDep<'a> for IndexSubTable<'a> {
type Args = (u16, u16);
type HostType = Self;
fn read_dep(
ctxt: &mut ReadCtxt<'a>,
(first_glyph_index, last_glyph_index): (u16, u16),
) -> Result<Self, ParseError> {
let index_format = ctxt.read_u16be()?;
let image_format = ImageFormat::try_from(ctxt.read_u16be()?)?;
let image_data_offset = ctxt.read_u32be()?;
match index_format {
1 => {
let offsets = ctxt.read_array::<U32Be>(usize::from(
last_glyph_index - first_glyph_index + 1 + 1,
))?;
Ok(IndexSubTable::Format1 {
image_format,
image_data_offset,
offsets,
})
}
2 => {
let image_size = ctxt.read_u32be()?;
let big_metrics = ctxt.read::<BigGlyphMetrics>()?;
Ok(IndexSubTable::Format2 {
image_format,
image_data_offset,
image_size,
big_metrics,
})
}
3 => {
let offsets = ctxt.read_array::<U16Be>(usize::from(
last_glyph_index - first_glyph_index + 1 + 1,
))?;
Ok(IndexSubTable::Format3 {
image_format,
image_data_offset,
offsets,
})
}
4 => {
let num_glyphs = ctxt.read_u32be()?;
let glyph_array =
ctxt.read_array::<GlyphOffsetPair>(usize::try_from(num_glyphs + 1)?)?;
Ok(IndexSubTable::Format4 {
image_format,
image_data_offset,
glyph_array,
})
}
5 => {
let image_size = ctxt.read_u32be()?;
let big_metrics = ctxt.read::<BigGlyphMetrics>()?;
let num_glyphs = ctxt.read_u32be()?;
let glyph_id_array = ctxt.read_array::<U16Be>(usize::try_from(num_glyphs)?)?;
Ok(IndexSubTable::Format5 {
image_format,
image_data_offset,
image_size,
big_metrics,
glyph_id_array,
})
}
_ => Err(ParseError::BadValue),
}
}
}
impl<'a> ReadFrom<'a> for SmallGlyphMetrics {
type ReadType = ((U8, U8), (I8, I8, U8));
fn from(((height, width), (bearing_x, bearing_y, advance)): ((u8, u8), (i8, i8, u8))) -> Self {
SmallGlyphMetrics {
height,
width,
bearing_x,
bearing_y,
advance,
}
}
}
impl<'a> ReadBinary<'a> for BigGlyphMetrics {
type HostType = Self;
fn read(ctxt: &mut ReadCtxt<'a>) -> Result<Self, ParseError> {
let height = ctxt.read_u8()?;
let width = ctxt.read_u8()?;
let hori_bearing_x = ctxt.read_i8()?;
let hori_bearing_y = ctxt.read_i8()?;
let hori_advance = ctxt.read_u8()?;
let vert_bearing_x = ctxt.read_i8()?;
let vert_bearing_y = ctxt.read_i8()?;
let vert_advance = ctxt.read_u8()?;
Ok(BigGlyphMetrics {
height,
width,
hori_bearing_x,
hori_bearing_y,
hori_advance,
vert_bearing_x,
vert_bearing_y,
vert_advance,
})
}
}
impl<'a> ReadFixedSizeDep<'a> for BigGlyphMetrics {
fn size(_scope: Self::Args) -> usize {
8
}
}
impl<'a> ReadFrom<'a> for GlyphOffsetPair {
type ReadType = (U16Be, U16Be);
fn from((glyph_id, offset): (u16, u16)) -> Self {
GlyphOffsetPair { glyph_id, offset }
}
}
impl<'a> ReadFrom<'a> for EbdtComponent {
type ReadType = (U16Be, I8, I8);
fn from((glyph_id, x_offset, y_offset): (u16, i8, i8)) -> Self {
EbdtComponent {
glyph_id,
x_offset,
y_offset,
}
}
}
impl TryFrom<u16> for ImageFormat {
type Error = ParseError;
fn try_from(value: u16) -> Result<Self, Self::Error> {
match value {
1 => Ok(ImageFormat::Format1),
2 => Ok(ImageFormat::Format2),
5 => Ok(ImageFormat::Format5),
6 => Ok(ImageFormat::Format6),
7 => Ok(ImageFormat::Format7),
8 => Ok(ImageFormat::Format8),
9 => Ok(ImageFormat::Format9),
17 => Ok(ImageFormat::Format17),
18 => Ok(ImageFormat::Format18),
19 => Ok(ImageFormat::Format19),
_ => Err(ParseError::BadValue),
}
}
}
impl<'a> TryFrom<(&BitmapInfo, GlyphBitmapData<'a>)> for BitmapGlyph {
type Error = ParseError;
fn try_from((info, glyph): (&BitmapInfo, GlyphBitmapData<'a>)) -> Result<Self, Self::Error> {
let res = match glyph {
GlyphBitmapData::Format1 {
small_metrics,
data,
} => {
let data = bgra_to_rgba(info.bit_depth, data.to_vec())?;
let metrics = EmbeddedMetrics::try_from((info, &small_metrics))?;
BitmapGlyph {
bitmap: Bitmap::Embedded(EmbeddedBitmap {
format: info.bit_depth,
width: small_metrics.width,
height: small_metrics.height,
data: Box::from(data),
}),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
GlyphBitmapData::Format2 {
small_metrics,
data,
} => {
let metrics = EmbeddedMetrics::try_from((info, &small_metrics))?;
let unpacked = unpack_bit_aligned_data(
info.bit_depth,
small_metrics.width,
small_metrics.height,
data,
)
.map_err(parse_error_from_bitreader_error)
.and_then(|data| bgra_to_rgba(info.bit_depth, data))?;
BitmapGlyph {
bitmap: Bitmap::Embedded(EmbeddedBitmap {
format: info.bit_depth,
width: small_metrics.width,
height: small_metrics.height,
data: unpacked.into(),
}),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
GlyphBitmapData::Format5 { big_metrics, data } => {
let metrics = EmbeddedMetrics::try_from((info, &big_metrics))?;
let unpacked = unpack_bit_aligned_data(
info.bit_depth,
big_metrics.width,
big_metrics.height,
data,
)
.map_err(parse_error_from_bitreader_error)
.and_then(|data| bgra_to_rgba(info.bit_depth, data))?;
BitmapGlyph {
bitmap: Bitmap::Embedded(EmbeddedBitmap {
format: info.bit_depth,
width: big_metrics.width,
height: big_metrics.height,
data: unpacked.into(),
}),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
GlyphBitmapData::Format6 { big_metrics, data } => {
let data = bgra_to_rgba(info.bit_depth, data.to_vec())?;
let metrics = EmbeddedMetrics::try_from((info, &big_metrics))?;
BitmapGlyph {
bitmap: Bitmap::Embedded(EmbeddedBitmap {
format: info.bit_depth,
width: big_metrics.width,
height: big_metrics.height,
data: Box::from(data),
}),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
GlyphBitmapData::Format7 { big_metrics, data } => {
let metrics = EmbeddedMetrics::try_from((info, &big_metrics))?;
let unpacked = unpack_bit_aligned_data(
info.bit_depth,
big_metrics.width,
big_metrics.height,
data,
)
.map_err(parse_error_from_bitreader_error)
.and_then(|data| bgra_to_rgba(info.bit_depth, data))?;
BitmapGlyph {
bitmap: Bitmap::Embedded(EmbeddedBitmap {
format: info.bit_depth,
width: big_metrics.width,
height: big_metrics.height,
data: unpacked.into(),
}),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
GlyphBitmapData::Format8 { .. } => return Err(ParseError::NotImplemented),
GlyphBitmapData::Format9 { .. } => return Err(ParseError::NotImplemented),
GlyphBitmapData::Format17 {
small_metrics,
data,
} => {
let metrics = EmbeddedMetrics::try_from((info, &small_metrics))?;
let bitmap = EncapsulatedBitmap {
format: EncapsulatedFormat::Png,
data: Box::from(data),
};
BitmapGlyph {
bitmap: Bitmap::Encapsulated(bitmap),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
GlyphBitmapData::Format18 { big_metrics, data }
| GlyphBitmapData::Format19 { big_metrics, data } => {
let metrics = EmbeddedMetrics::try_from((info, &big_metrics))?;
let bitmap = EncapsulatedBitmap {
format: EncapsulatedFormat::Png,
data: Box::from(data),
};
BitmapGlyph {
bitmap: Bitmap::Encapsulated(bitmap),
metrics: Metrics::Embedded(metrics),
ppem_x: Some(u16::from(info.ppem_x)),
ppem_y: Some(u16::from(info.ppem_y)),
}
}
};
Ok(res)
}
}
impl<'a> GlyphBitmapData<'a> {
pub fn width(&self) -> u8 {
match self {
GlyphBitmapData::Format1 {
small_metrics: SmallGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format2 {
small_metrics: SmallGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format5 {
big_metrics: BigGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format6 {
big_metrics: BigGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format7 {
big_metrics: BigGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format8 {
small_metrics: SmallGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format9 {
big_metrics: BigGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format17 {
small_metrics: SmallGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format18 {
big_metrics: BigGlyphMetrics { width, .. },
..
} => *width,
GlyphBitmapData::Format19 {
big_metrics: BigGlyphMetrics { width, .. },
..
} => *width,
}
}
pub fn height(&self) -> u8 {
match self {
GlyphBitmapData::Format1 {
small_metrics: SmallGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format2 {
small_metrics: SmallGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format5 {
big_metrics: BigGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format6 {
big_metrics: BigGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format7 {
big_metrics: BigGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format8 {
small_metrics: SmallGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format9 {
big_metrics: BigGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format17 {
small_metrics: SmallGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format18 {
big_metrics: BigGlyphMetrics { height, .. },
..
} => *height,
GlyphBitmapData::Format19 {
big_metrics: BigGlyphMetrics { height, .. },
..
} => *height,
}
}
}
impl<'a> fmt::Debug for GlyphBitmapData<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
GlyphBitmapData::Format1 {
small_metrics,
data,
} => f
.debug_struct("GlyphBitmapData::Format1")
.field("small_metrics", &small_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format2 {
small_metrics,
data,
} => f
.debug_struct("GlyphBitmapData::Format2")
.field("small_metrics", &small_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format5 { big_metrics, data } => f
.debug_struct("GlyphBitmapData::Format5")
.field("big_metrics", &big_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format6 { big_metrics, data } => f
.debug_struct("GlyphBitmapData::Format6")
.field("big_metrics", &big_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format7 { big_metrics, data } => f
.debug_struct("GlyphBitmapData::Format7")
.field("big_metrics", &big_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format8 { small_metrics, .. } => f
.debug_struct("GlyphBitmapData::Format8")
.field("small_metrics", &small_metrics)
.finish(),
GlyphBitmapData::Format9 { big_metrics, .. } => f
.debug_struct("GlyphBitmapData::Format9")
.field("big_metrics", &big_metrics)
.finish(),
GlyphBitmapData::Format17 {
small_metrics,
data,
} => f
.debug_struct("GlyphBitmapData::Format17")
.field("small_metrics", &small_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format18 { big_metrics, data } => f
.debug_struct("GlyphBitmapData::Format18")
.field("big_metrics", &big_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
GlyphBitmapData::Format19 { big_metrics, data } => f
.debug_struct("GlyphBitmapData::Format19")
.field("big_metrics", &big_metrics)
.field("data", &format_args!("[{} bytes]", data.len()))
.finish(),
}
}
}
impl TryFrom<(&BitmapInfo, &SmallGlyphMetrics)> for EmbeddedMetrics {
type Error = ParseError;
fn try_from(
(info, small_metrics): (&BitmapInfo, &SmallGlyphMetrics),
) -> Result<Self, Self::Error> {
match info.small_glyph_metrics_direction() {
MetricsDirection::Horizontal | MetricsDirection::Unknown => EmbeddedMetrics::new(
info.ppem_x,
info.ppem_y,
Some(BitmapMetrics {
origin_offset_x: i16::from(small_metrics.bearing_x),
origin_offset_y: i16::from(small_metrics.bearing_y)
- i16::from(small_metrics.height),
advance: small_metrics.advance,
ascender: info.hori.ascender,
descender: info.hori.descender,
}),
None,
),
MetricsDirection::Vertical => EmbeddedMetrics::new(
info.ppem_x,
info.ppem_y,
None,
Some(BitmapMetrics {
origin_offset_x: i16::from(small_metrics.bearing_x),
origin_offset_y: i16::from(small_metrics.bearing_y)
- i16::from(small_metrics.height),
advance: small_metrics.advance,
ascender: info.vert.ascender,
descender: info.vert.descender,
}),
),
}
}
}
impl TryFrom<(&BitmapInfo, &BigGlyphMetrics)> for EmbeddedMetrics {
type Error = ParseError;
fn try_from((info, big_metrics): (&BitmapInfo, &BigGlyphMetrics)) -> Result<Self, Self::Error> {
EmbeddedMetrics::new(
info.ppem_x,
info.ppem_y,
Some(BitmapMetrics {
origin_offset_x: i16::from(big_metrics.hori_bearing_x),
origin_offset_y: i16::from(big_metrics.hori_bearing_y)
- i16::from(big_metrics.height),
advance: big_metrics.hori_advance,
ascender: info.hori.ascender,
descender: info.hori.descender,
}),
Some(BitmapMetrics {
origin_offset_x: i16::from(big_metrics.vert_bearing_x),
origin_offset_y: i16::from(big_metrics.vert_bearing_y)
- i16::from(big_metrics.height),
advance: big_metrics.vert_advance,
ascender: info.vert.ascender,
descender: info.vert.descender,
}),
)
}
}
impl BitmapInfo {
fn small_glyph_metrics_direction(&self) -> MetricsDirection {
if self.flags & HORIZONTAL_METRICS == HORIZONTAL_METRICS {
MetricsDirection::Horizontal
} else if self.flags & VERTICAL_METRICS == VERTICAL_METRICS {
MetricsDirection::Vertical
} else {
MetricsDirection::Unknown
}
}
}
fn unpack_bit_aligned_data(
bit_depth: BitDepth,
width: u8,
height: u8,
data: &[u8],
) -> Result<Vec<u8>, BitReaderError> {
let bits_per_row = bit_depth as usize * usize::from(width);
let whole_bytes_per_row = bits_per_row >> 3;
let remaining_bits = (bits_per_row & 7) as u8;
let bytes_per_row = whole_bytes_per_row + if remaining_bits != 0 { 1 } else { 0 };
let mut offset = 0;
let mut image_data = vec![0u8; usize::from(height) * bytes_per_row];
let mut reader = BitReader::new(data);
for _ in 0..height {
for byte in image_data[offset..(offset + whole_bytes_per_row)].iter_mut() {
*byte = reader.read_u8(8)?;
}
offset += whole_bytes_per_row;
if remaining_bits != 0 {
let byte = reader.read_u8(remaining_bits)?;
image_data[offset] = byte << (8 - remaining_bits);
offset += 1;
}
}
Ok(image_data)
}
fn parse_error_from_bitreader_error(err: BitReaderError) -> ParseError {
match err {
BitReaderError::NotEnoughData { .. } => ParseError::BadEof,
BitReaderError::TooManyBitsForType { .. } => {
unreachable!("{}", err)
}
}
}
fn bgra_to_rgba(bit_depth: BitDepth, mut data: Vec<u8>) -> Result<Vec<u8>, ParseError> {
match bit_depth {
BitDepth::One | BitDepth::Two | BitDepth::Four | BitDepth::Eight => Ok(data),
BitDepth::ThirtyTwo => {
if data.len() % 4 != 0 {
return Err(ParseError::BadEof);
}
data.chunks_exact_mut(4).for_each(|chunk| chunk.swap(0, 2));
Ok(data)
}
}
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use alloc::borrow::Borrow;
use super::*;
use crate::font_data::FontData;
use crate::tables::FontTableProvider;
use crate::tag;
#[test]
fn test_parse_cblc() {
let cblc_data = include_bytes!("../../tests/fonts/opentype/CBLC.bin");
let cblc = ReadScope::new(&cblc_data).read::<CBLCTable<'_>>().unwrap();
let strikes = &cblc.bitmap_sizes;
assert_eq!(strikes.len(), 1);
assert_eq!(strikes[0].index_sub_tables.len(), 3);
let ranges = strikes[0]
.index_sub_table_records
.iter()
.map(|rec| rec.first_glyph_index..=rec.last_glyph_index)
.collect::<Vec<_>>();
assert_eq!(ranges, &[4..=17, 19..=1316, 1354..=3112]);
}
#[test]
fn test_parse_eblc() {
let buffer = include_bytes!("../../tests/fonts/opentype/TerminusTTF-4.47.0.ttf");
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 table = table_provider
.table_data(tag::EBLC)
.expect("no EBLC table")
.expect("no EBLC table");
let scope = ReadScope::new(table.borrow());
let eblc = scope.read::<CBLCTable<'_>>().unwrap();
let strikes = &eblc.bitmap_sizes;
assert_eq!(strikes.len(), 9);
}
#[test]
fn test_lookup_eblc() {
let buffer = include_bytes!("../../tests/fonts/opentype/TerminusTTF-4.47.0.ttf");
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 table = table_provider
.table_data(tag::EBLC)
.expect("no EBLC table")
.expect("no EBLC table");
let scope = ReadScope::new(table.borrow());
let eblc = scope.read::<CBLCTable<'_>>().unwrap();
let table = table_provider
.table_data(tag::EBDT)
.expect("no EBDT table")
.expect("no EBDT table");
let scope = ReadScope::new(table.borrow());
let ebdt = scope.read::<CBDTTable<'_>>().unwrap();
let strike = eblc
.find_strike(10, 30, BitDepth::ThirtyTwo)
.expect("no matching strike");
let res = lookup(10, &strike, &ebdt).expect("error looking up glyph");
match res {
Some(GlyphBitmapData::Format5 { data, .. }) => assert_eq!(data.len(), 64),
_ => panic!("expected GlyphBitmapData::Format5 got something else"),
}
}
#[test]
fn test_lookup_cblc() {
let cblc_data = include_bytes!("../../tests/fonts/opentype/CBLC.bin");
let cblc = ReadScope::new(&cblc_data).read::<CBLCTable<'_>>().unwrap();
let cbdt_data = include_bytes!("../../tests/fonts/opentype/CBDT.bin");
let cbdt = ReadScope::new(&cbdt_data).read::<CBDTTable<'_>>().unwrap();
let strike = cblc
.find_strike(1077, 30, BitDepth::ThirtyTwo)
.expect("no matching strike");
let res = lookup(1077, &strike, &cbdt).expect("error looking up glyph");
match res {
Some(GlyphBitmapData::Format17 {
data,
small_metrics: SmallGlyphMetrics { width, height, .. },
}) => {
assert_eq!((width, height), (136, 128));
assert_eq!(&data[1..4], b"PNG");
}
_ => panic!("expected PNG data got something else"),
}
assert!(cblc.find_strike(1077, 30, BitDepth::Four).is_none());
}
#[test]
fn test_unpack_bit_aligned_data() {
let data = &[0xD3, 0xAA, 0x70];
let expected = &[0xD3, 0x80, 0xA9, 0xC0];
let actual = unpack_bit_aligned_data(BitDepth::Two, 5, 2, data).unwrap();
assert_eq!(&actual, expected);
}
#[test]
fn test_bgra_to_rgba_no_change() {
let original = vec![1, 2, 3, 4, 5, 6, 7, 8];
let actual = bgra_to_rgba(BitDepth::One, original.clone()).unwrap();
assert_eq!(actual, original);
}
#[test]
fn test_bgra_to_rgba_reorder() {
let data = vec![1, 2, 3, 4, 5, 6, 7, 8];
let expected = &[3, 2, 1, 4, 7, 6, 5, 8];
let actual = bgra_to_rgba(BitDepth::ThirtyTwo, data).unwrap();
assert_eq!(&actual, expected);
}
#[test]
fn test_bgra_to_rgba_too_short() {
let data = vec![1, 2, 3, 4, 5, 6, 7];
let res = bgra_to_rgba(BitDepth::ThirtyTwo, data);
assert_eq!(res, Err(ParseError::BadEof));
}
}