#![deny(missing_docs)]
mod outline;
mod subset;
mod variation;
use std::mem;
use std::sync::Arc;
use itertools::Itertools;
use log::warn;
use pathfinder_geometry::transform2d::Matrix2x2F;
use pathfinder_geometry::vector::Vector2F;
use rustc_hash::FxHashMap;
use crate::binary::read::{
ReadBinary, ReadBinaryDep, ReadCtxt, ReadFrom, ReadScope, ReadUnchecked,
};
use crate::binary::write::{WriteBinary, WriteBinaryDep, WriteContext};
use crate::binary::{word_align, I16Be, U16Be, I8, U8};
use crate::error::{ParseError, WriteError};
use crate::tables::loca::{owned, LocaTable};
use crate::tables::os2::Os2;
use crate::tables::{
read_and_box_table, F2Dot14, FontTableProvider, HeadTable, HheaTable, HmtxTable,
IndexToLocFormat, MaxpTable,
};
use crate::{tag, SafeFrom};
pub use outline::{GlyfVisitorContext, VariableGlyfContext, VariableGlyfContextStore};
pub use subset::SubsetGlyph;
#[allow(unused)]
const COMPOSITE_GLYPH_RECURSION_LIMIT: u8 = 6;
#[enumflags2::bitflags]
#[repr(u8)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub enum SimpleGlyphFlag {
ON_CURVE_POINT = 0b00000001,
X_SHORT_VECTOR = 0b00000010,
Y_SHORT_VECTOR = 0b00000100,
REPEAT_FLAG = 0b00001000,
X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR = 0b00010000,
Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR = 0b00100000,
}
pub type SimpleGlyphFlags = enumflags2::BitFlags<SimpleGlyphFlag>;
#[enumflags2::bitflags]
#[repr(u16)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub enum CompositeGlyphFlag {
ARG_1_AND_2_ARE_WORDS = 0x0001,
ARGS_ARE_XY_VALUES = 0x0002,
ROUND_XY_TO_GRID = 0x0004,
WE_HAVE_A_SCALE = 0x0008,
MORE_COMPONENTS = 0x0020,
WE_HAVE_AN_X_AND_Y_SCALE = 0x0040,
WE_HAVE_A_TWO_BY_TWO = 0x0080,
WE_HAVE_INSTRUCTIONS = 0x0100,
USE_MY_METRICS = 0x0200,
OVERLAP_COMPOUND = 0x0400,
SCALED_COMPONENT_OFFSET = 0x0800,
UNSCALED_COMPONENT_OFFSET = 0x1000,
}
pub type CompositeGlyphFlags = enumflags2::BitFlags<CompositeGlyphFlag>;
#[derive(Debug, PartialEq)]
pub struct GlyfTable<'a> {
records: Vec<GlyfRecord<'a>>,
}
pub struct LocaGlyf {
loaded: bool,
loca: owned::LocaTable,
glyf: Box<[u8]>,
cache: FxHashMap<u16, Arc<Glyph>>,
}
#[derive(Debug, PartialEq, Clone)]
pub enum GlyfRecord<'a> {
Present {
number_of_contours: i16,
scope: ReadScope<'a>,
},
Parsed(Glyph),
}
pub type PhantomPoints = [Point; 4];
#[derive(Debug, PartialEq, Clone)]
pub enum Glyph {
Empty(EmptyGlyph),
Simple(SimpleGlyph),
Composite(CompositeGlyph),
}
#[derive(Debug, PartialEq, Clone)]
pub struct EmptyGlyph {
pub phantom_points: Option<PhantomPoints>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct SimpleGlyph {
pub bounding_box: BoundingBox,
pub end_pts_of_contours: Vec<u16>,
pub instructions: Box<[u8]>,
pub coordinates: Vec<(SimpleGlyphFlags, Point)>,
pub phantom_points: Option<Box<PhantomPoints>>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct CompositeGlyph {
pub bounding_box: BoundingBox,
pub glyphs: Vec<CompositeGlyphComponent>,
pub instructions: Box<[u8]>,
pub phantom_points: Option<Box<PhantomPoints>>,
}
#[derive(Debug, PartialEq, Clone)]
pub struct CompositeGlyphComponent {
pub flags: CompositeGlyphFlags,
pub glyph_index: u16,
pub argument1: CompositeGlyphArgument,
pub argument2: CompositeGlyphArgument,
pub scale: Option<CompositeGlyphScale>,
}
#[allow(missing_docs)]
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum CompositeGlyphArgument {
U8(u8),
I8(i8),
U16(u16),
I16(i16),
}
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum CompositeGlyphScale {
Scale(F2Dot14),
XY {
x_scale: F2Dot14,
y_scale: F2Dot14,
},
Matrix([[F2Dot14; 2]; 2]),
}
pub struct CompositeGlyphs {
pub glyphs: Vec<CompositeGlyphComponent>,
pub have_instructions: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Point(pub i16, pub i16);
#[derive(Debug, PartialEq, Copy, Clone)]
pub struct BoundingBox {
pub x_min: i16,
pub x_max: i16,
pub y_min: i16,
pub y_max: i16,
}
impl ReadBinaryDep for GlyfTable<'_> {
type Args<'a> = &'a LocaTable<'a>;
type HostType<'a> = GlyfTable<'a>;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
loca: Self::Args<'a>,
) -> Result<Self::HostType<'a>, ParseError> {
if loca.offsets.len() < 2 {
return Err(ParseError::BadIndex);
}
let glyph_records = loca
.offsets
.iter()
.tuple_windows()
.map(|(start, end)| match end.checked_sub(start) {
Some(0) => Ok(GlyfRecord::empty()),
Some(length) => {
let offset = usize::try_from(start)?;
let glyph_scope = ctxt.scope().offset_length(offset, usize::try_from(length)?);
match glyph_scope {
Ok(scope) => {
let number_of_contours = scope.read::<I16Be>()?;
Ok(GlyfRecord::Present {
number_of_contours,
scope,
})
}
Err(ParseError::BadEof) => {
warn!("glyph length out of bounds, trying to parse");
let scope = ctxt.scope().offset(offset);
scope.read::<Glyph>().map(GlyfRecord::Parsed)
}
Err(err) => Err(err),
}
}
None => Err(ParseError::BadOffset),
})
.collect::<Result<Vec<_>, _>>()?;
Ok(GlyfTable {
records: glyph_records,
})
}
}
impl<'a> WriteBinaryDep<Self> for GlyfTable<'a> {
type Output = owned::LocaTable;
type Args = IndexToLocFormat;
fn write_dep<C: WriteContext>(
ctxt: &mut C,
table: GlyfTable<'a>,
index_to_loc_format: IndexToLocFormat,
) -> Result<Self::Output, WriteError> {
let mut offsets: Vec<u32> = Vec::with_capacity(table.records.len() + 1);
let start = ctxt.bytes_written();
for record in table.records {
let offset = ctxt.bytes_written();
offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
match record {
GlyfRecord::Present { scope, .. } => ReadScope::write(ctxt, scope)?,
GlyfRecord::Parsed(glyph) => Glyph::write(ctxt, glyph)?,
}
if index_to_loc_format == IndexToLocFormat::Short {
let length = ctxt.bytes_written() - offset;
let padded_length = word_align(length);
ctxt.write_zeros(padded_length - length)?;
}
}
offsets.push(u32::try_from(ctxt.bytes_written() - start)?);
Ok(owned::LocaTable { offsets })
}
}
impl Glyph {
pub fn empty() -> Glyph {
Glyph::Empty(EmptyGlyph::new())
}
pub fn number_of_contours(&self) -> i16 {
match self {
Glyph::Empty(_) => 0,
Glyph::Simple(simple) => simple.number_of_contours(),
Glyph::Composite(_) => -1,
}
}
pub fn bounding_box(&self) -> Option<BoundingBox> {
match self {
Glyph::Empty(_) => None,
Glyph::Simple(simple) => Some(simple.bounding_box),
Glyph::Composite(composite) => Some(composite.bounding_box),
}
}
pub fn phantom_points(&self) -> Option<PhantomPoints> {
match self {
Glyph::Empty(empty) => empty.phantom_points,
Glyph::Simple(SimpleGlyph { phantom_points, .. })
| Glyph::Composite(CompositeGlyph { phantom_points, .. }) => {
phantom_points.as_deref().copied()
}
}
}
fn number_of_points(&self) -> Result<u16, ParseError> {
match self {
Glyph::Empty(_) => Ok(0),
Glyph::Simple(glyph) => Ok(glyph.coordinates.len().try_into()?),
Glyph::Composite(composite) => Ok(composite.glyphs.len().try_into()?),
}
}
}
pub(crate) fn calculate_phantom_points(
glyph_id: u16,
bounding_box: Option<BoundingBox>,
hmtx: &HmtxTable<'_>,
vmtx: Option<&HmtxTable<'_>>,
os2: Option<&Os2>,
hhea: &HheaTable,
) -> Result<[Point; 4], ParseError> {
let horizonal_metrics = hmtx.metric(glyph_id)?;
let x_min = bounding_box.map(|bbox| bbox.x_min).unwrap_or(0);
let y_max = bounding_box.map(|bbox| bbox.y_max).unwrap_or(0);
let pp1 = Point(x_min - horizonal_metrics.lsb, 0);
let pp2 = Point(pp1.0 + i16::try_from(horizonal_metrics.advance_width)?, 0);
let (advance_height, tsb) = match vmtx {
Some(vmtx) => vmtx.metric(glyph_id).and_then(|metric| {
i16::try_from(metric.advance_width)
.map(|aw| (aw, metric.lsb))
.map_err(|_| ParseError::LimitExceeded)
})?,
None => {
let (default_ascender, default_descender) =
match os2.and_then(|os2| os2.version0.as_ref()) {
Some(os2) => (os2.s_typo_ascender, os2.s_typo_descender),
None => (hhea.ascender, hhea.descender),
};
let advance_height = default_ascender - default_descender;
let tsb = default_ascender - y_max;
(advance_height, tsb)
}
};
let x = 0;
let pp3 = Point(x, y_max + tsb);
let pp4 = Point(x, pp3.1 - advance_height);
Ok([pp1, pp2, pp3, pp4])
}
impl ReadBinary for Glyph {
type HostType<'a> = Glyph;
fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
let number_of_contours = ctxt.read_i16be()?;
if number_of_contours >= 0 {
let glyph = ctxt.read_dep::<SimpleGlyph>(number_of_contours as u16)?;
Ok(Glyph::Simple(glyph))
} else {
let glyph = ctxt.read::<CompositeGlyph>()?;
Ok(Glyph::Composite(glyph))
}
}
}
impl WriteBinary for Glyph {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, glyph: Glyph) -> Result<(), WriteError> {
match glyph {
Glyph::Empty(_) => Ok(()),
Glyph::Simple(simple_glyph) => SimpleGlyph::write(ctxt, simple_glyph),
Glyph::Composite(composite) => CompositeGlyph::write(ctxt, composite),
}
}
}
impl SimpleGlyph {
pub fn number_of_contours(&self) -> i16 {
self.end_pts_of_contours.len() as i16
}
pub fn contours(&self) -> impl Iterator<Item = &[(SimpleGlyphFlags, Point)]> {
self.end_pts_of_contours.iter().scan(0, move |i, &end| {
let start = *i;
let end = usize::from(end);
*i = end + 1;
self.coordinates.get(start..=end)
})
}
pub fn bounding_box(&self) -> BoundingBox {
BoundingBox::from_points(self.coordinates.iter().copied().map(|(_flag, point)| point))
}
}
impl ReadBinaryDep for SimpleGlyph {
type Args<'a> = u16;
type HostType<'a> = SimpleGlyph;
fn read_dep<'a>(
ctxt: &mut ReadCtxt<'a>,
number_of_contours: Self::Args<'_>,
) -> Result<Self::HostType<'a>, ParseError> {
let number_of_contours = usize::from(number_of_contours);
let bounding_box = ctxt.read::<BoundingBox>()?;
let end_pts_of_contours = ctxt.read_array::<U16Be>(number_of_contours)?.to_vec();
let instruction_length = ctxt.read::<U16Be>()?;
let instructions = ctxt.read_slice(usize::from(instruction_length))?;
let number_of_coordinates = end_pts_of_contours
.last()
.map_or(0, |&last| usize::from(last) + 1);
let mut coordinates = Vec::with_capacity(number_of_coordinates);
while coordinates.len() < number_of_coordinates {
let flag = ctxt.read::<SimpleGlyphFlags>()?;
if flag.is_repeated() {
let count = usize::from(ctxt.read::<U8>()?) + 1; let repeat = std::iter::repeat_n((flag, Point::zero()), count);
coordinates.extend(repeat)
} else {
coordinates.push((flag, Point::zero()));
}
}
for (flag, Point(x, _y)) in coordinates.iter_mut() {
*x = if flag.x_is_short() {
ctxt.read::<U8>()
.map(|val| i16::from(val) * flag.x_short_sign())?
} else if flag.x_is_same_or_positive() {
0
} else {
ctxt.read::<I16Be>()?
}
}
let mut prev_point = Point::zero();
for (flag, point) in coordinates.iter_mut() {
let y = if flag.y_is_short() {
ctxt.read::<U8>()
.map(|val| i16::from(val) * flag.y_short_sign())?
} else if flag.y_is_same_or_positive() {
0
} else {
ctxt.read::<I16Be>()?
};
prev_point = Point(prev_point.0 + point.0, prev_point.1 + y);
*point = prev_point
}
Ok(SimpleGlyph {
bounding_box,
end_pts_of_contours,
instructions: Box::from(instructions),
coordinates,
phantom_points: None,
})
}
}
impl WriteBinary for SimpleGlyph {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, glyph: SimpleGlyph) -> Result<(), WriteError> {
I16Be::write(ctxt, glyph.number_of_contours())?;
BoundingBox::write(ctxt, glyph.bounding_box)?;
ctxt.write_vec::<U16Be, _>(glyph.end_pts_of_contours)?;
U16Be::write(ctxt, u16::try_from(glyph.instructions.len())?)?;
ctxt.write_bytes(&glyph.instructions)?;
let mask = SimpleGlyphFlag::ON_CURVE_POINT; for flag in glyph.coordinates.iter().map(|(flag, _)| *flag) {
U8::write(ctxt, (flag & mask).bits())?;
}
let mut prev_x = 0;
for (_, Point(x, _)) in &glyph.coordinates {
let delta_x = x - prev_x;
I16Be::write(ctxt, delta_x)?;
prev_x = *x;
}
let mut prev_y = 0;
for (_, Point(_, y)) in &glyph.coordinates {
let delta_y = y - prev_y;
I16Be::write(ctxt, delta_y)?;
prev_y = *y;
}
Ok(())
}
}
impl ReadFrom for SimpleGlyphFlags {
type ReadType = U8;
fn read_from(flag: u8) -> Self {
SimpleGlyphFlags::from_bits_truncate(flag)
}
}
impl ReadBinary for CompositeGlyphs {
type HostType<'a> = Self;
fn read(ctxt: &mut ReadCtxt<'_>) -> Result<Self, ParseError> {
let mut have_instructions = false;
let mut glyphs = Vec::new();
loop {
let flags = ctxt.read::<CompositeGlyphFlags>()?;
let data = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
if flags.we_have_instructions() {
have_instructions = true;
}
glyphs.push(data);
if !flags.more_components() {
break;
}
}
Ok(CompositeGlyphs {
glyphs,
have_instructions,
})
}
}
impl ReadBinary for CompositeGlyph {
type HostType<'a> = CompositeGlyph;
fn read<'a>(ctxt: &mut ReadCtxt<'a>) -> Result<Self::HostType<'a>, ParseError> {
let bounding_box = ctxt.read::<BoundingBox>()?;
let glyphs = ctxt.read::<CompositeGlyphs>()?;
let instruction_length = if glyphs.have_instructions {
usize::from(ctxt.read::<U16Be>()?)
} else {
0
};
let instructions = ctxt.read_slice(instruction_length)?;
Ok(CompositeGlyph {
bounding_box,
glyphs: glyphs.glyphs,
instructions: Box::from(instructions),
phantom_points: None,
})
}
}
impl WriteBinary for CompositeGlyph {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, composite: Self) -> Result<Self::Output, WriteError> {
I16Be::write(ctxt, -1_i16)?; BoundingBox::write(ctxt, composite.bounding_box)?;
let mut has_instructions = false;
for glyph in composite.glyphs {
has_instructions |= glyph.flags.we_have_instructions();
CompositeGlyphComponent::write(ctxt, glyph)?;
}
if has_instructions {
U16Be::write(ctxt, u16::try_from(composite.instructions.len())?)?;
ctxt.write_bytes(&composite.instructions)?;
}
Ok(())
}
}
#[allow(missing_docs)]
pub trait SimpleGlyphFlagExt {
fn is_on_curve(self) -> bool;
fn x_is_short(self) -> bool;
fn y_is_short(self) -> bool;
fn is_repeated(self) -> bool;
fn x_short_sign(self) -> i16;
fn y_short_sign(self) -> i16;
fn x_is_same_or_positive(self) -> bool;
fn y_is_same_or_positive(self) -> bool;
}
impl SimpleGlyphFlagExt for SimpleGlyphFlags {
fn is_on_curve(self) -> bool {
self.contains(SimpleGlyphFlag::ON_CURVE_POINT)
}
fn x_is_short(self) -> bool {
self.contains(SimpleGlyphFlag::X_SHORT_VECTOR)
}
fn y_is_short(self) -> bool {
self.contains(SimpleGlyphFlag::Y_SHORT_VECTOR)
}
fn is_repeated(self) -> bool {
self.contains(SimpleGlyphFlag::REPEAT_FLAG)
}
fn x_short_sign(self) -> i16 {
if self.x_is_same_or_positive() {
1
} else {
-1
}
}
fn y_short_sign(self) -> i16 {
if self.y_is_same_or_positive() {
1
} else {
-1
}
}
fn x_is_same_or_positive(self) -> bool {
self.contains(SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR)
}
fn y_is_same_or_positive(self) -> bool {
self.contains(SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR)
}
}
impl ReadFrom for CompositeGlyphFlags {
type ReadType = U16Be;
fn read_from(flag: u16) -> Self {
CompositeGlyphFlags::from_bits_truncate(flag)
}
}
impl ReadBinaryDep for CompositeGlyphArgument {
type Args<'a> = CompositeGlyphFlags;
type HostType<'a> = Self;
fn read_dep(ctxt: &mut ReadCtxt<'_>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
let arg = match (flags.arg_1_and_2_are_words(), flags.args_are_xy_values()) {
(true, true) => CompositeGlyphArgument::I16(ctxt.read_i16be()?),
(true, false) => CompositeGlyphArgument::U16(ctxt.read_u16be()?),
(false, true) => CompositeGlyphArgument::I8(ctxt.read_i8()?),
(false, false) => CompositeGlyphArgument::U8(ctxt.read_u8()?),
};
Ok(arg)
}
}
impl WriteBinary for CompositeGlyphArgument {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, arg: CompositeGlyphArgument) -> Result<(), WriteError> {
match arg {
CompositeGlyphArgument::U8(val) => U8::write(ctxt, val),
CompositeGlyphArgument::I8(val) => I8::write(ctxt, val),
CompositeGlyphArgument::U16(val) => U16Be::write(ctxt, val),
CompositeGlyphArgument::I16(val) => I16Be::write(ctxt, val),
}
}
}
impl ReadBinaryDep for CompositeGlyphComponent {
type Args<'a> = CompositeGlyphFlags;
type HostType<'a> = Self;
fn read_dep(ctxt: &mut ReadCtxt<'_>, flags: Self::Args<'_>) -> Result<Self, ParseError> {
let glyph_index = ctxt.read_u16be()?;
let argument1 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
let argument2 = ctxt.read_dep::<CompositeGlyphArgument>(flags)?;
let scale = if flags.we_have_a_scale() {
Some(CompositeGlyphScale::Scale(ctxt.read::<F2Dot14>()?))
} else if flags.we_have_an_x_and_y_scale() {
Some(CompositeGlyphScale::XY {
x_scale: ctxt.read::<F2Dot14>()?,
y_scale: ctxt.read::<F2Dot14>()?,
})
} else if flags.we_have_a_two_by_two() {
Some(CompositeGlyphScale::Matrix([
[ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
[ctxt.read::<F2Dot14>()?, ctxt.read::<F2Dot14>()?],
]))
} else {
None
};
Ok(CompositeGlyphComponent {
flags,
glyph_index,
argument1,
argument2,
scale,
})
}
}
impl WriteBinary for CompositeGlyphComponent {
type Output = ();
fn write<C: WriteContext>(
ctxt: &mut C,
glyph: CompositeGlyphComponent,
) -> Result<(), WriteError> {
U16Be::write(ctxt, glyph.flags.bits())?;
U16Be::write(ctxt, glyph.glyph_index)?;
CompositeGlyphArgument::write(ctxt, glyph.argument1)?;
CompositeGlyphArgument::write(ctxt, glyph.argument2)?;
if let Some(scale) = glyph.scale {
CompositeGlyphScale::write(ctxt, scale)?;
}
Ok(())
}
}
impl WriteBinary for CompositeGlyphScale {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, scale: CompositeGlyphScale) -> Result<(), WriteError> {
match scale {
CompositeGlyphScale::Scale(scale) => F2Dot14::write(ctxt, scale)?,
CompositeGlyphScale::XY { x_scale, y_scale } => {
F2Dot14::write(ctxt, x_scale)?;
F2Dot14::write(ctxt, y_scale)?;
}
CompositeGlyphScale::Matrix(matrix) => {
F2Dot14::write(ctxt, matrix[0][0])?;
F2Dot14::write(ctxt, matrix[0][1])?;
F2Dot14::write(ctxt, matrix[1][0])?;
F2Dot14::write(ctxt, matrix[1][1])?;
}
}
Ok(())
}
}
impl ReadFrom for BoundingBox {
type ReadType = ((I16Be, I16Be), (I16Be, I16Be));
fn read_from(((x_min, y_min), (x_max, y_max)): ((i16, i16), (i16, i16))) -> Self {
BoundingBox {
x_min,
y_min,
x_max,
y_max,
}
}
}
impl WriteBinary for BoundingBox {
type Output = ();
fn write<C: WriteContext>(ctxt: &mut C, bbox: BoundingBox) -> Result<(), WriteError> {
I16Be::write(ctxt, bbox.x_min)?;
I16Be::write(ctxt, bbox.y_min)?;
I16Be::write(ctxt, bbox.x_max)?;
I16Be::write(ctxt, bbox.y_max)?;
Ok(())
}
}
impl<'a> GlyfTable<'a> {
pub fn new(records: Vec<GlyfRecord<'a>>) -> Result<Self, ParseError> {
if records.len() > usize::from(u16::MAX) {
return Err(ParseError::LimitExceeded);
}
Ok(GlyfTable { records })
}
pub fn num_glyphs(&self) -> u16 {
self.records.len() as u16
}
pub fn records(&self) -> &[GlyfRecord<'a>] {
&self.records
}
pub fn records_mut(&mut self) -> &mut [GlyfRecord<'a>] {
&mut self.records
}
pub fn push(&mut self, record: GlyfRecord<'a>) -> Result<(), ParseError> {
if self.num_glyphs() < u16::MAX {
self.records.push(record);
Ok(())
} else {
Err(ParseError::LimitExceeded)
}
}
pub fn get_parsed_glyph(&mut self, glyph_index: u16) -> Result<&Glyph, ParseError> {
let record = self
.records
.get_mut(usize::from(glyph_index))
.ok_or(ParseError::BadIndex)?;
record.parse()?;
match record {
GlyfRecord::Parsed(glyph) => Ok(glyph),
GlyfRecord::Present { .. } => unreachable!("glyph should be parsed"),
}
}
pub(crate) fn take(&mut self, glyph_index: u16) -> Option<GlyfRecord<'a>> {
let target = self.records.get_mut(usize::from(glyph_index))?;
Some(mem::replace(target, GlyfRecord::empty()))
}
pub(crate) fn replace(
&mut self,
glyph_index: u16,
record: GlyfRecord<'a>,
) -> Result<(), ParseError> {
let target = self
.records
.get_mut(usize::from(glyph_index))
.ok_or(ParseError::BadIndex)?;
*target = record;
Ok(())
}
}
impl LocaGlyf {
pub fn new() -> Self {
LocaGlyf {
loaded: false,
loca: owned::LocaTable::new(),
glyf: Box::default(),
cache: FxHashMap::default(),
}
}
pub fn load<F: FontTableProvider>(provider: &F) -> Result<Self, ParseError> {
let head = ReadScope::new(&provider.read_table_data(tag::HEAD)?).read::<HeadTable>()?;
let maxp = ReadScope::new(&provider.read_table_data(tag::MAXP)?).read::<MaxpTable>()?;
let loca_data = provider.read_table_data(tag::LOCA)?;
let loca = ReadScope::new(&loca_data)
.read_dep::<LocaTable<'_>>((maxp.num_glyphs, head.index_to_loc_format))?;
let loca = owned::LocaTable::from(&loca);
let glyf = read_and_box_table(provider, tag::GLYF)?;
Ok(LocaGlyf {
loaded: true,
loca,
glyf,
cache: FxHashMap::default(),
})
}
pub fn loaded(loca: owned::LocaTable, glyf: Box<[u8]>) -> Self {
LocaGlyf {
loaded: true,
loca,
glyf,
cache: FxHashMap::default(),
}
}
pub fn is_loaded(&self) -> bool {
self.loaded
}
pub fn glyph(&mut self, index: u16) -> Result<Arc<Glyph>, ParseError> {
if let Some(glyph) = self.cache.get(&index) {
return Ok(Arc::clone(glyph));
}
let start = self
.loca
.offsets
.get(usize::from(index))
.copied()
.ok_or(ParseError::BadOffset)
.map(usize::safe_from)?;
let end = self
.loca
.offsets
.get(
index
.checked_add(1)
.ok_or(ParseError::LimitExceeded)
.map(usize::from)?,
)
.copied()
.ok_or(ParseError::BadOffset)
.map(usize::safe_from)?
.min(self.glyf.len());
let glyph_data = self.glyf.get(start..end).ok_or(ParseError::BadOffset)?;
let glyph = if glyph_data.is_empty() {
Arc::new(Glyph::empty())
} else {
ReadScope::new(glyph_data).read::<Glyph>().map(Arc::new)?
};
self.cache.insert(index, Arc::clone(&glyph));
Ok(glyph)
}
}
impl GlyfRecord<'_> {
pub fn empty() -> Self {
GlyfRecord::Parsed(Glyph::empty())
}
pub fn number_of_contours(&self) -> i16 {
match self {
GlyfRecord::Present {
number_of_contours, ..
} => *number_of_contours,
GlyfRecord::Parsed(glyph) => glyph.number_of_contours(),
}
}
pub fn number_of_points(&self) -> Result<u16, ParseError> {
match self {
GlyfRecord::Present {
scope,
number_of_contours,
} => {
let mut ctxt = scope.ctxt();
let _skip = ctxt.read_slice(U16Be::SIZE + BoundingBox::SIZE)?;
if *number_of_contours >= 0 {
let end_pts_of_contours =
ctxt.read_array::<U16Be>(*number_of_contours as usize)?;
match end_pts_of_contours.last() {
Some(last) => last.checked_add(1).ok_or(ParseError::LimitExceeded),
None => Ok(0),
}
} else {
let mut count = 0;
loop {
let flags = ctxt.read::<CompositeGlyphFlags>()?;
let _composite_glyph = ctxt.read_dep::<CompositeGlyphComponent>(flags)?;
count += 1;
if !flags.more_components() {
break;
}
}
Ok(count)
}
}
GlyfRecord::Parsed(glyph) => glyph.number_of_points(),
}
}
pub fn is_composite(&self) -> bool {
self.number_of_contours() < 0
}
pub fn parse(&mut self) -> Result<(), ParseError> {
if let GlyfRecord::Present { scope, .. } = self {
*self = scope.read::<Glyph>().map(GlyfRecord::Parsed)?;
}
Ok(())
}
}
impl<'a> From<SimpleGlyph> for GlyfRecord<'a> {
fn from(glyph: SimpleGlyph) -> GlyfRecord<'a> {
GlyfRecord::Parsed(Glyph::Simple(glyph))
}
}
impl<'a> From<CompositeGlyph> for GlyfRecord<'a> {
fn from(glyph: CompositeGlyph) -> GlyfRecord<'a> {
GlyfRecord::Parsed(Glyph::Composite(glyph))
}
}
impl EmptyGlyph {
pub fn new() -> Self {
EmptyGlyph {
phantom_points: None,
}
}
}
#[allow(missing_docs)]
pub trait CompositeGlyphFlagExt {
fn arg_1_and_2_are_words(self) -> bool;
fn args_are_xy_values(self) -> bool;
fn we_have_a_scale(self) -> bool;
fn we_have_an_x_and_y_scale(self) -> bool;
fn we_have_a_two_by_two(self) -> bool;
fn more_components(self) -> bool;
fn we_have_instructions(self) -> bool;
fn component_offsets(self) -> ComponentOffsets;
}
impl CompositeGlyphFlagExt for CompositeGlyphFlags {
fn arg_1_and_2_are_words(self) -> bool {
self.contains(CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS)
}
fn args_are_xy_values(self) -> bool {
self.contains(CompositeGlyphFlag::ARGS_ARE_XY_VALUES)
}
fn we_have_a_scale(self) -> bool {
self.contains(CompositeGlyphFlag::WE_HAVE_A_SCALE)
}
fn we_have_an_x_and_y_scale(self) -> bool {
self.contains(CompositeGlyphFlag::WE_HAVE_AN_X_AND_Y_SCALE)
}
fn we_have_a_two_by_two(self) -> bool {
self.contains(CompositeGlyphFlag::WE_HAVE_A_TWO_BY_TWO)
}
fn more_components(self) -> bool {
self.contains(CompositeGlyphFlag::MORE_COMPONENTS)
}
fn we_have_instructions(self) -> bool {
self.contains(CompositeGlyphFlag::WE_HAVE_INSTRUCTIONS)
}
fn component_offsets(self) -> ComponentOffsets {
let scaled = self.contains(CompositeGlyphFlag::SCALED_COMPONENT_OFFSET);
let unscaled = self.contains(CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET);
match (scaled, unscaled) {
(true, false) => ComponentOffsets::Scaled,
(false, true) => ComponentOffsets::Unscaled,
(true, true) | (false, false) => ComponentOffsets::Unscaled,
}
}
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub enum ComponentOffsets {
Scaled,
Unscaled,
}
impl Point {
pub fn zero() -> Self {
Point(0, 0)
}
}
impl BoundingBox {
pub fn empty() -> Self {
BoundingBox {
x_min: 0,
x_max: 0,
y_min: 0,
y_max: 0,
}
}
pub fn from_points(points: impl ExactSizeIterator<Item = Point>) -> Self {
assert!(points.len() > 0);
let mut points = points.peekable();
let &Point(initial_x, initial_y) = points.peek().unwrap();
let initial = BoundingBox {
x_min: initial_x,
x_max: initial_x,
y_min: initial_y,
y_max: initial_y,
};
points.fold(initial, |mut bounding_box, point| {
bounding_box.add(point);
bounding_box
})
}
pub fn add(&mut self, Point(x, y): Point) {
self.x_min = i16::min(x, self.x_min);
self.x_max = i16::max(x, self.x_max);
self.y_min = i16::min(y, self.y_min);
self.y_max = i16::max(y, self.y_max);
}
}
impl std::ops::Add for Point {
type Output = Self;
fn add(self, Point(x1, y1): Point) -> Self::Output {
let Point(x, y) = self;
Point(x + x1, y + y1)
}
}
impl From<CompositeGlyphArgument> for i32 {
fn from(arg: CompositeGlyphArgument) -> Self {
match arg {
CompositeGlyphArgument::U8(value) => i32::from(value),
CompositeGlyphArgument::I8(value) => i32::from(value),
CompositeGlyphArgument::U16(value) => i32::from(value),
CompositeGlyphArgument::I16(value) => i32::from(value),
}
}
}
impl TryFrom<CompositeGlyphArgument> for u16 {
type Error = std::num::TryFromIntError;
fn try_from(arg: CompositeGlyphArgument) -> Result<Self, Self::Error> {
match arg {
CompositeGlyphArgument::U8(value) => Ok(u16::from(value)),
CompositeGlyphArgument::I8(value) => u16::try_from(value),
CompositeGlyphArgument::U16(value) => Ok(value),
CompositeGlyphArgument::I16(value) => u16::try_from(value),
}
}
}
impl From<CompositeGlyphScale> for Matrix2x2F {
fn from(scale: CompositeGlyphScale) -> Self {
match scale {
CompositeGlyphScale::Scale(scale) => {
let scale = f32::from(scale);
Matrix2x2F::from_scale(scale)
}
CompositeGlyphScale::XY { x_scale, y_scale } => {
let scale = Vector2F::new(f32::from(x_scale), f32::from(y_scale));
Matrix2x2F::from_scale(scale)
}
CompositeGlyphScale::Matrix(matrix) => Matrix2x2F::row_major(
f32::from(matrix[0][0]),
f32::from(matrix[0][1]),
f32::from(matrix[1][0]),
f32::from(matrix[1][1]),
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary::write::WriteBuffer;
use crate::error::ReadWriteError;
pub(super) fn simple_glyph_fixture() -> SimpleGlyph {
SimpleGlyph {
bounding_box: BoundingBox {
x_min: 60,
x_max: 915,
y_min: -105,
y_max: 702,
},
end_pts_of_contours: vec![8],
instructions: Box::default(),
coordinates: vec![
(
SimpleGlyphFlag::ON_CURVE_POINT
| SimpleGlyphFlag::Y_SHORT_VECTOR
| SimpleGlyphFlag::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
Point(433, 77),
),
(
SimpleGlyphFlag::X_SHORT_VECTOR
| SimpleGlyphFlag::Y_SHORT_VECTOR
| SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
Point(499, 30),
),
(
SimpleGlyphFlag::ON_CURVE_POINT
| SimpleGlyphFlag::X_SHORT_VECTOR
| SimpleGlyphFlag::Y_SHORT_VECTOR
| SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
Point(625, 2),
),
(
SimpleGlyphFlag::X_SHORT_VECTOR
| SimpleGlyphFlag::Y_SHORT_VECTOR
| SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
Point(756, -27),
),
(
SimpleGlyphFlag::ON_CURVE_POINT
| SimpleGlyphFlag::X_SHORT_VECTOR
| SimpleGlyphFlag::Y_SHORT_VECTOR
| SimpleGlyphFlag::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
Point(915, -31),
),
(
SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
Point(891, -47),
),
(
SimpleGlyphFlag::ON_CURVE_POINT
| SimpleGlyphFlag::X_SHORT_VECTOR
| SimpleGlyphFlag::Y_SHORT_VECTOR,
Point(862, -60),
),
(
SimpleGlyphFlag::X_SHORT_VECTOR | SimpleGlyphFlag::Y_SHORT_VECTOR,
Point(832, -73),
),
(
SimpleGlyphFlag::ON_CURVE_POINT
| SimpleGlyphFlag::X_SHORT_VECTOR
| SimpleGlyphFlag::Y_SHORT_VECTOR,
Point(819, -103),
),
],
phantom_points: None,
}
}
pub(super) fn composite_glyph_fixture(instructions: &'static [u8]) -> CompositeGlyph {
CompositeGlyph {
bounding_box: BoundingBox {
x_min: 205,
x_max: 4514,
y_min: 0,
y_max: 1434,
},
glyphs: vec![
CompositeGlyphComponent {
flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
| CompositeGlyphFlag::ARGS_ARE_XY_VALUES
| CompositeGlyphFlag::ROUND_XY_TO_GRID
| CompositeGlyphFlag::MORE_COMPONENTS
| CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
glyph_index: 5,
argument1: CompositeGlyphArgument::I16(3453),
argument2: CompositeGlyphArgument::I16(0),
scale: None,
},
CompositeGlyphComponent {
flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
| CompositeGlyphFlag::ARGS_ARE_XY_VALUES
| CompositeGlyphFlag::ROUND_XY_TO_GRID
| CompositeGlyphFlag::MORE_COMPONENTS
| CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
glyph_index: 4,
argument1: CompositeGlyphArgument::I16(2773),
argument2: CompositeGlyphArgument::I16(0),
scale: None,
},
CompositeGlyphComponent {
flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
| CompositeGlyphFlag::ARGS_ARE_XY_VALUES
| CompositeGlyphFlag::ROUND_XY_TO_GRID
| CompositeGlyphFlag::MORE_COMPONENTS
| CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET,
glyph_index: 3,
argument1: CompositeGlyphArgument::I16(1182),
argument2: CompositeGlyphArgument::I16(0),
scale: None,
},
CompositeGlyphComponent {
flags: CompositeGlyphFlag::ARG_1_AND_2_ARE_WORDS
| CompositeGlyphFlag::ARGS_ARE_XY_VALUES
| CompositeGlyphFlag::ROUND_XY_TO_GRID
| CompositeGlyphFlag::UNSCALED_COMPONENT_OFFSET
| CompositeGlyphFlag::WE_HAVE_INSTRUCTIONS,
glyph_index: 2,
argument1: CompositeGlyphArgument::I16(205),
argument2: CompositeGlyphArgument::I16(0),
scale: None,
},
],
instructions: Box::from(instructions),
phantom_points: None,
}
}
#[test]
fn test_point_bounding_box() {
let points = [Point(1761, 565), Point(2007, 565), Point(1884, 1032)];
let expected = BoundingBox {
x_min: 1761,
y_min: 565,
x_max: 2007,
y_max: 1032,
};
assert_eq!(BoundingBox::from_points(points.iter().copied()), expected);
}
#[test]
fn write_glyf_table_loca_sanity_check() {
let glyf = GlyfTable {
records: vec![GlyfRecord::empty(), GlyfRecord::empty()],
};
let num_glyphs = glyf.records.len();
let mut buffer = WriteBuffer::new();
let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
assert_eq!(loca.offsets.len(), num_glyphs + 1);
}
#[test]
fn write_composite_glyf_instructions() {
let glyph = Glyph::Composite(composite_glyph_fixture(&[1, 2, 3, 4]));
let mut buffer = WriteBuffer::new();
Glyph::write(&mut buffer, glyph).unwrap();
match ReadScope::new(buffer.bytes()).read::<Glyph>() {
Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
assert_eq!(&*instructions, vec![1, 2, 3, 4].as_slice())
}
_ => panic!("did not read back expected instructions"),
}
}
#[test]
fn read_glyph_offsets_correctly() {
let glyph = simple_glyph_fixture();
let mut buffer = WriteBuffer::new();
buffer.write_zeros(4).unwrap(); SimpleGlyph::write(&mut buffer, glyph).unwrap();
let glyph_data = buffer.into_inner();
let mut buffer = WriteBuffer::new();
let loca = owned::LocaTable {
offsets: vec![4, 4, glyph_data.len() as u32 - 4],
};
owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
.expect("unable to generate loca");
let loca_data = buffer.into_inner();
let num_glyphs = 2;
let loca = ReadScope::new(&loca_data)
.read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
.expect("unable to read loca");
let glyf = ReadScope::new(&glyph_data)
.read_dep::<GlyfTable<'_>>(&loca)
.expect("unable to read glyf");
assert_eq!(glyf.records.len(), 2);
assert_eq!(&glyf.records[0], &GlyfRecord::empty());
let glyph = &glyf.records[1];
assert_eq!(glyph.number_of_contours(), 1);
}
#[test]
fn simple_glyph_with_zero_contours() {
let glyph_data = &[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ];
let expected = SimpleGlyph {
bounding_box: BoundingBox::empty(),
end_pts_of_contours: vec![],
instructions: Box::default(),
coordinates: vec![],
phantom_points: None,
};
let glyph = ReadScope::new(glyph_data)
.read_dep::<SimpleGlyph>(0)
.unwrap();
assert_eq!(glyph, expected);
}
#[test]
fn write_simple_glyph_with_zero_contours() {
let glyph = SimpleGlyph {
bounding_box: BoundingBox::empty(),
end_pts_of_contours: vec![],
instructions: Box::default(),
coordinates: vec![],
phantom_points: None,
};
let mut buffer = WriteBuffer::new();
assert!(SimpleGlyph::write(&mut buffer, glyph).is_ok());
}
#[test]
fn read_glyph_with_incorrect_loca_length() {
let glyph = simple_glyph_fixture();
let mut buffer = WriteBuffer::new();
Glyph::write(&mut buffer, Glyph::Simple(glyph)).unwrap();
let glyph_data = buffer.into_inner();
let mut buffer = WriteBuffer::new();
let loca = owned::LocaTable {
offsets: vec![0, 0, glyph_data.len() as u32 + 1], };
owned::LocaTable::write_dep(&mut buffer, loca, IndexToLocFormat::Long)
.expect("unable to generate loca");
let loca_data = buffer.into_inner();
let num_glyphs = 2;
let loca = ReadScope::new(&loca_data)
.read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))
.expect("unable to read loca");
assert!(ReadScope::new(&glyph_data)
.read_dep::<GlyfTable<'_>>(&loca)
.is_ok())
}
#[test]
fn write_composite_glyph_with_empty_instructions() {
let glyph = composite_glyph_fixture(&[]);
let mut buffer = WriteBuffer::new();
Glyph::write(&mut buffer, Glyph::Composite(glyph)).unwrap();
match ReadScope::new(buffer.bytes()).read::<Glyph>() {
Ok(Glyph::Composite(CompositeGlyph { instructions, .. })) => {
assert_eq!(instructions, Box::default())
}
Ok(_) => panic!("did not read back expected glyph"),
Err(_) => panic!("unable to read back glyph"),
}
}
#[test]
fn test_number_of_points_empty() {
let glyph = GlyfRecord::empty();
assert_eq!(glyph.number_of_points().unwrap(), 0);
}
#[test]
fn test_number_of_points_simple_parsed() {
let glyph = GlyfRecord::from(simple_glyph_fixture());
assert_eq!(glyph.number_of_points().unwrap(), 9);
}
#[test]
fn test_number_of_points_simple_present() -> Result<(), ReadWriteError> {
let glyph = GlyfRecord::from(simple_glyph_fixture());
let glyf = GlyfTable {
records: vec![GlyfRecord::empty(), glyph],
};
let num_glyphs = glyf.records.len() as u16;
let mut buffer = WriteBuffer::new();
let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
let mut loca_buffer = WriteBuffer::new();
owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
let loca_data = loca_buffer.into_inner();
let loca = ReadScope::new(&loca_data)
.read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
let glyf = ReadScope::new(&buffer.bytes())
.read_dep::<GlyfTable<'_>>(&loca)
.unwrap();
let glyph = &glyf.records[1];
assert!(matches!(glyph, GlyfRecord::Present { .. }));
assert_eq!(glyph.number_of_points().unwrap(), 9);
Ok(())
}
#[test]
fn test_number_of_points_composite_parsed() {
let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
assert_eq!(glyph.number_of_points().unwrap(), 4);
}
#[test]
fn test_number_of_points_composite_present() -> Result<(), ReadWriteError> {
let glyph = GlyfRecord::from(composite_glyph_fixture(&[]));
let glyf = GlyfTable {
records: vec![GlyfRecord::empty(), glyph],
};
let num_glyphs = glyf.records.len() as u16;
let mut buffer = WriteBuffer::new();
let loca = GlyfTable::write_dep(&mut buffer, glyf, IndexToLocFormat::Long).unwrap();
let mut loca_buffer = WriteBuffer::new();
owned::LocaTable::write_dep(&mut loca_buffer, loca, IndexToLocFormat::Long)?;
let loca_data = loca_buffer.into_inner();
let loca = ReadScope::new(&loca_data)
.read_dep::<LocaTable<'_>>((num_glyphs, IndexToLocFormat::Long))?;
let glyf = ReadScope::new(&buffer.bytes())
.read_dep::<GlyfTable<'_>>(&loca)
.unwrap();
let glyph = &glyf.records[1];
assert!(matches!(glyph, GlyfRecord::Present { .. }));
assert_eq!(glyph.number_of_points().unwrap(), 4);
Ok(())
}
}