Skip to content

Commit

Permalink
Split VVAR and HVAR tables
Browse files Browse the repository at this point in the history
They are not identical.
  • Loading branch information
LaurenzV authored Jun 16, 2024
1 parent 46a0b9e commit df952ff
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 11 deletions.
34 changes: 28 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ pub use tables::CFFError;
#[cfg(feature = "apple-layout")]
pub use tables::{ankr, feat, kerx, morx, trak};
#[cfg(feature = "variable-fonts")]
pub use tables::{avar, cff2, fvar, gvar, hvar, mvar};
pub use tables::{avar, cff2, fvar, gvar, hvar, mvar, vvar};
pub use tables::{cbdt, cblc, cff1 as cff, vhea};
pub use tables::{
cmap, colr, cpal, glyf, head, hhea, hmtx, kern, loca, maxp, name, os2, post, sbix, svg, vorg,
Expand Down Expand Up @@ -991,7 +991,7 @@ pub struct FaceTables<'a> {
#[cfg(feature = "variable-fonts")]
pub mvar: Option<mvar::Table<'a>>,
#[cfg(feature = "variable-fonts")]
pub vvar: Option<hvar::Table<'a>>,
pub vvar: Option<vvar::Table<'a>>,
}

/// A font face.
Expand Down Expand Up @@ -1298,7 +1298,7 @@ impl<'a> Face<'a> {
#[cfg(feature = "variable-fonts")]
mvar: raw_tables.mvar.and_then(mvar::Table::parse),
#[cfg(feature = "variable-fonts")]
vvar: raw_tables.vvar.and_then(hvar::Table::parse),
vvar: raw_tables.vvar.and_then(vvar::Table::parse),
})
}

Expand Down Expand Up @@ -1914,7 +1914,7 @@ impl<'a> Face<'a> {
if self.is_variable() {
// Ignore variation offset when `hvar` is not set.
if let Some(hvar) = self.tables.hvar {
if let Some(offset) = hvar.side_bearing_offset(glyph_id, self.coords()) {
if let Some(offset) = hvar.left_side_bearing_offset(glyph_id, self.coords()) {
// We can't use `round()` in `no_std`, so this is the next best thing.
bearing += offset + 0.5;
}
Expand Down Expand Up @@ -1942,7 +1942,7 @@ impl<'a> Face<'a> {
if self.is_variable() {
// Ignore variation offset when `vvar` is not set.
if let Some(vvar) = self.tables.vvar {
if let Some(offset) = vvar.side_bearing_offset(glyph_id, self.coords()) {
if let Some(offset) = vvar.top_side_bearing_offset(glyph_id, self.coords()) {
// We can't use `round()` in `no_std`, so this is the next best thing.
bearing += offset + 0.5;
}
Expand All @@ -1960,8 +1960,30 @@ impl<'a> Face<'a> {

/// Returns glyph's vertical origin according to
/// [Vertical Origin Table](https://docs.microsoft.com/en-us/typography/opentype/spec/vorg).
///
/// This method is affected by variation axes.
pub fn glyph_y_origin(&self, glyph_id: GlyphId) -> Option<i16> {
self.tables.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))
#[cfg(feature = "variable-fonts")]
{
let mut origin = self.tables.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))? as f32;

if self.is_variable() {
// Ignore variation offset when `vvar` is not set.
if let Some(vvar) = self.tables.vvar {
if let Some(offset) = vvar.vertical_origin_offset(glyph_id, self.coords()) {
// We can't use `round()` in `no_std`, so this is the next best thing.
origin += offset + 0.5;
}
}
}

i16::try_num_from(origin)
}

#[cfg(not(feature = "variable-fonts"))]
{
self.tables.vorg.map(|vorg| vorg.glyph_y_origin(glyph_id))
}
}

/// Returns glyph's name.
Expand Down
32 changes: 27 additions & 5 deletions src/tables/hvar.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
//! A [Horizontal/Vertical Metrics Variations Table](
//! A [Horizontal Metrics Variations Table](
//! https://docs.microsoft.com/en-us/typography/opentype/spec/hvar) implementation.

use crate::delta_set::DeltaSetIndexMap;
use crate::parser::{Offset, Offset32, Stream};
use crate::var_store::ItemVariationStore;
use crate::{GlyphId, NormalizedCoordinate};

/// A [Horizontal/Vertical Metrics Variations Table](
/// A [Horizontal Metrics Variations Table](
/// https://docs.microsoft.com/en-us/typography/opentype/spec/hvar).
#[derive(Clone, Copy)]
pub struct Table<'a> {
data: &'a [u8],
variation_store: ItemVariationStore<'a>,
advance_width_mapping_offset: Option<Offset32>,
lsb_mapping_offset: Option<Offset32>,
rsb_mapping_offset: Option<Offset32>,
}

impl<'a> Table<'a> {
Expand All @@ -35,10 +36,11 @@ impl<'a> Table<'a> {
variation_store,
advance_width_mapping_offset: s.read::<Option<Offset32>>()?,
lsb_mapping_offset: s.read::<Option<Offset32>>()?,
rsb_mapping_offset: s.read::<Option<Offset32>>()?,
})
}

/// Returns advance offset for a glyph.
/// Returns the advance width offset for a glyph.
#[inline]
pub fn advance_offset(
&self,
Expand All @@ -59,14 +61,34 @@ impl<'a> Table<'a> {
.parse_delta(outer_idx, inner_idx, coordinates)
}

/// Returns side bearing offset for a glyph.
/// Returns the left side bearing offset for a glyph.
#[inline]
pub fn side_bearing_offset(
pub fn left_side_bearing_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
) -> Option<f32> {
let set_data = self.data.get(self.lsb_mapping_offset?.to_usize()..)?;
self.side_bearing_offset(glyph_id, coordinates, set_data)
}

/// Returns the right side bearing offset for a glyph.
#[inline]
pub fn right_side_bearing_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
) -> Option<f32> {
let set_data = self.data.get(self.rsb_mapping_offset?.to_usize()..)?;
self.side_bearing_offset(glyph_id, coordinates, set_data)
}

fn side_bearing_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
set_data: &[u8],
) -> Option<f32> {
let (outer_idx, inner_idx) = DeltaSetIndexMap::new(set_data).map(glyph_id.0 as u32)?;
self.variation_store
.parse_delta(outer_idx, inner_idx, coordinates)
Expand Down
2 changes: 2 additions & 0 deletions src/tables/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ pub mod gvar;
pub mod hvar;
#[cfg(feature = "variable-fonts")]
pub mod mvar;
#[cfg(feature = "variable-fonts")]
pub mod vvar;

pub use cff::cff1;
#[cfg(feature = "variable-fonts")]
Expand Down
115 changes: 115 additions & 0 deletions src/tables/vvar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//! A [Vertical Metrics Variations Table](
//! https://docs.microsoft.com/en-us/typography/opentype/spec/hvar) implementation.

use crate::delta_set::DeltaSetIndexMap;
use crate::parser::{Offset, Offset32, Stream};
use crate::var_store::ItemVariationStore;
use crate::{GlyphId, NormalizedCoordinate};

/// A [Vertical Metrics Variations Table](
/// https://docs.microsoft.com/en-us/typography/opentype/spec/hvar).
#[derive(Clone, Copy)]
pub struct Table<'a> {
data: &'a [u8],
variation_store: ItemVariationStore<'a>,
advance_height_mapping_offset: Option<Offset32>,
tsb_mapping_offset: Option<Offset32>,
bsb_mapping_offset: Option<Offset32>,
vorg_mapping_offset: Option<Offset32>,
}

impl<'a> Table<'a> {
/// Parses a table from raw data.
pub fn parse(data: &'a [u8]) -> Option<Self> {
let mut s = Stream::new(data);

let version = s.read::<u32>()?;
if version != 0x00010000 {
return None;
}

let variation_store_offset = s.read::<Offset32>()?;
let var_store_s = Stream::new_at(data, variation_store_offset.to_usize())?;
let variation_store = ItemVariationStore::parse(var_store_s)?;

Some(Table {
data,
variation_store,
advance_height_mapping_offset: s.read::<Option<Offset32>>()?,
tsb_mapping_offset: s.read::<Option<Offset32>>()?,
bsb_mapping_offset: s.read::<Option<Offset32>>()?,
vorg_mapping_offset: s.read::<Option<Offset32>>()?,
})
}

/// Returns the advance height offset for a glyph.
#[inline]
pub fn advance_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
) -> Option<f32> {
let (outer_idx, inner_idx) = if let Some(offset) = self.advance_height_mapping_offset {
DeltaSetIndexMap::new(self.data.get(offset.to_usize()..)?).map(glyph_id.0 as u32)?
} else {
// 'If there is no delta-set index mapping table for advance widths,
// then glyph IDs implicitly provide the indices:
// for a given glyph ID, the delta-set outer-level index is zero,
// and the glyph ID is the delta-set inner-level index.'
(0, glyph_id.0)
};

self.variation_store
.parse_delta(outer_idx, inner_idx, coordinates)
}

/// Returns the top side bearing offset for a glyph.
#[inline]
pub fn top_side_bearing_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
) -> Option<f32> {
let set_data = self.data.get(self.tsb_mapping_offset?.to_usize()..)?;
self.side_bearing_offset(glyph_id, coordinates, set_data)
}

/// Returns the bottom side bearing offset for a glyph.
#[inline]
pub fn bottom_side_bearing_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
) -> Option<f32> {
let set_data = self.data.get(self.bsb_mapping_offset?.to_usize()..)?;
self.side_bearing_offset(glyph_id, coordinates, set_data)
}

/// Returns the vertical origin offset for a glyph.
#[inline]
pub fn vertical_origin_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
) -> Option<f32> {
let set_data = self.data.get(self.vorg_mapping_offset?.to_usize()..)?;
self.side_bearing_offset(glyph_id, coordinates, set_data)
}

fn side_bearing_offset(
&self,
glyph_id: GlyphId,
coordinates: &[NormalizedCoordinate],
set_data: &[u8],
) -> Option<f32> {
let (outer_idx, inner_idx) = DeltaSetIndexMap::new(set_data).map(glyph_id.0 as u32)?;
self.variation_store
.parse_delta(outer_idx, inner_idx, coordinates)
}
}

impl core::fmt::Debug for Table<'_> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "Table {{ ... }}")
}
}

0 comments on commit df952ff

Please sign in to comment.