608 lines
18 KiB
Rust
608 lines
18 KiB
Rust
//! A [Glyph Data Table](
|
|
//! https://docs.microsoft.com/en-us/typography/opentype/spec/glyf) implementation.
|
|
|
|
use core::num::NonZeroU16;
|
|
|
|
use crate::parser::{LazyArray16, NumFrom, Stream, F2DOT14};
|
|
use crate::{loca, BBox, GlyphId, OutlineBuilder, Rect, Transform};
|
|
|
|
pub(crate) struct Builder<'a> {
|
|
pub builder: &'a mut dyn OutlineBuilder,
|
|
pub transform: Transform,
|
|
is_default_ts: bool, // `bool` is faster than `Option` or `is_default`.
|
|
// We have to always calculate the bbox, because `gvar` doesn't store one
|
|
// and in case of a malformed bbox in `glyf`.
|
|
pub bbox: BBox,
|
|
first_on_curve: Option<Point>,
|
|
first_off_curve: Option<Point>,
|
|
last_off_curve: Option<Point>,
|
|
}
|
|
|
|
impl<'a> Builder<'a> {
|
|
#[inline]
|
|
pub fn new(transform: Transform, bbox: BBox, builder: &'a mut dyn OutlineBuilder) -> Self {
|
|
Builder {
|
|
builder,
|
|
transform,
|
|
is_default_ts: transform.is_default(),
|
|
bbox,
|
|
first_on_curve: None,
|
|
first_off_curve: None,
|
|
last_off_curve: None,
|
|
}
|
|
}
|
|
|
|
#[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.bbox.extend_by(x, 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.bbox.extend_by(x, 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.bbox.extend_by(x1, y1);
|
|
self.bbox.extend_by(x, y);
|
|
|
|
self.builder.quad_to(x1, y1, x, y);
|
|
}
|
|
|
|
// Useful links:
|
|
//
|
|
// - https://developer.apple.com/fonts/TrueType-Reference-Manual/RM01/Chap1.html
|
|
// - https://stackoverflow.com/a/20772557
|
|
#[inline]
|
|
pub fn push_point(&mut self, x: f32, y: f32, on_curve_point: bool, last_point: bool) {
|
|
let p = Point { x, y };
|
|
if self.first_on_curve.is_none() {
|
|
if on_curve_point {
|
|
self.first_on_curve = Some(p);
|
|
self.move_to(p.x, p.y);
|
|
} else {
|
|
if let Some(offcurve) = self.first_off_curve {
|
|
let mid = offcurve.lerp(p, 0.5);
|
|
self.first_on_curve = Some(mid);
|
|
self.last_off_curve = Some(p);
|
|
self.move_to(mid.x, mid.y);
|
|
} else {
|
|
self.first_off_curve = Some(p);
|
|
}
|
|
}
|
|
} else {
|
|
match (self.last_off_curve, on_curve_point) {
|
|
(Some(offcurve), true) => {
|
|
self.last_off_curve = None;
|
|
self.quad_to(offcurve.x, offcurve.y, p.x, p.y);
|
|
}
|
|
(Some(offcurve), false) => {
|
|
self.last_off_curve = Some(p);
|
|
let mid = offcurve.lerp(p, 0.5);
|
|
self.quad_to(offcurve.x, offcurve.y, mid.x, mid.y);
|
|
}
|
|
(None, true) => {
|
|
self.line_to(p.x, p.y);
|
|
}
|
|
(None, false) => {
|
|
self.last_off_curve = Some(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
if last_point {
|
|
self.finish_contour();
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn finish_contour(&mut self) {
|
|
if let (Some(offcurve1), Some(offcurve2)) = (self.first_off_curve, self.last_off_curve) {
|
|
self.last_off_curve = None;
|
|
let mid = offcurve2.lerp(offcurve1, 0.5);
|
|
self.quad_to(offcurve2.x, offcurve2.y, mid.x, mid.y);
|
|
}
|
|
|
|
if let (Some(p), Some(offcurve1)) = (self.first_on_curve, self.first_off_curve) {
|
|
self.quad_to(offcurve1.x, offcurve1.y, p.x, p.y);
|
|
} else if let (Some(p), Some(offcurve2)) = (self.first_on_curve, self.last_off_curve) {
|
|
self.quad_to(offcurve2.x, offcurve2.y, p.x, p.y);
|
|
} else if let Some(p) = self.first_on_curve {
|
|
self.line_to(p.x, p.y);
|
|
}
|
|
|
|
self.first_on_curve = None;
|
|
self.first_off_curve = None;
|
|
self.last_off_curve = None;
|
|
|
|
self.builder.close();
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub(crate) struct CompositeGlyphInfo {
|
|
pub glyph_id: GlyphId,
|
|
pub transform: Transform,
|
|
#[allow(dead_code)]
|
|
pub flags: CompositeGlyphFlags,
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub(crate) struct CompositeGlyphIter<'a> {
|
|
stream: Stream<'a>,
|
|
}
|
|
|
|
impl<'a> CompositeGlyphIter<'a> {
|
|
#[inline]
|
|
pub fn new(data: &'a [u8]) -> Self {
|
|
CompositeGlyphIter {
|
|
stream: Stream::new(data),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for CompositeGlyphIter<'a> {
|
|
type Item = CompositeGlyphInfo;
|
|
|
|
#[inline]
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let flags = CompositeGlyphFlags(self.stream.read::<u16>()?);
|
|
let glyph_id = self.stream.read::<GlyphId>()?;
|
|
|
|
let mut ts = Transform::default();
|
|
|
|
if flags.args_are_xy_values() {
|
|
if flags.arg_1_and_2_are_words() {
|
|
ts.e = f32::from(self.stream.read::<i16>()?);
|
|
ts.f = f32::from(self.stream.read::<i16>()?);
|
|
} else {
|
|
ts.e = f32::from(self.stream.read::<i8>()?);
|
|
ts.f = f32::from(self.stream.read::<i8>()?);
|
|
}
|
|
}
|
|
|
|
if flags.we_have_a_two_by_two() {
|
|
ts.a = self.stream.read::<F2DOT14>()?.to_f32();
|
|
ts.b = self.stream.read::<F2DOT14>()?.to_f32();
|
|
ts.c = self.stream.read::<F2DOT14>()?.to_f32();
|
|
ts.d = self.stream.read::<F2DOT14>()?.to_f32();
|
|
} else if flags.we_have_an_x_and_y_scale() {
|
|
ts.a = self.stream.read::<F2DOT14>()?.to_f32();
|
|
ts.d = self.stream.read::<F2DOT14>()?.to_f32();
|
|
} else if flags.we_have_a_scale() {
|
|
ts.a = self.stream.read::<F2DOT14>()?.to_f32();
|
|
ts.d = ts.a;
|
|
}
|
|
|
|
if !flags.more_components() {
|
|
// Finish the iterator even if stream still has some data.
|
|
self.stream.jump_to_end();
|
|
}
|
|
|
|
Some(CompositeGlyphInfo {
|
|
glyph_id,
|
|
transform: ts,
|
|
flags,
|
|
})
|
|
}
|
|
}
|
|
|
|
// Due to some optimization magic, using f32 instead of i16
|
|
// makes the code ~10% slower. At least on my machine.
|
|
// I guess it's due to the fact that with i16 the struct
|
|
// fits into the machine word.
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub(crate) struct GlyphPoint {
|
|
pub x: i16,
|
|
pub y: i16,
|
|
/// Indicates that a point is a point on curve
|
|
/// and not a control point.
|
|
pub on_curve_point: bool,
|
|
pub last_point: bool,
|
|
}
|
|
|
|
#[derive(Clone, Default)]
|
|
pub(crate) struct GlyphPointsIter<'a> {
|
|
endpoints: EndpointsIter<'a>,
|
|
flags: FlagsIter<'a>,
|
|
x_coords: CoordsIter<'a>,
|
|
y_coords: CoordsIter<'a>,
|
|
pub points_left: u16, // Number of points left in the glyph.
|
|
}
|
|
|
|
#[cfg(feature = "variable-fonts")]
|
|
impl GlyphPointsIter<'_> {
|
|
#[inline]
|
|
pub fn current_contour(&self) -> u16 {
|
|
self.endpoints.index - 1
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for GlyphPointsIter<'a> {
|
|
type Item = GlyphPoint;
|
|
|
|
#[inline]
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
self.points_left = self.points_left.checked_sub(1)?;
|
|
|
|
// TODO: skip empty contours
|
|
|
|
let last_point = self.endpoints.next();
|
|
let flags = self.flags.next()?;
|
|
Some(GlyphPoint {
|
|
x: self
|
|
.x_coords
|
|
.next(flags.x_short(), flags.x_is_same_or_positive_short()),
|
|
y: self
|
|
.y_coords
|
|
.next(flags.y_short(), flags.y_is_same_or_positive_short()),
|
|
on_curve_point: flags.on_curve_point(),
|
|
last_point,
|
|
})
|
|
}
|
|
}
|
|
|
|
/// A simple flattening iterator for glyph's endpoints.
|
|
///
|
|
/// Translates endpoints like: 2 4 7
|
|
/// into flags: 0 0 1 0 1 0 0 1
|
|
#[derive(Clone, Copy, Default)]
|
|
struct EndpointsIter<'a> {
|
|
endpoints: LazyArray16<'a, u16>, // Each endpoint indicates a contour end.
|
|
index: u16,
|
|
left: u16,
|
|
}
|
|
|
|
impl<'a> EndpointsIter<'a> {
|
|
#[inline]
|
|
fn new(endpoints: LazyArray16<'a, u16>) -> Option<Self> {
|
|
Some(EndpointsIter {
|
|
endpoints,
|
|
index: 1,
|
|
left: endpoints.get(0)?,
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
fn next(&mut self) -> bool {
|
|
if self.left == 0 {
|
|
if let Some(end) = self.endpoints.get(self.index) {
|
|
let prev = self.endpoints.get(self.index - 1).unwrap_or(0);
|
|
// Malformed font can have endpoints not in increasing order,
|
|
// so we have to use checked_sub.
|
|
self.left = end.saturating_sub(prev);
|
|
self.left = self.left.saturating_sub(1);
|
|
}
|
|
|
|
// Always advance the index, so we can check the current contour number.
|
|
if let Some(n) = self.index.checked_add(1) {
|
|
self.index = n;
|
|
}
|
|
|
|
true
|
|
} else {
|
|
self.left -= 1;
|
|
false
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default)]
|
|
struct FlagsIter<'a> {
|
|
stream: Stream<'a>,
|
|
// Number of times the `flags` should be used
|
|
// before reading the next one from `stream`.
|
|
repeats: u8,
|
|
flags: SimpleGlyphFlags,
|
|
}
|
|
|
|
impl<'a> FlagsIter<'a> {
|
|
#[inline]
|
|
fn new(data: &'a [u8]) -> Self {
|
|
FlagsIter {
|
|
stream: Stream::new(data),
|
|
repeats: 0,
|
|
flags: SimpleGlyphFlags(0),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for FlagsIter<'a> {
|
|
type Item = SimpleGlyphFlags;
|
|
|
|
#[inline]
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.repeats == 0 {
|
|
self.flags = SimpleGlyphFlags(self.stream.read::<u8>().unwrap_or(0));
|
|
if self.flags.repeat_flag() {
|
|
self.repeats = self.stream.read::<u8>().unwrap_or(0);
|
|
}
|
|
} else {
|
|
self.repeats -= 1;
|
|
}
|
|
|
|
Some(self.flags)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Default)]
|
|
struct CoordsIter<'a> {
|
|
stream: Stream<'a>,
|
|
prev: i16, // Points are stored as deltas, so we have to keep the previous one.
|
|
}
|
|
|
|
impl<'a> CoordsIter<'a> {
|
|
#[inline]
|
|
fn new(data: &'a [u8]) -> Self {
|
|
CoordsIter {
|
|
stream: Stream::new(data),
|
|
prev: 0,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn next(&mut self, is_short: bool, is_same_or_short: bool) -> i16 {
|
|
// See https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description
|
|
// for details about Simple Glyph Flags processing.
|
|
|
|
// We've already checked the coords data, so it's safe to fallback to 0.
|
|
|
|
let mut n = 0;
|
|
if is_short {
|
|
n = i16::from(self.stream.read::<u8>().unwrap_or(0));
|
|
if !is_same_or_short {
|
|
n = -n;
|
|
}
|
|
} else if !is_same_or_short {
|
|
n = self.stream.read::<i16>().unwrap_or(0);
|
|
}
|
|
|
|
self.prev = self.prev.wrapping_add(n);
|
|
self.prev
|
|
}
|
|
}
|
|
|
|
#[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),
|
|
}
|
|
}
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#simple-glyph-description
|
|
#[derive(Clone, Copy, Default)]
|
|
struct SimpleGlyphFlags(u8);
|
|
|
|
#[rustfmt::skip]
|
|
impl SimpleGlyphFlags {
|
|
#[inline] fn on_curve_point(self) -> bool { self.0 & 0x01 != 0 }
|
|
#[inline] fn x_short(self) -> bool { self.0 & 0x02 != 0 }
|
|
#[inline] fn y_short(self) -> bool { self.0 & 0x04 != 0 }
|
|
#[inline] fn repeat_flag(self) -> bool { self.0 & 0x08 != 0 }
|
|
#[inline] fn x_is_same_or_positive_short(self) -> bool { self.0 & 0x10 != 0 }
|
|
#[inline] fn y_is_same_or_positive_short(self) -> bool { self.0 & 0x20 != 0 }
|
|
}
|
|
|
|
// https://docs.microsoft.com/en-us/typography/opentype/spec/glyf#composite-glyph-description
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub(crate) struct CompositeGlyphFlags(u16);
|
|
|
|
#[rustfmt::skip]
|
|
impl CompositeGlyphFlags {
|
|
#[inline] pub fn arg_1_and_2_are_words(self) -> bool { self.0 & 0x0001 != 0 }
|
|
#[inline] pub fn args_are_xy_values(self) -> bool { self.0 & 0x0002 != 0 }
|
|
#[inline] pub fn we_have_a_scale(self) -> bool { self.0 & 0x0008 != 0 }
|
|
#[inline] pub fn more_components(self) -> bool { self.0 & 0x0020 != 0 }
|
|
#[inline] pub fn we_have_an_x_and_y_scale(self) -> bool { self.0 & 0x0040 != 0 }
|
|
#[inline] pub fn we_have_a_two_by_two(self) -> bool { self.0 & 0x0080 != 0 }
|
|
}
|
|
|
|
// It's not defined in the spec, so we are using our own value.
|
|
pub(crate) const MAX_COMPONENTS: u8 = 32;
|
|
|
|
#[allow(clippy::comparison_chain)]
|
|
#[inline]
|
|
fn outline_impl(
|
|
loca_table: loca::Table,
|
|
glyf_table: &[u8],
|
|
data: &[u8],
|
|
depth: u8,
|
|
builder: &mut Builder,
|
|
) -> Option<Option<Rect>> {
|
|
if depth >= MAX_COMPONENTS {
|
|
return None;
|
|
}
|
|
|
|
let mut s = Stream::new(data);
|
|
let number_of_contours = s.read::<i16>()?;
|
|
s.advance(8); // Skip bbox. We use calculated one.
|
|
|
|
if number_of_contours > 0 {
|
|
// Simple glyph.
|
|
|
|
// u16 casting is safe, since we already checked that the value is positive.
|
|
let number_of_contours = NonZeroU16::new(number_of_contours as u16)?;
|
|
for point in parse_simple_outline(s.tail()?, number_of_contours)? {
|
|
builder.push_point(
|
|
f32::from(point.x),
|
|
f32::from(point.y),
|
|
point.on_curve_point,
|
|
point.last_point,
|
|
);
|
|
}
|
|
} else if number_of_contours < 0 {
|
|
// Composite glyph.
|
|
for comp in CompositeGlyphIter::new(s.tail()?) {
|
|
if let Some(range) = loca_table.glyph_range(comp.glyph_id) {
|
|
if let Some(glyph_data) = glyf_table.get(range) {
|
|
let transform = Transform::combine(builder.transform, comp.transform);
|
|
let mut b = Builder::new(transform, builder.bbox, builder.builder);
|
|
outline_impl(loca_table, glyf_table, glyph_data, depth + 1, &mut b)?;
|
|
|
|
// Take updated bbox.
|
|
builder.bbox = b.bbox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if builder.bbox.is_default() {
|
|
return Some(None);
|
|
}
|
|
|
|
Some(builder.bbox.to_rect())
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn parse_simple_outline(
|
|
glyph_data: &[u8],
|
|
number_of_contours: NonZeroU16,
|
|
) -> Option<GlyphPointsIter> {
|
|
let mut s = Stream::new(glyph_data);
|
|
let endpoints = s.read_array16::<u16>(number_of_contours.get())?;
|
|
|
|
let points_total = endpoints.last()?.checked_add(1)?;
|
|
|
|
// Contours with a single point should be ignored.
|
|
// But this is not an error, so we should return an "empty" iterator.
|
|
if points_total == 1 {
|
|
return Some(GlyphPointsIter::default());
|
|
}
|
|
|
|
// Skip instructions byte code.
|
|
let instructions_len = s.read::<u16>()?;
|
|
s.advance(usize::from(instructions_len));
|
|
|
|
let flags_offset = s.offset();
|
|
let (x_coords_len, y_coords_len) = resolve_coords_len(&mut s, points_total)?;
|
|
let x_coords_offset = s.offset();
|
|
let y_coords_offset = x_coords_offset + usize::num_from(x_coords_len);
|
|
let y_coords_end = y_coords_offset + usize::num_from(y_coords_len);
|
|
|
|
Some(GlyphPointsIter {
|
|
endpoints: EndpointsIter::new(endpoints)?,
|
|
flags: FlagsIter::new(glyph_data.get(flags_offset..x_coords_offset)?),
|
|
x_coords: CoordsIter::new(glyph_data.get(x_coords_offset..y_coords_offset)?),
|
|
y_coords: CoordsIter::new(glyph_data.get(y_coords_offset..y_coords_end)?),
|
|
points_left: points_total,
|
|
})
|
|
}
|
|
|
|
/// Resolves coordinate arrays length.
|
|
///
|
|
/// The length depends on *Simple Glyph Flags*, so we have to process them all to find it.
|
|
fn resolve_coords_len(s: &mut Stream, points_total: u16) -> Option<(u32, u32)> {
|
|
let mut flags_left = u32::from(points_total);
|
|
let mut repeats;
|
|
let mut x_coords_len = 0;
|
|
let mut y_coords_len = 0;
|
|
while flags_left > 0 {
|
|
let flags = SimpleGlyphFlags(s.read::<u8>()?);
|
|
|
|
// The number of times a glyph point repeats.
|
|
repeats = if flags.repeat_flag() {
|
|
let repeats = s.read::<u8>()?;
|
|
u32::from(repeats) + 1
|
|
} else {
|
|
1
|
|
};
|
|
|
|
if repeats > flags_left {
|
|
return None;
|
|
}
|
|
|
|
// No need to check for `*_coords_len` overflow since u32 is more than enough.
|
|
|
|
// Non-obfuscated code below.
|
|
// Branchless version is surprisingly faster.
|
|
//
|
|
// if flags.x_short() {
|
|
// // Coordinate is 1 byte long.
|
|
// x_coords_len += repeats;
|
|
// } else if !flags.x_is_same_or_positive_short() {
|
|
// // Coordinate is 2 bytes long.
|
|
// x_coords_len += repeats * 2;
|
|
// }
|
|
// if flags.y_short() {
|
|
// // Coordinate is 1 byte long.
|
|
// y_coords_len += repeats;
|
|
// } else if !flags.y_is_same_or_positive_short() {
|
|
// // Coordinate is 2 bytes long.
|
|
// y_coords_len += repeats * 2;
|
|
// }
|
|
|
|
x_coords_len += (flags.0 & 0x02 != 0) as u32 * repeats;
|
|
x_coords_len += (flags.0 & (0x02 | 0x10) == 0) as u32 * (repeats * 2);
|
|
|
|
y_coords_len += (flags.0 & 0x04 != 0) as u32 * repeats;
|
|
y_coords_len += (flags.0 & (0x04 | 0x20) == 0) as u32 * (repeats * 2);
|
|
|
|
flags_left -= repeats;
|
|
}
|
|
|
|
Some((x_coords_len, y_coords_len))
|
|
}
|
|
|
|
/// A [Glyph Data Table](
|
|
/// https://docs.microsoft.com/en-us/typography/opentype/spec/glyf).
|
|
#[derive(Clone, Copy)]
|
|
pub struct Table<'a> {
|
|
pub(crate) data: &'a [u8],
|
|
loca_table: loca::Table<'a>,
|
|
}
|
|
|
|
impl core::fmt::Debug for Table<'_> {
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
write!(f, "Table {{ ... }}")
|
|
}
|
|
}
|
|
|
|
impl<'a> Table<'a> {
|
|
/// Parses a table from raw data.
|
|
#[inline]
|
|
pub fn parse(loca_table: loca::Table<'a>, data: &'a [u8]) -> Option<Self> {
|
|
Some(Table { loca_table, data })
|
|
}
|
|
|
|
/// Outlines a glyph.
|
|
#[inline]
|
|
pub fn outline(&self, glyph_id: GlyphId, builder: &mut dyn OutlineBuilder) -> Option<Rect> {
|
|
let mut b = Builder::new(Transform::default(), BBox::new(), builder);
|
|
let glyph_data = self.get(glyph_id)?;
|
|
outline_impl(self.loca_table, self.data, glyph_data, 0, &mut b)?
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn get(&self, glyph_id: GlyphId) -> Option<&'a [u8]> {
|
|
let range = self.loca_table.glyph_range(glyph_id)?;
|
|
self.data.get(range)
|
|
}
|
|
}
|