#![deny(missing_docs)]
use alloc::borrow::Cow;
use alloc::collections::BTreeMap;
use alloc::boxed::Box;
use crate::binary::read::ReadScope;
use crate::error::ParseError;
use crate::font::Encoding;
use crate::macroman::macroman_to_char;
use crate::post::PostTable;
use crate::tables::cmap::CmapSubtable;
use crate::tables::{HheaTable, HmtxTable, MaxpTable};
pub fn advance(
maxp: &MaxpTable,
hhea: &HheaTable,
hmtx_data: &[u8],
glyph: u16,
) -> Result<u16, ParseError> {
let glyph = usize::from(glyph);
let num_glyphs = usize::from(maxp.num_glyphs);
let num_metrics = usize::from(hhea.num_h_metrics);
let hmtx = ReadScope::new(hmtx_data).read_dep::<HmtxTable<'_>>((num_glyphs, num_metrics))?;
if glyph > num_glyphs - 1 {
Ok(0)
} else if glyph < num_metrics {
Ok(hmtx.h_metrics.get_item(glyph).advance_width)
} else if num_metrics > 0 {
Ok(hmtx.h_metrics.get_item(num_metrics - 1).advance_width)
} else {
Err(ParseError::BadIndex)
}
}
rental! {
mod rentable {
use super::*;
use alloc::boxed::Box;
#[rental]
pub struct Post {
data: Box<[u8]>,
table: PostTable<'data>,
}
}
}
pub struct GlyphNames {
post: Option<rentable::Post>,
cmap: Option<CmapMappings>,
}
struct CmapMappings {
encoding: Encoding,
mappings: BTreeMap<u16, u32>,
}
impl GlyphNames {
pub fn new(
cmap_subtable: &Option<(Encoding, CmapSubtable<'_>)>,
post_data: Option<Box<[u8]>>,
) -> Self {
let post = post_data.and_then(|data| {
rentable::Post::try_new_or_drop(data, |data| {
ReadScope::new(data).read::<PostTable<'_>>()
})
.ok()
});
let cmap = cmap_subtable
.as_ref()
.and_then(|(encoding, subtable)| CmapMappings::new(*encoding, subtable));
GlyphNames { post, cmap }
}
pub fn glyph_name<'a>(&self, gid: u16) -> Cow<'a, str> {
if gid == 0 {
return Cow::from(".notdef");
}
self.glyph_name_from_post(gid)
.or_else(|| self.glyph_name_from_cmap(gid))
.unwrap_or_else(|| Cow::from(format!("g{}", gid)))
}
fn glyph_name_from_post<'a>(&self, gid: u16) -> Option<Cow<'a, str>> {
let post = self.post.as_ref()?;
post.glyph_name(gid)
}
fn glyph_name_from_cmap<'a>(&self, gid: u16) -> Option<Cow<'a, str>> {
let cmap = self.cmap.as_ref()?;
cmap.glyph_name(gid)
}
}
impl rentable::Post {
fn glyph_name<'a>(&self, gid: u16) -> Option<Cow<'a, str>> {
#[cfg(not(feature = "std"))]
use alloc::borrow::ToOwned;
self.rent(|post: &PostTable<'_>| {
match post.glyph_name(gid) {
Ok(Some(glyph_name)) if glyph_name != ".notdef" => {
Some(Cow::from(glyph_name.to_owned()))
}
_ => None,
}
})
}
}
impl CmapMappings {
fn new(encoding: Encoding, subtable: &CmapSubtable<'_>) -> Option<CmapMappings> {
let mappings = subtable.mappings().ok()?;
Some(CmapMappings { encoding, mappings })
}
fn glyph_name<'a>(&self, gid: u16) -> Option<Cow<'a, str>> {
let &ch = self.mappings.get(&gid)?;
match self.encoding {
Encoding::AppleRoman => glyph_names::glyph_name(macroman_to_unicode(ch)?),
Encoding::Unicode => glyph_names::glyph_name(ch),
Encoding::Symbol => None,
Encoding::Big5 => None, }
}
}
fn macroman_to_unicode(ch: u32) -> Option<u32> {
macroman_to_char(ch as u8).map(|ch| ch as u32)
}