use crate::model::Error;
use crate::{
model::{
glyph::outline::{DrawOptions, DrawnMetrics, PathSink, UnscaledOutlineSink},
Font,
},
tables::{
glyf::Glyf,
glyf::{
outline::{
Buffers, OutlineContext, OutlinePlan, Scale, Scale26Dot6, ScaleF32, Unscaled,
},
PointCoord, PHANTOM_POINT_COUNT,
},
gvar::{GlyphVariationData, SelectedGvar},
hmtx::GlyphMetricRecords,
loca::{Loca, LocaGlyph},
},
types::{F2Dot14, Fixed, GlyphId, Point},
};
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub(crate) enum Precision {
Fixed,
Float,
}
#[derive(Clone)]
pub struct GlyfGlyph<'a> {
context: GlyfContext<'a>,
plan: OutlinePlan<'a>,
}
impl<'a> GlyfGlyph<'a> {
pub(crate) fn new(context: GlyfContext<'a>, glyph: GlyphId) -> Option<Self> {
let plan = OutlinePlan::new(&context, glyph).ok()?;
Some(Self { context, plan })
}
pub fn glyph(&self) -> GlyphId {
self.plan.glyph
}
pub fn font(&self) -> &'a Font {
self.context.font
}
pub fn plan(&self) -> &OutlinePlan<'a> {
&self.plan
}
pub fn required_memory_size(&self) -> usize {
plan_buffer_size(&self.plan)
}
pub(crate) fn draw(
&self,
options: &DrawOptions,
precision: Precision,
buf: &mut [u8],
sink: &mut dyn PathSink,
) -> Result<DrawnMetrics, Error> {
draw_plan(&self.plan, &self.context, options, precision, buf, sink)
}
pub fn required_memory_size_unscaled(&self) -> usize {
self.plan
.buffer_lengths::<Unscaled>()
.packed_len::<Unscaled>()
}
pub(crate) fn draw_unscaled(
&self,
buf: &mut [u8],
sink: &mut dyn UnscaledOutlineSink,
) -> Result<DrawnMetrics, Error> {
let lengths = self.plan.buffer_lengths::<Unscaled>();
let buffers = Buffers::from_bytes(buf, &lengths)?;
let outline = self.plan.load(&self.context, &Unscaled, buffers, None)?;
let (points, flags) = (outline.points(), outline.flags());
let mut start = 0;
for end in outline.contours() {
let end = *end as usize;
if end < start || end >= points.len() {
return Err(Error::Malformed);
}
sink.start_contour();
for (point, flags) in points[start..=end].iter().zip(&flags[start..=end]) {
sink.point(point.x, point.y, flags.without_markers());
}
sink.end_contour();
start = end + 1;
}
Ok(DrawnMetrics {
h_advance: Some(outline.adjusted_advance_width() as f32),
x_bearing: Some(outline.adjusted_lsb() as f32),
has_overlaps: self.plan.has_overlaps,
..Default::default()
})
}
}
pub(crate) fn plan_buffer_size(plan: &OutlinePlan<'_>) -> usize {
plan.buffer_lengths::<Scale26Dot6>()
.packed_len::<Scale26Dot6>()
}
pub(crate) fn draw_plan<'a>(
plan: &OutlinePlan<'a>,
context: &'a dyn OutlineContext<'a>,
options: &DrawOptions,
precision: Precision,
buf: &mut [u8],
sink: &mut dyn PathSink,
) -> Result<DrawnMetrics, Error> {
let units_per_em = context.units_per_em();
match precision {
Precision::Fixed => load_and_draw(
plan,
context,
&Scale26Dot6::new(options.ppem, units_per_em),
options,
buf,
sink,
),
Precision::Float => load_and_draw(
plan,
context,
&ScaleF32::new(options.ppem, units_per_em),
options,
buf,
sink,
),
}
}
fn load_and_draw<'a, S: Scale>(
plan: &OutlinePlan<'a>,
context: &'a dyn OutlineContext<'a>,
scale: &S,
options: &DrawOptions,
buf: &mut [u8],
mut sink: &mut dyn PathSink,
) -> Result<DrawnMetrics, Error> {
let lengths = plan.buffer_lengths::<S>();
let buffers = Buffers::from_bytes(buf, &lengths)?;
let outline = plan.load(context, scale, buffers, None)?;
let metrics = DrawnMetrics {
h_advance: Some(outline.adjusted_advance_width().to_f32()),
x_bearing: Some(outline.adjusted_lsb().to_f32()),
has_overlaps: plan.has_overlaps,
..Default::default()
};
if !outline.to_path(options.contour_start, &mut sink) {
return Err(Error::Malformed);
}
Ok(metrics)
}
#[derive(Clone, Copy)]
pub(crate) struct GlyfContext<'a> {
pub(crate) font: &'a Font,
tables: &'a (Glyf<'a>, Loca<'a>),
gvar: Option<&'a SelectedGvar<'a>>,
h_metrics: &'a GlyphMetricRecords<'a>,
v_metrics: &'a GlyphMetricRecords<'a>,
coords: &'a [F2Dot14],
gvar_scalars: &'a [Fixed],
h_line: (i32, i32),
num_glyphs: u32,
units_per_em: u16,
has_hvar: bool,
}
impl<'a> GlyfContext<'a> {
pub(crate) fn new(font: &'a Font) -> Option<Self> {
let tables = font.selected_glyf_loca()?;
let v_metrics = font.v_metric_records();
let h_line = if v_metrics.is_empty() {
OutlineContext::h_line_metrics(&font)
} else {
(0, 0)
};
Some(Self {
font,
tables,
gvar: font.selected_gvar(),
h_metrics: font.h_metric_records(),
v_metrics,
coords: font.normalized_coords(),
gvar_scalars: font.gvar_scalars(),
h_line,
num_glyphs: font.num_glyphs(),
units_per_em: font.units_per_em(),
has_hvar: font.hvar().is_some(),
})
}
fn metrics(&self, records: &GlyphMetricRecords<'_>, glyph: GlyphId) -> (i32, i32) {
(
records
.stated_side_bearing(glyph)
.unwrap_or_default()
.to_i32(),
records.stated_advance(self.num_glyphs, glyph).to_i32(),
)
}
}
impl<'a> OutlineContext<'a> for GlyfContext<'a> {
fn glyph(&self, glyph: GlyphId) -> Option<LocaGlyph<'a>> {
let (glyf, loca) = self.tables;
loca.get(glyph, glyf)
}
fn glyph_variation_data(&self, glyph: GlyphId) -> Option<GlyphVariationData<'a>> {
self.gvar?.glyph_variation_data(glyph).ok()?
}
fn has_gvar(&self) -> bool {
self.gvar.is_some()
}
fn phantom_point_deltas(&self, glyph: GlyphId) -> Option<[Point<Fixed>; PHANTOM_POINT_COUNT]> {
let (glyf, loca) = self.tables;
self.gvar?.phantom_point_deltas_with_scalars(
glyf,
loca,
self.coords,
self.gvar_scalars,
glyph,
)
}
fn gvar_scalars(&self) -> &[Fixed] {
self.gvar_scalars
}
fn coords(&self) -> &[F2Dot14] {
self.coords
}
fn units_per_em(&self) -> u16 {
self.units_per_em
}
fn h_metrics(&self, glyph: GlyphId) -> (i32, i32) {
self.metrics(self.h_metrics, glyph)
}
fn v_metrics(&self, glyph: GlyphId) -> Option<(i32, i32)> {
(!self.v_metrics.is_empty()).then(|| self.metrics(self.v_metrics, glyph))
}
fn h_line_metrics(&self) -> (i32, i32) {
self.h_line
}
fn has_hvar(&self) -> bool {
self.has_hvar
}
}
#[derive(Clone, Copy)]
pub(crate) struct FontAt<'a> {
pub(crate) context: GlyfContext<'a>,
pub(crate) coords: &'a [F2Dot14],
}
impl<'a> OutlineContext<'a> for FontAt<'a> {
fn glyph(&self, glyph: GlyphId) -> Option<LocaGlyph<'a>> {
self.context.glyph(glyph)
}
fn glyph_variation_data(&self, glyph: GlyphId) -> Option<GlyphVariationData<'a>> {
self.context.glyph_variation_data(glyph)
}
fn has_gvar(&self) -> bool {
self.context.has_gvar()
}
fn phantom_point_deltas(&self, glyph: GlyphId) -> Option<[Point<Fixed>; PHANTOM_POINT_COUNT]> {
let (glyf, loca) = self.context.tables;
self.context
.gvar?
.phantom_point_deltas_with_scalars(glyf, loca, self.coords, &[], glyph)
}
fn coords(&self) -> &[F2Dot14] {
self.coords
}
fn units_per_em(&self) -> u16 {
self.context.units_per_em
}
fn h_metrics(&self, glyph: GlyphId) -> (i32, i32) {
self.context.h_metrics(glyph)
}
fn v_metrics(&self, glyph: GlyphId) -> Option<(i32, i32)> {
self.context.v_metrics(glyph)
}
fn h_line_metrics(&self) -> (i32, i32) {
self.context.h_line
}
fn has_hvar(&self) -> bool {
self.context.has_hvar
}
}