Skip to content

Commit

Permalink
Add morx table.
Browse files Browse the repository at this point in the history
  • Loading branch information
RazrFalcon committed Feb 19, 2022
1 parent fe79846 commit ff6eb20
Show file tree
Hide file tree
Showing 7 changed files with 608 additions and 4 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Expand Up @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## [Unreleased]
### Added
- `apple-layout` build feature.
- `ankr`, `feat` and `trak` tables.
- `ankr`, `feat`, `morx` and `trak` tables.

## [0.14.0] - 2021-12-28
### Changed
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Expand Up @@ -20,7 +20,7 @@ std = []
variable-fonts = []
# Enables GDEF, GPOS and GSUB tables.
opentype-layout = []
# Enables feat, trak tables.
# Enables ankr, feat, morx and trak tables.
apple-layout = []
# Enables glyph name query via `Face::glyph_name`.
# TrueType fonts do not store default glyph names, to reduce file size,
Expand Down
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -83,6 +83,7 @@ There are roughly three types of TrueType tables:
| `HVAR` table ||| |
| `kern` table | ~ (no AAT 1) | ~ (only 0) | ~ (only 0) |
| `maxp` table ||||
| `morx` table || | |
| `MVAR` table ||| |
| `name` table ||| |
| `OS/2` table ||| |
Expand Down
115 changes: 114 additions & 1 deletion src/aat.rs
Expand Up @@ -7,7 +7,120 @@ related types.
use core::num::NonZeroU16;

use crate::GlyphId;
use crate::parser::{Stream, FromData, LazyArray16};
use crate::parser::{Stream, FromData, LazyArray16, Offset, Offset32, NumFrom};

/// Predefined classes.
///
/// Search for _Class Code_ in [Apple Advanced Typography Font Tables](
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
pub mod class {
#![allow(missing_docs)]
pub const END_OF_TEXT: u16 = 0;
pub const OUT_OF_BOUNDS: u16 = 1;
pub const DELETED_GLYPH: u16 = 2;
}

/// An [Extended State Table](ExtendedStateTable) entry.
#[derive(Clone, Copy, Debug)]
pub struct ExtendedStateEntry<T: FromData> {
/// A new state.
pub new_state: u16,
/// Entry flags.
pub flags: u16,
/// Additional data.
///
/// Use `()` if no data expected.
pub extra: T,
}

impl<T: FromData> FromData for ExtendedStateEntry<T> {
const SIZE: usize = 4 + T::SIZE;

#[inline]
fn parse(data: &[u8]) -> Option<Self> {
let mut s = Stream::new(data);
Some(ExtendedStateEntry {
new_state: s.read::<u16>()?,
flags: s.read::<u16>()?,
extra: s.read::<T>()?,
})
}
}

/// An [Extended State Table](
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
///
/// Also called `STXHeader`.
///
/// Currently used by `kerx` and `morx` tables.
#[derive(Clone)]
pub struct ExtendedStateTable<'a, T> {
number_of_classes: u32,
lookup: Lookup<'a>,
state_array: &'a [u8],
entry_table: &'a [u8],
entry_type: core::marker::PhantomData<T>,
}

impl<'a, T: FromData> ExtendedStateTable<'a, T> {
// TODO: make private
/// Parses an Extended State Table from a stream.
///
/// `number_of_glyphs` is from the `maxp` table.
pub fn parse(number_of_glyphs: NonZeroU16, s: &mut Stream<'a>) -> Option<Self> {
let data = s.tail()?;

let number_of_classes = s.read::<u32>()?;
// Note that offsets are not from the subtable start,
// but from subtable start + `header_size`.
// So there is not need to subtract the `header_size`.
let lookup_table_offset = s.read::<Offset32>()?.to_usize();
let state_array_offset = s.read::<Offset32>()?.to_usize();
let entry_table_offset = s.read::<Offset32>()?.to_usize();

Some(ExtendedStateTable {
number_of_classes,
lookup: Lookup::parse(number_of_glyphs, data.get(lookup_table_offset..)?)?,
// We don't know the actual data size and it's kinda expensive to calculate.
// So we are simply storing all the data past the offset.
// Despite the fact that they may overlap.
state_array: data.get(state_array_offset..)?,
entry_table: data.get(entry_table_offset..)?,
entry_type: core::marker::PhantomData,
})
}

/// Returns a glyph class.
#[inline]
pub fn class(&self, glyph_id: GlyphId) -> Option<u16> {
if glyph_id.0 == 0xFFFF {
return Some(class::DELETED_GLYPH);
}

self.lookup.value(glyph_id)
}

/// Returns a class entry.
#[inline]
pub fn entry(&self, state: u16, mut class: u16) -> Option<ExtendedStateEntry<T>> {
if u32::from(class) >= self.number_of_classes {
class = class::OUT_OF_BOUNDS;
}

let state_idx =
usize::from(state) * usize::num_from(self.number_of_classes) + usize::from(class);

let entry_idx: u16 = Stream::read_at(self.state_array, state_idx * u16::SIZE)?;
Stream::read_at(self.entry_table, usize::from(entry_idx) * ExtendedStateEntry::<T>::SIZE)
}
}

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


/// A [lookup table](
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6Tables.html).
Expand Down
8 changes: 7 additions & 1 deletion src/lib.rs
Expand Up @@ -69,7 +69,7 @@ pub use tables::CFFError;
pub use tables::{cmap, kern, sbix, maxp, hmtx, name, os2, loca, svg, vorg, post, head, hhea, glyf};
pub use tables::{cff1 as cff, vhea, cbdt, cblc};
#[cfg(feature = "opentype-layout")] pub use tables::{gdef, gpos, gsub};
#[cfg(feature = "apple-layout")] pub use tables::{ankr, feat, trak};
#[cfg(feature = "apple-layout")] pub use tables::{ankr, feat, morx, trak};
#[cfg(feature = "variable-fonts")] pub use tables::{cff2, avar, fvar, gvar, hvar, mvar};

#[cfg(feature = "opentype-layout")]
Expand Down Expand Up @@ -572,6 +572,7 @@ pub struct RawFaceTables<'a> {

#[cfg(feature = "apple-layout")] pub ankr: Option<&'a [u8]>,
#[cfg(feature = "apple-layout")] pub feat: Option<&'a [u8]>,
#[cfg(feature = "apple-layout")] pub morx: Option<&'a [u8]>,
#[cfg(feature = "apple-layout")] pub trak: Option<&'a [u8]>,

#[cfg(feature = "variable-fonts")] pub avar: Option<&'a [u8]>,
Expand Down Expand Up @@ -620,6 +621,7 @@ pub struct FaceTables<'a> {

#[cfg(feature = "apple-layout")] pub ankr: Option<ankr::Table<'a>>,
#[cfg(feature = "apple-layout")] pub feat: Option<feat::Table<'a>>,
#[cfg(feature = "apple-layout")] pub morx: Option<morx::Table<'a>>,
#[cfg(feature = "apple-layout")] pub trak: Option<trak::Table<'a>>,

#[cfg(feature = "variable-fonts")] pub avar: Option<avar::Table<'a>>,
Expand Down Expand Up @@ -773,6 +775,8 @@ impl<'a> Face<'a> {
b"kern" => tables.kern = table_data,
b"loca" => tables.loca = table_data,
b"maxp" => tables.maxp = table_data.unwrap_or_default(),
#[cfg(feature = "apple-layout")]
b"morx" => tables.morx = table_data,
b"name" => tables.name = table_data,
b"post" => tables.post = table_data,
b"sbix" => tables.sbix = table_data,
Expand Down Expand Up @@ -866,6 +870,8 @@ impl<'a> Face<'a> {
#[cfg(feature = "apple-layout")] ankr: raw_tables.ankr
.and_then(|data| ankr::Table::parse(maxp.number_of_glyphs, data)),
#[cfg(feature = "apple-layout")] feat: raw_tables.feat.and_then(feat::Table::parse),
#[cfg(feature = "apple-layout")] morx: raw_tables.morx
.and_then(|data| morx::Table::parse(maxp.number_of_glyphs, data)),
#[cfg(feature = "apple-layout")] trak: raw_tables.trak.and_then(trak::Table::parse),

#[cfg(feature = "variable-fonts")] avar: raw_tables.avar.and_then(avar::Table::parse),
Expand Down
1 change: 1 addition & 0 deletions src/tables/mod.rs
Expand Up @@ -23,6 +23,7 @@ pub mod vorg;

#[cfg(feature = "apple-layout")] pub mod ankr;
#[cfg(feature = "apple-layout")] pub mod feat;
#[cfg(feature = "apple-layout")] pub mod morx;
#[cfg(feature = "apple-layout")] pub mod trak;

#[cfg(feature = "variable-fonts")] pub mod avar;
Expand Down

0 comments on commit ff6eb20

Please sign in to comment.