use crate::parser::{Stream, LazyArray, TrySlice};
use crate::{Font, GlyphId, OutlineBuilder, TableName, Rect, Result, Error};
trait OutlineBuilderInner {
fn move_to(&mut self, x: f32, y: f32);
fn line_to(&mut self, x: f32, y: f32);
fn quad_to(&mut self, x1: f32, y1: f32, x: f32, y: f32);
fn close(&mut self);
}
struct Builder<'a, T: OutlineBuilder> {
builder: &'a mut T,
transform: Transform,
is_default_ts: bool, }
impl<'a, T: OutlineBuilder> OutlineBuilderInner for Builder<'a, T> {
#[inline]
fn move_to(&mut self, mut x: f32, mut y: f32) {
if !self.is_default_ts {
self.transform.apply_to(&mut x, &mut y);
}
self.builder.move_to(x, y);
}
#[inline]
fn line_to(&mut self, mut x: f32, mut y: f32) {
if !self.is_default_ts {
self.transform.apply_to(&mut x, &mut y);
}
self.builder.line_to(x, y);
}
#[inline]
fn quad_to(&mut self, mut x1: f32, mut y1: f32, mut x: f32, mut y: f32) {
if !self.is_default_ts {
self.transform.apply_to(&mut x1, &mut y1);
self.transform.apply_to(&mut x, &mut y);
}
self.builder.quad_to(x1, y1, x, y);
}
#[inline]
fn close(&mut self) {
self.builder.close();
}
}
#[derive(Clone, Copy)]
struct SimpleGlyphFlags(u8);
impl SimpleGlyphFlags {
const ON_CURVE_POINT: Self = Self(1 << 0);
const X_SHORT_VECTOR: Self = Self(1 << 1);
const Y_SHORT_VECTOR: Self = Self(1 << 2);
const REPEAT_FLAG: Self = Self(1 << 3);
const X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR: Self = Self(1 << 4);
const Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR: Self = Self(1 << 5);
#[inline] fn empty() -> Self { Self(0) }
#[inline] fn all() -> Self { Self(63) }
#[inline] fn from_bits_truncate(bits: u8) -> Self { Self(bits & Self::all().0) }
#[inline] fn contains(&self, other: Self) -> bool { (self.0 & other.0) == other.0 }
}
#[derive(Clone, Copy)]
struct CompositeGlyphFlags(u16);
impl CompositeGlyphFlags {
const ARG_1_AND_2_ARE_WORDS: Self = Self(1 << 0);
const ARGS_ARE_XY_VALUES: Self = Self(1 << 1);
const WE_HAVE_A_SCALE: Self = Self(1 << 3);
const MORE_COMPONENTS: Self = Self(1 << 5);
const WE_HAVE_AN_X_AND_Y_SCALE: Self = Self(1 << 6);
const WE_HAVE_A_TWO_BY_TWO: Self = Self(1 << 7);
#[inline] fn all() -> Self { Self(235) }
#[inline] fn from_bits_truncate(bits: u16) -> Self { Self(bits & Self::all().0) }
#[inline] fn contains(&self, other: Self) -> bool { (self.0 & other.0) == other.0 }
}
#[inline]
fn f32_bound(min: f32, val: f32, max: f32) -> f32 {
debug_assert!(min.is_finite());
debug_assert!(val.is_finite());
debug_assert!(max.is_finite());
if val > max {
return max;
} else if val < min {
return min;
}
val
}
const MAX_COMPONENTS: u8 = 32;
impl<'a> Font<'a> {
pub(crate) fn glyf_glyph_outline(
&self,
glyph_id: GlyphId,
builder: &mut impl OutlineBuilder,
) -> Result<Rect> {
let mut b = Builder {
builder,
transform: Transform::default(),
is_default_ts: true,
};
let glyph_data = self.glyph_data(glyph_id)?;
self.outline_impl(glyph_data, 0, &mut b)
}
fn glyph_data(&self, glyph_id: GlyphId) -> Result<&[u8]> {
let range = self.glyph_range(glyph_id)?;
let data = self.glyf.ok_or_else(|| Error::TableMissing(TableName::GlyphData))?;
data.try_slice(range)
}
fn outline_impl<T: OutlineBuilder>(
&self,
data: &[u8],
depth: u8,
builder: &mut Builder<T>,
) -> Result<Rect> {
if depth >= MAX_COMPONENTS {
return Ok(Rect::zero());
}
let mut s = Stream::new(data);
let number_of_contours: i16 = s.read()?;
let rect = Rect {
x_min: s.read()?,
y_min: s.read()?,
x_max: s.read()?,
y_max: s.read()?,
};
if number_of_contours > 0 {
Self::parse_simple_outline(s.tail()?, number_of_contours as u16, builder)?;
} else if number_of_contours < 0 {
self.parse_composite_outline(s.tail()?, depth + 1, builder)?;
} else {
return Ok(Rect::zero());
}
Ok(rect)
}
#[inline(never)]
fn parse_simple_outline<T: OutlineBuilder>(
glyph_data: &[u8],
number_of_contours: u16,
builder: &mut Builder<T>,
) -> Result<()> {
let mut s = Stream::new(glyph_data);
let endpoints: LazyArray<u16> = s.read_array(number_of_contours)?;
let points_total = {
let last_point = endpoints.last().unwrap();
if last_point == core::u16::MAX {
return Ok(());
}
last_point + 1
};
let instructions_len: u16 = s.read()?;
s.skip_len(instructions_len);
let flags_offset = s.offset();
let x_coords_len = Self::resolve_x_coords_len(&mut s, points_total)
.ok_or_else(|| Error::NoOutline)?;
let x_coords_offset = s.offset();
let y_coords_offset = x_coords_offset + x_coords_len as usize;
let mut points = GlyphPoints {
flags: Stream::new(glyph_data.try_slice(flags_offset..x_coords_offset)?),
x_coords: Stream::new(glyph_data.try_slice(x_coords_offset..y_coords_offset)?),
y_coords: Stream::new(glyph_data.try_slice(y_coords_offset..glyph_data.len())?),
points_left: points_total,
flag_repeats: 0,
last_flags: SimpleGlyphFlags::empty(),
x: 0,
y: 0,
};
let mut total = 0u16;
let mut last = 0u16;
for n in endpoints {
if n < last {
break;
}
last = n;
if n == core::u16::MAX {
break;
}
let n = n + 1 - total;
if n >= 2 {
Self::parse_contour(points.by_ref().take(n as usize), builder);
}
total += n;
}
Ok(())
}
fn resolve_x_coords_len(
s: &mut Stream,
points_total: u16,
) -> Option<u16> {
type Flags = SimpleGlyphFlags;
let mut flags_left = points_total;
let mut x_coords_len = 0u16;
while flags_left > 0 {
let flags: u8 = s.read().ok()?;
let flags = Flags::from_bits_truncate(flags);
let repeats = if flags.contains(Flags::REPEAT_FLAG) {
let repeats: u8 = s.read().ok()?;
repeats as u16 + 1
} else {
1
};
if flags.contains(Flags::X_SHORT_VECTOR) {
x_coords_len = x_coords_len.checked_add(repeats)?;
} else {
if !flags.contains(Flags::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR) {
x_coords_len = x_coords_len.checked_add(repeats * 2)?;
}
}
if repeats <= flags_left {
flags_left -= repeats;
} else {
return None;
}
}
Some(x_coords_len)
}
fn parse_contour<T: OutlineBuilder>(
points: core::iter::Take<&mut GlyphPoints>,
builder: &mut Builder<T>,
) {
let mut first_oncurve: Option<Point> = None;
let mut first_offcurve: Option<Point> = None;
let mut last_offcurve: Option<Point> = None;
for point in points {
let p = Point { x: point.x as f32, y: point.y as f32 };
if first_oncurve.is_none() {
if point.on_curve_point {
first_oncurve = Some(p);
builder.move_to(p.x, p.y);
} else {
match first_offcurve {
Some(offcurve) => {
let mid = offcurve.lerp(p, 0.5);
first_oncurve = Some(mid);
last_offcurve = Some(p);
builder.move_to(mid.x, mid.y);
}
None => {
first_offcurve = Some(p);
}
}
}
} else {
match (last_offcurve, point.on_curve_point) {
(Some(offcurve), true) => {
last_offcurve = None;
builder.quad_to(offcurve.x, offcurve.y, p.x, p.y);
}
(Some(offcurve), false) => {
last_offcurve = Some(p);
let mid = offcurve.lerp(p, 0.5);
builder.quad_to(offcurve.x, offcurve.y, mid.x, mid.y);
}
(None, true) => {
builder.line_to(p.x, p.y);
}
(None, false) => {
last_offcurve = Some(p);
}
}
}
}
loop {
match (first_offcurve, last_offcurve) {
(Some(offcurve1), Some(offcurve2)) => {
last_offcurve = None;
let mid = offcurve2.lerp(offcurve1, 0.5);
builder.quad_to(offcurve2.x, offcurve2.y, mid.x, mid.y);
}
(Some(offcurve1), None) => {
if let Some(p) = first_oncurve {
builder.quad_to(offcurve1.x, offcurve1.y, p.x, p.y);
}
break;
}
(None, Some(offcurve2)) => {
if let Some(p) = first_oncurve {
builder.quad_to(offcurve2.x, offcurve2.y, p.x, p.y);
}
break;
}
(None, None) => {
if let Some(p) = first_oncurve {
builder.line_to(p.x, p.y);
}
break;
}
}
}
builder.close();
}
#[inline(never)]
fn parse_composite_outline<T: OutlineBuilder>(
&self,
glyph_data: &[u8],
depth: u8,
builder: &mut Builder<T>,
) -> Result<()> {
type Flags = CompositeGlyphFlags;
if depth >= MAX_COMPONENTS {
return Ok(());
}
let mut s = Stream::new(glyph_data);
let flags = Flags::from_bits_truncate(s.read()?);
let glyph_id: GlyphId = s.read()?;
let mut ts = Transform::default();
if flags.contains(Flags::ARGS_ARE_XY_VALUES) {
if flags.contains(Flags::ARG_1_AND_2_ARE_WORDS) {
ts.e = s.read::<i16>()? as f32;
ts.f = s.read::<i16>()? as f32;
} else {
ts.e = s.read::<i8>()? as f32;
ts.f = s.read::<i8>()? as f32;
}
}
if flags.contains(Flags::WE_HAVE_A_TWO_BY_TWO) {
ts.a = s.read_f2_14()?;
ts.b = s.read_f2_14()?;
ts.c = s.read_f2_14()?;
ts.d = s.read_f2_14()?;
} else if flags.contains(Flags::WE_HAVE_AN_X_AND_Y_SCALE) {
ts.a = s.read_f2_14()?;
ts.d = s.read_f2_14()?;
} else if flags.contains(Flags::WE_HAVE_A_SCALE) {
ts.a = f32_bound(-2.0, s.read_f2_14()?, 2.0);
ts.d = ts.a;
}
if let Ok(glyph_data) = self.glyph_data(glyph_id) {
let transform = Transform::combine(builder.transform, ts);
let mut b = Builder {
builder: builder.builder,
transform,
is_default_ts: transform.is_default(),
};
self.outline_impl(glyph_data, depth + 1, &mut b)?;
}
if flags.contains(Flags::MORE_COMPONENTS) {
if depth < MAX_COMPONENTS {
self.parse_composite_outline(s.tail()?, depth + 1, builder)?;
}
}
Ok(())
}
}
#[derive(Clone, Copy)]
struct Transform {
a: f32, b: f32, c: f32,
d: f32, e: f32, f: f32,
}
impl Transform {
#[inline]
fn combine(ts1: Self, ts2: Self) -> Self {
Transform {
a: ts1.a * ts2.a + ts1.c * ts2.b,
b: ts1.b * ts2.a + ts1.d * ts2.b,
c: ts1.a * ts2.c + ts1.c * ts2.d,
d: ts1.b * ts2.c + ts1.d * ts2.d,
e: ts1.a * ts2.e + ts1.c * ts2.f + ts1.e,
f: ts1.b * ts2.e + ts1.d * ts2.f + ts1.f,
}
}
#[inline]
fn apply_to(&self, x: &mut f32, y: &mut f32) {
let tx = *x;
let ty = *y;
*x = self.a * tx + self.c * ty + self.e;
*y = self.b * tx + self.d * ty + self.f;
}
#[inline]
fn is_default(&self) -> bool {
self.a == 1.0
&& self.b == 0.0
&& self.c == 0.0
&& self.d == 1.0
&& self.e == 0.0
&& self.f == 0.0
}
}
impl Default for Transform {
#[inline]
fn default() -> Self {
Transform { a: 1.0, b: 0.0, c: 0.0, d: 1.0, e: 0.0, f: 0.0 }
}
}
impl core::fmt::Debug for Transform {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Transform({} {} {} {} {} {})", self.a, self.b, self.c, self.d, self.e, self.f)
}
}
#[derive(Clone, Copy, Debug)]
struct GlyphPoint {
x: i16,
y: i16,
on_curve_point: bool,
}
struct GlyphPoints<'a> {
flags: Stream<'a>,
x_coords: Stream<'a>,
y_coords: Stream<'a>,
points_left: u16,
flag_repeats: u8,
last_flags: SimpleGlyphFlags,
x: i16,
y: i16,
}
impl<'a> Iterator for GlyphPoints<'a> {
type Item = GlyphPoint;
fn next(&mut self) -> Option<Self::Item> {
type Flags = SimpleGlyphFlags;
if self.points_left == 0 {
return None;
}
if self.flag_repeats == 0 {
self.last_flags = Flags::from_bits_truncate(self.flags.read().ok()?);
if self.last_flags.contains(Flags::REPEAT_FLAG) {
self.flag_repeats = self.flags.read().ok()?;
}
} else {
self.flag_repeats -= 1;
}
let x = get_glyph_coord(
self.last_flags,
Flags::X_SHORT_VECTOR,
Flags::X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR,
&mut self.x_coords,
).ok()?;
self.x = self.x.wrapping_add(x);
let y = get_glyph_coord(
self.last_flags,
Flags::Y_SHORT_VECTOR,
Flags::Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR,
&mut self.y_coords,
).ok()?;
self.y = self.y.wrapping_add(y);
self.points_left -= 1;
Some(GlyphPoint {
x: self.x,
y: self.y,
on_curve_point: self.last_flags.contains(Flags::ON_CURVE_POINT),
})
}
}
fn get_glyph_coord(
flags: SimpleGlyphFlags,
short_vector: SimpleGlyphFlags,
is_same_or_positive_short_vector: SimpleGlyphFlags,
coords: &mut Stream,
) -> Result<i16> {
let flags = (
flags.contains(short_vector),
flags.contains(is_same_or_positive_short_vector),
);
Ok(match flags {
(true, true) => {
coords.read::<u8>()? as i16
}
(true, false) => {
-(coords.read::<u8>()? as i16)
}
(false, true) => {
0
}
(false, false) => {
coords.read()?
}
})
}
#[derive(Clone, Copy, Debug)]
struct Point {
x: f32,
y: f32,
}
impl Point {
#[inline]
fn lerp(&self, other: Point, t: f32) -> Point {
Point {
x: self.x + t * (other.x - self.x),
y: self.y + t * (other.y - self.y),
}
}
}