use super::{raw_tag, Array, Bytes, FromBeData, RawTag};
pub const MORX: RawTag = raw_tag(b"morx");
pub const LTAG: RawTag = raw_tag(b"ltag");
pub const KERX: RawTag = raw_tag(b"kerx");
pub const ANKR: RawTag = raw_tag(b"ankr");
pub const KERN: RawTag = raw_tag(b"kern");
const MAX_CYCLES: u16 = 16;
pub fn lookup<T: FromBeData>(data: &Bytes, offset: usize, id: u16) -> Option<T> {
let element_size = T::SIZE;
let fmt = data.read::<u16>(offset)?;
match fmt {
0 => {
return data.read::<T>(offset + 2 + id as usize * element_size);
}
2 => {
let reclen = (4 + element_size).max(6);
let nrecs = data.read::<u16>(offset + 4)? as usize;
let base = offset + 12;
let mut l = 0;
let mut h = nrecs;
while l < h {
let i = (l + h) / 2;
let rec = base + i * reclen;
let last = data.read::<u16>(rec)?;
if id > last {
l = i + 1;
} else if id < data.read::<u16>(rec + 2)? {
h = i;
} else {
return data.read::<T>(rec + 4);
}
}
}
4 => {
let reclen = 6;
let nrecs = data.read::<u16>(offset + 4)? as usize;
let base = offset + 12;
let mut l = 0;
let mut h = nrecs;
while l < h {
let i = (l + h) / 2;
let rec = base + i * reclen;
let first = data.read::<u16>(rec + 2)?;
let last = data.read::<u16>(rec)?;
if id > last {
l = i + 1;
} else if id < first {
h = i;
} else {
let index = (id - first) as usize;
let value_offset =
data.read::<u16>(rec + 4)? as usize + offset + index * element_size;
return data.read::<T>(value_offset);
}
}
}
6 => {
let reclen = (2 + element_size).max(4);
let nrecs = data.read::<u16>(offset + 4)? as usize;
let base = offset + 12;
let mut l = 0;
let mut h = nrecs;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let rec = base + i * reclen;
let glyph = data.read::<u16>(rec)?;
match id.cmp(&glyph) {
Greater => l = i + 1,
Less => h = i,
Equal => return data.read::<T>(rec + 2),
}
}
}
8 => {
let first = data.read::<u16>(offset + 2)?;
let count = data.read::<u16>(offset + 4)?;
if id < first {
return None;
}
let index = id - first;
if index >= count {
return None;
}
return data.read::<T>(offset + 6 + index as usize * element_size);
}
10 => {
let first = data.read::<u16>(offset + 4)?;
let count = data.read::<u16>(offset + 6)?;
if id < first {
return None;
}
let index = id - first;
if index >= count {
return None;
}
return data.read::<T>(offset + 8 + index as usize * element_size);
}
_ => {}
}
None
}
#[derive(Copy, Clone)]
pub struct ExtendedStateTable<'a> {
data: Bytes<'a>,
classes: u32,
class_table: u32,
state_array: u32,
entry_table: u32,
}
impl<'a> ExtendedStateTable<'a> {
pub fn new(data: &Bytes<'a>) -> Option<Self> {
Some(Self {
data: *data,
classes: data.read::<u32>(0)?,
class_table: data.read::<u32>(4)?,
state_array: data.read::<u32>(8)?,
entry_table: data.read::<u32>(12)?,
})
}
pub fn class(&self, glyph_id: u16) -> u16 {
if glyph_id == 0xFFFF {
return 2;
}
lookup::<u16>(&self.data, self.class_table as usize, glyph_id).unwrap_or(1)
}
pub fn entry<T: FromBeData>(&self, state: u16, class: u16) -> Option<Entry<T>> {
let mut offset = self.state_array as usize;
offset += state as usize * self.classes as usize * 2 + class as usize * 2;
let index = self.data.read::<u16>(offset)? as usize;
let entry_offset = index * (4 + T::SIZE);
self.data
.read::<Entry<T>>(self.entry_table as usize + entry_offset)
}
}
#[derive(Copy, Clone)]
pub struct StateTable<'a> {
data: Bytes<'a>,
class_count: u16,
state_array: u32,
states: &'a [u8],
entry_table: u32,
first_glyph: u16,
classes: &'a [u8],
}
impl<'a> StateTable<'a> {
pub fn new(data: &Bytes<'a>) -> Option<Self> {
let class_count = data.read_u16(0)?;
let class_offset = data.read_u16(2)? as usize;
let state_array = data.read_u16(4)? as u32;
let entry_table = data.read_u16(6)? as u32;
let first_glyph = data.read_u16(class_offset)?;
let glyph_count = data.read_u16(class_offset + 2)? as usize;
let classes = data.read_bytes(class_offset + 4, glyph_count)?;
let states = data.data().get(state_array as usize..)?;
Some(Self {
data: *data,
class_count,
state_array,
states,
entry_table,
first_glyph,
classes,
})
}
pub fn class(&self, glyph_id: u16) -> u16 {
if glyph_id == 0xFFFF {
return 2;
}
if let Some(index) = glyph_id.checked_sub(self.first_glyph) {
self.classes.get(index as usize).copied().unwrap_or(1) as u16
} else {
1
}
}
pub fn entry<T: FromBeData>(&self, state: u16, class: u16) -> Option<Entry<T>> {
let index = state as usize * self.class_count as usize + class as usize;
let entry_index = *self.states.get(index)? as usize;
let entry_offset = entry_index * (4 + T::SIZE);
let mut entry = self
.data
.read::<Entry<T>>(self.entry_table as usize + entry_offset)?;
let new_state =
(entry.new_state as u32).checked_sub(self.state_array)? / self.class_count as u32;
entry.new_state = new_state as u16;
Some(entry)
}
}
#[derive(Copy, Clone)]
pub struct Entry<T> {
pub new_state: u16,
pub flags: u16,
pub data: T,
}
impl<T: FromBeData> FromBeData for Entry<T> {
const SIZE: usize = 4 + T::SIZE;
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
let new_state = u16::from_be_data_unchecked(buf, offset);
let flags = u16::from_be_data_unchecked(buf, offset + 2);
let data = T::from_be_data_unchecked(buf, offset + 4);
Self {
new_state,
flags,
data,
}
}
}
pub mod morx {
use super::*;
pub fn chains<'a>(data: &'a [u8], offset: u32) -> Chains<'a> {
let data = Bytes::with_offset(data, offset as usize).unwrap_or_else(|| Bytes::new(&[]));
let len = data.read_u32(4).unwrap_or(0);
Chains {
data,
offset: 8,
len,
cur: 0,
}
}
#[derive(Copy, Clone)]
pub struct Chains<'a> {
data: Bytes<'a>,
offset: usize,
len: u32,
cur: u32,
}
impl<'a> Iterator for Chains<'a> {
type Item = Chain<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.len {
return None;
}
self.cur += 1;
let offset = self.offset;
let len = self.data.read_u32(offset + 4)? as usize;
self.offset += len;
let default_flags = self.data.read_u32(offset)?;
let feature_count = self.data.read_u32(offset + 8)?;
let subtable_count = self.data.read_u32(offset + 12)?;
Some(Chain {
data: self.data,
offset,
default_flags,
feature_count,
subtable_count,
})
}
}
#[derive(Copy, Clone)]
pub struct Chain<'a> {
data: Bytes<'a>,
offset: usize,
default_flags: u32,
feature_count: u32,
subtable_count: u32,
}
impl<'a> Chain<'a> {
pub fn default_flags(&self) -> u32 {
self.default_flags
}
pub fn features(&self) -> Features<'a> {
Features {
inner: *self,
cur: 0,
}
}
pub fn subtables(&self) -> Subtables<'a> {
let offset = self.offset + 16 + 12 * self.feature_count as usize;
Subtables {
inner: *self,
offset,
len: self.subtable_count,
cur: 0,
}
}
}
#[derive(Copy, Clone)]
pub struct Features<'a> {
inner: Chain<'a>,
cur: u32,
}
impl<'a> Iterator for Features<'a> {
type Item = Feature;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.inner.feature_count {
return None;
}
let index = self.cur;
self.cur += 1;
let offset = self.inner.offset + 16 + index as usize * 12;
let b = &self.inner.data;
Some(Feature {
selector: b.read::<u16>(offset)?,
setting_selector: b.read::<u16>(offset + 2)?,
enable_flags: b.read::<u32>(offset + 4)?,
disable_flags: b.read::<u32>(offset + 8)?,
})
}
}
#[derive(Copy, Clone)]
pub struct Feature {
pub selector: u16,
pub setting_selector: u16,
pub enable_flags: u32,
pub disable_flags: u32,
}
#[derive(Copy, Clone)]
pub struct Subtables<'a> {
inner: Chain<'a>,
offset: usize,
len: u32,
cur: u32,
}
impl<'a> Iterator for Subtables<'a> {
type Item = Subtable<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.len {
return None;
}
self.cur += 1;
let offset = self.offset;
let b = self.inner.data;
self.offset += b.read_u32(offset)? as usize;
let coverage = b.read_u32(offset + 4)?;
let kind = coverage & 0xFF;
let flags = b.read_u32(offset + 8)?;
Some(Subtable {
data: b,
offset,
kind,
coverage,
flags,
})
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Order {
Layout,
ReverseLayout,
Logical,
ReverseLogical,
}
#[derive(Copy, Clone)]
pub struct Subtable<'a> {
data: Bytes<'a>,
offset: usize,
kind: u32,
coverage: u32,
flags: u32,
}
impl<'a> Subtable<'a> {
pub fn coverage(&self) -> u32 {
self.coverage
}
pub fn flags(&self) -> u32 {
self.flags
}
pub fn order(&self) -> Order {
let order_bits = (self.coverage >> 28) & 0b101;
match order_bits {
0b000 => Order::Layout,
0b100 => Order::ReverseLayout,
0b001 => Order::Logical,
0b101 => Order::ReverseLogical,
_ => Order::Layout,
}
}
pub fn should_reverse(&self, is_rtl: bool) -> bool {
let order_bits = (self.coverage >> 28) & 0b101;
match order_bits {
0b000 => is_rtl,
0b100 => !is_rtl,
0b001 => false,
0b101 => true,
_ => false,
}
}
pub fn kind(&self) -> Option<SubtableKind<'a>> {
let data = Bytes::with_offset(self.data.data(), self.offset + 12)?;
Some(match self.kind {
0 => SubtableKind::Rearrangement(Rearrangement::new(data)?),
1 => SubtableKind::Contextual(Contextual::new(data)?),
2 => SubtableKind::Ligature(Ligature::new(data)?),
4 => SubtableKind::NonContextual(NonContextual::new(data)),
5 => SubtableKind::Insertion(Insertion::new(data)?),
_ => return None,
})
}
}
#[derive(Copy, Clone)]
pub enum SubtableKind<'a> {
Rearrangement(Rearrangement<'a>),
Contextual(Contextual<'a>),
Ligature(Ligature<'a>),
NonContextual(NonContextual<'a>),
Insertion(Insertion<'a>),
}
#[derive(Copy, Clone)]
pub struct Rearrangement<'a> {
state_table: ExtendedStateTable<'a>,
}
impl<'a> Rearrangement<'a> {
fn new(data: Bytes<'a>) -> Option<Self> {
Some(Self {
state_table: ExtendedStateTable::new(&data)?,
})
}
pub fn next(
&self,
state: &mut RearrangementState,
index: usize,
glyph_id: u16,
end_of_text: bool,
mut f: impl FnMut(&Rearrange) -> Option<()>,
) -> Option<usize> {
const MARK_FIRST: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
const MARK_LAST: u16 = 0x2000;
let class = if end_of_text {
0
} else {
self.state_table.class(glyph_id)
};
let entry = self.state_table.entry::<()>(state.state, class)?;
state.state = entry.new_state;
if entry.flags & MARK_FIRST != 0 {
state.first = index;
}
if entry.flags & MARK_LAST != 0 {
state.last = index;
}
let verb = entry.flags & 0xF;
let start = state.first;
let end = state.last;
if verb != 0 && start <= end {
let m = REARRANGEMENT_MAP[verb as usize & 0xF];
let l = 2.min(m >> 4) as usize;
let r = 2.min(m & 0x0F) as usize;
let reverse_l = 3 == (m >> 4);
let reverse_r = 3 == (m & 0x0F);
let rearrange = Rearrange {
l,
r,
reverse_l,
reverse_r,
start,
end,
};
f(&rearrange)?;
}
let mut advance = entry.flags & DONT_ADVANCE == 0;
if advance {
state.cycles = 0;
} else if state.cycles > MAX_CYCLES {
state.cycles = 0;
advance = true;
} else {
state.cycles += 1;
}
Some(advance as usize)
}
}
const REARRANGEMENT_MAP: [u8; 16] = [
0x00, 0x10, 0x01, 0x11, 0x20, 0x30, 0x02, 0x03, 0x12, 0x13, 0x21, 0x31, 0x22, 0x32, 0x23, 0x33, ];
#[derive(Copy, Clone, Default)]
pub struct RearrangementState {
state: u16,
first: usize,
last: usize,
cycles: u16,
}
impl RearrangementState {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Copy, Clone)]
pub struct Rearrange {
l: usize,
r: usize,
reverse_l: bool,
reverse_r: bool,
start: usize,
end: usize,
}
impl Rearrange {
pub fn apply<T: Copy + Default>(&self, buffer: &mut [T]) {
let l = self.l;
let r = self.r;
let reverse_l = self.reverse_l;
let reverse_r = self.reverse_r;
let start = self.start;
let end = (self.end + 1).min(buffer.len());
let mut tmp = [T::default(); 4];
if end - start >= l + r {
tmp[..l].copy_from_slice(&buffer[start..(start + l)]);
tmp[2..(2 + r)].copy_from_slice(&buffer[(end - r)..end]);
if l != r {
buffer.copy_within((start + l)..(end - r), start + r);
}
buffer[start..(r + start)].copy_from_slice(&tmp[2..(r + 2)]);
buffer[(end - l)..end].copy_from_slice(&tmp[..l]);
if reverse_l {
buffer.swap(end - 1, end - 2);
}
if reverse_r {
buffer.swap(start, start + 1);
}
}
}
}
#[derive(Copy, Clone)]
pub struct Contextual<'a> {
data: Bytes<'a>,
state_table: ExtendedStateTable<'a>,
table: u32,
}
impl<'a> Contextual<'a> {
fn new(data: Bytes<'a>) -> Option<Self> {
let table = data.read_u32(16)?;
Some(Self {
data,
state_table: ExtendedStateTable::new(&data)?,
table,
})
}
pub fn next(
&self,
state: &mut ContextualState,
index: usize,
glyph_id: u16,
end_of_text: bool,
mut f: impl FnMut(usize, u16) -> Option<()>,
) -> Option<()> {
const SET_MARK: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
if end_of_text && !state.mark_set {
return Some(());
}
let mut last_glyph_id = glyph_id;
let mut current_glyph_id = glyph_id;
let mut class = if end_of_text {
0
} else {
self.state_table.class(glyph_id)
};
if index == 0 && !end_of_text {
state.mark_index = 0;
state.mark_id = glyph_id;
}
let mut cycles = 0;
loop {
let entry = self
.state_table
.entry::<ContextualData>(state.state, class)?;
state.state = entry.new_state;
if entry.data.mark_index != 0xFFFF {
if let Some(g) = self.lookup(entry.data.mark_index, state.mark_id) {
f(state.mark_index, g)?;
if state.mark_index == index {
last_glyph_id = g;
current_glyph_id = g;
}
}
}
if entry.data.current_index != 0xFFFF {
if let Some(g) = self.lookup(entry.data.current_index, last_glyph_id) {
f(index, g)?;
current_glyph_id = g;
}
}
if entry.flags & SET_MARK != 0 {
state.mark_set = true;
state.mark_index = index;
state.mark_id = current_glyph_id;
}
if entry.flags & DONT_ADVANCE == 0 || cycles > MAX_CYCLES {
break;
}
cycles += 1;
class = self.state_table.class(current_glyph_id);
last_glyph_id = current_glyph_id;
}
Some(())
}
fn lookup(&self, table_index: u16, glyph_id: u16) -> Option<u16> {
let offset = self
.data
.read_u32(self.table as usize + table_index as usize * 4)?
as usize
+ self.table as usize;
lookup(&self.data, offset, glyph_id)
}
}
#[derive(Copy, Clone, Default)]
pub struct ContextualState {
state: u16,
mark_set: bool,
mark_index: usize,
mark_id: u16,
}
impl ContextualState {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Copy, Clone)]
struct ContextualData {
mark_index: u16,
current_index: u16,
}
impl FromBeData for ContextualData {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
let mark_index = u16::from_be_data_unchecked(buf, offset);
let current_index = u16::from_be_data_unchecked(buf, offset + 2);
Self {
mark_index,
current_index,
}
}
}
#[derive(Copy, Clone)]
pub struct Ligature<'a> {
data: Bytes<'a>,
state_table: ExtendedStateTable<'a>,
action: u32,
component: u32,
ligature: u32,
}
impl<'a> Ligature<'a> {
fn new(data: Bytes<'a>) -> Option<Self> {
let state_table = ExtendedStateTable::new(&data)?;
let action = data.read::<u32>(16)?;
let component = data.read::<u32>(20)?;
let ligature = data.read::<u32>(24)?;
Some(Self {
data,
state_table,
action,
component,
ligature,
})
}
pub fn next(
&self,
state: &mut LigatureState,
index: usize,
glyph_id: u16,
end_of_text: bool,
mut f: impl FnMut(usize, u16, &[usize]) -> Option<()>,
) -> Option<()> {
const SET_COMPONENT: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
const PERFORM_ACTION: u16 = 0x2000;
const LAST: u32 = 0x80000000;
const STORE: u32 = 0x40000000;
let class = if end_of_text {
0
} else {
self.state_table.class(glyph_id)
};
let mut cycles = 0;
loop {
let entry = self.state_table.entry::<u16>(state.state, class)?;
state.state = entry.new_state;
if entry.flags & SET_COMPONENT != 0 {
state.push(index, glyph_id)?;
}
if entry.flags & PERFORM_ACTION != 0 {
let mut action_index = entry.data;
let mut ligature_index = 0;
let end_pos = state.pos;
let mut pos = end_pos;
while pos > 0 {
pos -= 1;
let glyph_index = state.indices[pos];
let action = self.action(action_index)?;
action_index += 1;
let mut offset = action & 0x3FFFFFFF;
if offset & 0x20000000 != 0 {
offset |= 0xC0000000;
}
let offset = offset as i32;
let component_index = state.glyphs[pos] as i32 + offset;
let component = self.component(component_index as u32)?;
ligature_index += component as u32;
if action & (LAST | STORE) != 0 {
let ligature = self.ligature(ligature_index)?;
f(glyph_index, ligature, state.indices.get(pos + 1..end_pos)?)?;
state.glyphs[pos] = ligature;
pos += 1;
}
if action & LAST != 0 || cycles > (MAX_CYCLES * 2) {
break;
}
cycles += 1;
}
state.pos = pos;
}
if entry.flags & DONT_ADVANCE == 0 || cycles > MAX_CYCLES {
break;
}
cycles += 1;
}
Some(())
}
fn action(&self, index: u16) -> Option<u32> {
self.data
.read::<u32>(self.action as usize + index as usize * 4)
}
fn component(&self, index: u32) -> Option<u16> {
self.data
.read::<u16>(self.component as usize + index as usize * 2)
}
fn ligature(&self, index: u32) -> Option<u16> {
self.data
.read::<u16>(self.ligature as usize + index as usize * 2)
}
}
#[derive(Copy, Clone)]
pub struct LigatureState {
state: u16,
indices: [usize; 32],
glyphs: [u16; 32],
pos: usize,
}
impl LigatureState {
pub fn new() -> Self {
Self {
state: 0,
indices: [0; 32],
glyphs: [0; 32],
pos: 0,
}
}
fn push(&mut self, index: usize, glyph_id: u16) -> Option<()> {
*self.indices.get_mut(self.pos)? = index;
*self.glyphs.get_mut(self.pos)? = glyph_id;
self.pos += 1;
Some(())
}
}
#[derive(Copy, Clone)]
pub struct NonContextual<'a> {
data: Bytes<'a>,
}
impl<'a> NonContextual<'a> {
fn new(data: Bytes<'a>) -> Self {
Self { data }
}
pub fn substitute(&self, glyph_id: u16) -> Option<u16> {
lookup(&self.data, 0, glyph_id)
}
}
#[derive(Copy, Clone)]
pub struct Insertion<'a> {
data: Bytes<'a>,
state_table: ExtendedStateTable<'a>,
action: usize,
}
impl<'a> Insertion<'a> {
fn new(data: Bytes<'a>) -> Option<Self> {
let state_table = ExtendedStateTable::new(&data)?;
let action = data.read_u32(16)? as usize;
Some(Self {
data,
state_table,
action,
})
}
pub fn next(
&self,
state: &mut InsertionState,
index: usize,
glyph_id: u16,
end_of_text: bool,
mut f: impl FnMut(usize, Array<'a, u16>) -> Option<()>,
) -> Option<usize> {
const SET_MARK: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
const _CURRENT_IS_KASHIDA_LIKE: u16 = 0x2000;
const _MARKED_IS_KASHIDA_LIKE: u16 = 0x1000;
const CURRENT_INSERT_BEFORE: u16 = 0x800;
const MARKED_INSERT_BEFORE: u16 = 0x400;
let class = if end_of_text {
0
} else {
self.state_table.class(glyph_id)
};
let entry = self
.state_table
.entry::<InsertionData>(state.state, class)?;
state.state = entry.new_state;
let mut working_index = index;
let mut mark_inserted = 0;
if entry.data.mark_index != 0xFFFF {
let before = entry.flags & MARKED_INSERT_BEFORE != 0;
let base = if before { state.mark } else { state.mark + 1 };
let glyphs = self.marked_glyphs(entry.flags, entry.data.mark_index)?;
mark_inserted = glyphs.len();
working_index += mark_inserted;
f(base, glyphs)?;
}
if entry.flags & SET_MARK != 0 {
state.mark = index;
}
let mut current_inserted = 0;
if entry.data.current_index != 0xFFFF {
let before = entry.flags & CURRENT_INSERT_BEFORE != 0;
let base = if before {
working_index
} else {
working_index + 1
};
let glyphs = self.current_glyphs(entry.flags, entry.data.current_index)?;
current_inserted = glyphs.len();
f(base, glyphs)?;
}
let mut advance = entry.flags & DONT_ADVANCE == 0;
if advance {
state.cycles = 0;
} else if state.cycles > MAX_CYCLES {
state.cycles = 0;
advance = true;
} else {
state.cycles += 1;
}
if advance {
Some(mark_inserted + current_inserted + 1)
} else {
Some(mark_inserted)
}
}
fn marked_glyphs(&self, flags: u16, index: u16) -> Option<Array<'a, u16>> {
const MARKED_INSERT_COUNT: u16 = 0x1F;
let len = (flags & MARKED_INSERT_COUNT) as usize;
let offset = self.action + index as usize * 2;
self.data.read_array::<u16>(offset, len)
}
fn current_glyphs(&self, flags: u16, index: u16) -> Option<Array<'a, u16>> {
const CURRENT_INSERT_COUNT: u16 = 0x3E0;
let len = (flags & CURRENT_INSERT_COUNT) as usize >> 5;
let offset = self.action + index as usize * 2;
self.data.read_array::<u16>(offset, len)
}
}
#[derive(Copy, Clone)]
struct InsertionData {
current_index: u16,
mark_index: u16,
}
impl FromBeData for InsertionData {
unsafe fn from_be_data_unchecked(buf: &[u8], offset: usize) -> Self {
let current_index = u16::from_be_data_unchecked(buf, offset);
let mark_index = u16::from_be_data_unchecked(buf, offset + 2);
Self {
current_index,
mark_index,
}
}
}
#[derive(Copy, Clone, Default)]
pub struct InsertionState {
state: u16,
mark: usize,
cycles: u16,
}
impl InsertionState {
pub fn new() -> Self {
Self::default()
}
}
pub fn language_index(data: &[u8], ltag: u32, language: &str) -> Option<u32> {
let name = language.as_bytes();
let b = Bytes::with_offset(data, ltag as usize)?;
let count = b.read_u32(8)?;
let mut s = b.stream_at(12)?;
for i in 0..count {
let offset = s.read_u16()? as usize;
let len = s.read_u16()? as usize;
let bytes = b.read_bytes(offset, len)?;
if bytes == name {
return Some(i);
}
}
None
}
pub fn feature_from_tag(tag: RawTag) -> Option<(u16, [u16; 2])> {
Some(match AT_TO_AAT.binary_search_by(|e| e.0.cmp(&tag)) {
Ok(index) => {
let (_, feature, on, off) = AT_TO_AAT[index];
(feature as u16, [on as u16, off as u16])
}
_ => return None,
})
}
const AT_TO_AAT: [(u32, u8, u8, u8); 77] = [
(raw_tag(b"afrc"), 11, 1, 0),
(raw_tag(b"c2pc"), 38, 2, 0),
(raw_tag(b"c2sc"), 38, 1, 0),
(raw_tag(b"calt"), 36, 0, 1),
(raw_tag(b"case"), 33, 0, 1),
(raw_tag(b"clig"), 1, 18, 19),
(raw_tag(b"cpsp"), 33, 2, 3),
(raw_tag(b"cswh"), 36, 4, 5),
(raw_tag(b"dlig"), 1, 4, 5),
(raw_tag(b"expt"), 20, 10, 16),
(raw_tag(b"frac"), 11, 2, 0),
(raw_tag(b"fwid"), 22, 1, 7),
(raw_tag(b"halt"), 22, 6, 7),
(raw_tag(b"hist"), 1, 20, 21),
(raw_tag(b"hkna"), 34, 0, 1),
(raw_tag(b"hlig"), 1, 20, 21),
(raw_tag(b"hngl"), 23, 1, 0),
(raw_tag(b"hojo"), 20, 12, 16),
(raw_tag(b"hwid"), 22, 2, 7),
(raw_tag(b"ital"), 32, 2, 3),
(raw_tag(b"jp04"), 20, 11, 16),
(raw_tag(b"jp78"), 20, 2, 16),
(raw_tag(b"jp83"), 20, 3, 16),
(raw_tag(b"jp90"), 20, 4, 16),
(raw_tag(b"liga"), 1, 2, 3),
(raw_tag(b"lnum"), 21, 1, 2),
(raw_tag(b"mgrk"), 15, 10, 11),
(raw_tag(b"nlck"), 20, 13, 16),
(raw_tag(b"onum"), 21, 0, 2),
(raw_tag(b"ordn"), 10, 3, 0),
(raw_tag(b"palt"), 22, 5, 7),
(raw_tag(b"pcap"), 37, 2, 0),
(raw_tag(b"pkna"), 22, 0, 7),
(raw_tag(b"pnum"), 6, 1, 4),
(raw_tag(b"pwid"), 22, 0, 7),
(raw_tag(b"qwid"), 22, 4, 7),
(raw_tag(b"rlig"), 1, 0, 1),
(raw_tag(b"ruby"), 28, 2, 3),
(raw_tag(b"sinf"), 10, 4, 0),
(raw_tag(b"smcp"), 37, 1, 0),
(raw_tag(b"smpl"), 20, 1, 16),
(raw_tag(b"ss01"), 35, 2, 3),
(raw_tag(b"ss02"), 35, 4, 5),
(raw_tag(b"ss03"), 35, 6, 7),
(raw_tag(b"ss04"), 35, 8, 9),
(raw_tag(b"ss05"), 35, 10, 11),
(raw_tag(b"ss06"), 35, 12, 13),
(raw_tag(b"ss07"), 35, 14, 15),
(raw_tag(b"ss08"), 35, 16, 17),
(raw_tag(b"ss09"), 35, 18, 19),
(raw_tag(b"ss10"), 35, 20, 21),
(raw_tag(b"ss11"), 35, 22, 23),
(raw_tag(b"ss12"), 35, 24, 25),
(raw_tag(b"ss13"), 35, 26, 27),
(raw_tag(b"ss14"), 35, 28, 29),
(raw_tag(b"ss15"), 35, 30, 31),
(raw_tag(b"ss16"), 35, 32, 33),
(raw_tag(b"ss17"), 35, 34, 35),
(raw_tag(b"ss18"), 35, 36, 37),
(raw_tag(b"ss19"), 35, 38, 39),
(raw_tag(b"ss20"), 35, 40, 41),
(raw_tag(b"subs"), 10, 2, 0),
(raw_tag(b"sups"), 10, 1, 0),
(raw_tag(b"swsh"), 36, 2, 3),
(raw_tag(b"titl"), 19, 4, 0),
(raw_tag(b"tnam"), 20, 14, 16),
(raw_tag(b"tnum"), 6, 0, 4),
(raw_tag(b"trad"), 20, 0, 16),
(raw_tag(b"twid"), 22, 3, 7),
(raw_tag(b"unic"), 3, 14, 15),
(raw_tag(b"valt"), 22, 5, 7),
(raw_tag(b"vert"), 4, 0, 1),
(raw_tag(b"vhal"), 22, 6, 7),
(raw_tag(b"vkna"), 34, 2, 3),
(raw_tag(b"vpal"), 22, 5, 7),
(raw_tag(b"vrt2"), 4, 0, 1),
(raw_tag(b"zero"), 14, 4, 5),
];
}
pub mod kerx {
use super::*;
pub fn subtables<'a>(data: &'a [u8], kerx: u32, ankr: u32) -> Subtables<'a> {
let b = if kerx == 0 {
Bytes::new(&[])
} else {
Bytes::with_offset(data, kerx as usize).unwrap_or_else(|| Bytes::new(&[]))
};
let ankr = if ankr == 0 {
&[]
} else {
data.get(ankr as usize..).unwrap_or(&[])
};
let version = b.read_u16(0).unwrap_or(0);
let len = b.read_u32(4).unwrap_or(0);
Subtables {
data: b,
version,
offset: 8,
len,
cur: 0,
ankr,
}
}
#[derive(Copy, Clone)]
pub struct Subtables<'a> {
data: Bytes<'a>,
version: u16,
offset: usize,
len: u32,
cur: u32,
ankr: &'a [u8],
}
impl<'a> Iterator for Subtables<'a> {
type Item = Subtable<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.len {
return None;
}
self.cur += 1;
let offset = self.offset;
let subtable = Subtable::new(&self.data, offset, self.version, self.ankr)?;
self.offset = self.offset.checked_add(subtable.size as usize)?;
Some(subtable)
}
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub struct Subtable<'a> {
data: Bytes<'a>,
version: u16,
size: u32,
coverage: u32,
tuple_count: u32,
ankr: &'a [u8],
}
impl<'a> Subtable<'a> {
fn new(data: &Bytes<'a>, offset: usize, version: u16, ankr: &'a [u8]) -> Option<Self> {
let data = Bytes::with_offset(data.data(), offset)?;
let size = data.read_u32(0)?;
let coverage = data.read_u32(4)?;
let tuple_count = if version >= 4 { data.read_u32(8)? } else { 0 };
Some(Self {
data,
version,
size,
coverage,
tuple_count,
ankr,
})
}
pub fn is_vertical(&self) -> bool {
self.coverage & 0x80000000 != 0
}
pub fn is_cross_stream(&self) -> bool {
self.coverage & 0x40000000 != 0
}
pub fn has_variations(&self) -> bool {
self.coverage & 0x20000000 != 0
}
pub fn should_reverse(&self, is_rtl: bool) -> bool {
if self.coverage & 0x10000000 != 0 {
!is_rtl
} else {
is_rtl
}
}
pub fn kind(&self) -> Option<SubtableKind<'a>> {
let format = self.coverage & 0xFF;
Some(match format {
0 => SubtableKind::Format0(Format0::new(self.data)?),
1 => SubtableKind::Format1(Format1::new(self.data, self.tuple_count)?),
2 => SubtableKind::Format2(Format2::new(self.data)?),
4 => SubtableKind::Format4(Format4::new(self.data, self.ankr)?),
_ => return None,
})
}
}
#[derive(Copy, Clone)]
pub enum SubtableKind<'a> {
Format0(Format0<'a>),
Format1(Format1<'a>),
Format2(Format2<'a>),
Format4(Format4<'a>),
}
#[derive(Copy, Clone)]
pub struct Format0<'a> {
data: Bytes<'a>,
count: usize,
}
impl<'a> Format0<'a> {
fn new(data: Bytes<'a>) -> Option<Self> {
let count = data.read_u32(12)? as usize;
Some(Self { data, count })
}
pub fn get(&self, left: u16, right: u16) -> Option<i16> {
let key = (left as u32) << 16 | right as u32;
let base = 28;
let reclen = 6;
let b = &self.data;
let mut l = 0;
let mut h = self.count;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let pair = b.read::<u32>(base + i * reclen)?;
match key.cmp(&pair) {
Greater => l = i + 1,
Less => h = i,
Equal => return b.read_i16(base + i * reclen + 4),
}
}
None
}
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub struct Format1<'a> {
data: Bytes<'a>,
state_table: ExtendedStateTable<'a>,
value: usize,
tuple_count: u32,
tuple_size: usize,
}
impl<'a> Format1<'a> {
fn new(data: Bytes<'a>, tuple_count: u32) -> Option<Self> {
let data = Bytes::with_offset(data.data(), 12)?;
let state_table = ExtendedStateTable::new(&data)?;
let value = data.read_u32(16)? as usize;
let tuple_size = if tuple_count == 0 {
2
} else {
tuple_count as usize * 2
};
Some(Self {
data,
state_table,
value,
tuple_count,
tuple_size,
})
}
pub fn next(
&self,
state: &mut ContextualState,
index: usize,
glyph_id: u16,
mut f: impl FnMut(usize, i16) -> Option<()>,
) -> Option<usize> {
const PUSH: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
const RESET: u16 = 0x2000;
let class = self.state_table.class(glyph_id);
let entry = self.state_table.entry::<u16>(state.state, class)?;
state.state = entry.new_state;
if entry.flags & PUSH != 0 {
if state.pos == state.stack.len() {
return None;
}
state.stack[state.pos] = index;
state.pos += 1;
}
if entry.data != 0xFFFF {
let mut offset = self
.value
.checked_add(entry.data as usize)?
.checked_mul(self.tuple_size)?;
while state.pos > 0 {
let value = self.data.read_i16(offset)?;
if value as usize == 0xFFFF {
break;
}
let pos = state.pos - 1;
state.pos = pos;
f(state.stack[pos], value)?;
offset = offset.checked_add(self.tuple_size)?;
}
}
if entry.flags & RESET != 0 {
state.pos = 0;
}
let mut advance = entry.flags & DONT_ADVANCE == 0;
if advance {
state.cycles = 0;
} else if state.cycles > MAX_CYCLES {
state.cycles = 0;
advance = true;
} else {
state.cycles += 1;
}
Some(advance as usize)
}
}
#[derive(Copy, Clone, Default)]
pub struct ContextualState {
state: u16,
stack: [usize; 8],
pos: usize,
cycles: u16,
}
impl ContextualState {
pub fn new() -> Self {
Self::default()
}
}
#[derive(Copy, Clone)]
pub struct Format2<'a> {
data: Bytes<'a>,
l_table: usize,
r_table: usize,
array: usize,
}
impl<'a> Format2<'a> {
fn new(data: Bytes<'a>) -> Option<Self> {
let l_table = data.read_u32(16)? as usize;
let r_table = data.read_u32(20)? as usize;
let array = data.read_u32(24)? as usize;
Some(Self {
data,
l_table,
r_table,
array,
})
}
pub fn get(&self, left: u16, right: u16) -> Option<i16> {
let b = &self.data;
let row = lookup::<u16>(b, self.l_table, left)? as usize;
let column = lookup::<u16>(b, self.r_table, right)? as usize;
b.read_i16(self.array + row + column)
}
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub struct Format4<'a> {
data: Bytes<'a>,
state_table: ExtendedStateTable<'a>,
action_type: u32,
control_table: usize,
ankr: &'a [u8],
}
impl<'a> Format4<'a> {
fn new(data: Bytes<'a>, ankr: &'a [u8]) -> Option<Self> {
let data = Bytes::with_offset(data.data(), 12)?;
let state_table = ExtendedStateTable::new(&data)?;
let flags = data.read_u32(16)?;
let action_type = (flags & 0xC0000000) >> 30;
let control_table = (flags & 0x00FFFFFF) as usize;
Some(Self {
data,
state_table,
action_type,
control_table,
ankr,
})
}
pub fn next(
&self,
state: &mut Format4State,
index: usize,
glyph_id: u16,
mut f: impl FnMut(usize, usize, f32, f32) -> Option<()>,
) -> Option<usize> {
const MARK: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
let class = self.state_table.class(glyph_id);
let entry = self.state_table.entry::<u16>(state.state, class)?;
state.state = entry.new_state;
if entry.flags & MARK != 0 {
state.mark = index;
state.mark_id = glyph_id;
}
if entry.data != 0xFFFF {
let offset = self.control_table.checked_add(entry.data as usize * 2)?;
match self.action_type {
0 => {}
1 => {
let mark_index = self.data.read_u16(offset)?;
let cur_index = self.data.read_u16(offset + 2)?;
if let Some((x, y)) =
self.anchor_offset(mark_index, state.mark_id, cur_index, glyph_id)
{
let diff = index - state.mark;
if diff < 255 {
f(index, diff, x, y);
}
}
}
2 => {}
_ => {}
}
}
let mut advance = entry.flags & DONT_ADVANCE == 0;
if advance {
state.cycles = 0;
} else if state.cycles > MAX_CYCLES {
state.cycles = 0;
advance = true;
} else {
state.cycles += 1;
}
Some(advance as usize)
}
fn anchor_offset(
&self,
mark_index: u16,
mark_id: u16,
cur_index: u16,
cur_id: u16,
) -> Option<(f32, f32)> {
let mark_point = anchor_points(self.ankr, mark_id)?.get(mark_index as u32)?;
let cur_point = anchor_points(self.ankr, cur_id)?.get(cur_index as u32)?;
Some((
mark_point.0 as f32 - cur_point.0 as f32,
mark_point.1 as f32 - cur_point.1 as f32,
))
}
}
#[derive(Copy, Clone, Default)]
pub struct Format4State {
state: u16,
mark: usize,
mark_id: u16,
cycles: u16,
}
impl Format4State {
pub fn new() -> Self {
Self::default()
}
}
pub fn anchor_points<'a>(data: &'a [u8], glyph_id: u16) -> Option<AnchorPoints<'a>> {
if data.is_empty() {
return None;
}
let b = Bytes::new(data);
let lookup_offset = b.read_u32(4)? as usize;
let base = b.read_u32(8)? as usize;
let offset = lookup::<u16>(&b, lookup_offset, glyph_id)? as usize + base;
Some(AnchorPoints {
data: b,
offset,
len: b.read_u32(offset)?,
})
}
#[derive(Copy, Clone)]
pub struct AnchorPoints<'a> {
data: Bytes<'a>,
offset: usize,
len: u32,
}
impl<'a> AnchorPoints<'a> {
pub fn len(&self) -> u32 {
self.len
}
pub fn get(&self, index: u32) -> Option<(i16, i16)> {
let offset = self.offset + 4 + index as usize * 4;
let x = self.data.read::<i16>(offset)?;
let y = self.data.read::<i16>(offset + 2)?;
Some((x, y))
}
}
}
pub mod kern {
use super::*;
pub fn subtables<'a>(data: &'a [u8], kern: u32) -> Subtables<'a> {
let b = if kern == 0 {
Bytes::new(&[])
} else {
Bytes::with_offset(data, kern as usize).unwrap_or_else(|| Bytes::new(&[]))
};
let version = b.read_or_default::<u16>(0);
if version == 0 {
let len = b.read_or_default::<u16>(2) as u32;
Subtables {
data: b,
offset: 4,
len,
cur: 0,
is_aat: false,
}
} else {
let len = b.read_or_default::<u32>(4);
Subtables {
data: b,
offset: 8,
len,
cur: 0,
is_aat: true,
}
}
}
#[derive(Copy, Clone)]
pub struct Subtables<'a> {
data: Bytes<'a>,
offset: usize,
len: u32,
cur: u32,
is_aat: bool,
}
impl<'a> Iterator for Subtables<'a> {
type Item = Subtable<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.cur >= self.len {
return None;
}
self.cur += 1;
let offset = self.offset;
let subtable = Subtable::new(&self.data, offset, self.is_aat)?;
self.offset = self.offset.checked_add(subtable.size as usize)?;
Some(subtable)
}
}
#[derive(Copy, Clone)]
pub struct Subtable<'a> {
data: Bytes<'a>,
offset: usize,
size: u32,
coverage: u16,
is_aat: bool,
is_horizontal: bool,
cross_stream: bool,
format: u8,
}
impl<'a> Subtable<'a> {
fn new(data: &Bytes<'a>, mut offset: usize, is_aat: bool) -> Option<Self> {
let data = Bytes::with_offset(data.data(), offset)?;
let size = if is_aat {
offset = 8;
data.read_u32(0)?
} else {
offset = 6;
data.read_u16(2)? as u32
};
let coverage = data.read_u16(4)?;
let (is_horizontal, cross_stream, format) = if is_aat {
let format = (coverage & 0xFF) as u8;
let is_vertical = coverage & 0x8000 != 0;
let cross_stream = coverage & 0x4000 != 0;
(!is_vertical, cross_stream, format)
} else {
let format = (coverage & 0xFF00) >> 8;
let coverage = coverage & 0xFF;
let is_horizontal = coverage & (1 << 0) != 0;
let cross_stream = coverage & (1 << 2) != 0;
(is_horizontal, cross_stream, format as u8)
};
Some(Self {
data,
offset,
size,
coverage,
is_aat,
is_horizontal,
cross_stream,
format,
})
}
pub fn is_horizontal(&self) -> bool {
self.is_horizontal
}
pub fn cross_stream(&self) -> bool {
self.cross_stream
}
pub fn kind(&self) -> Option<SubtableKind<'a>> {
Some(match self.format {
0 => SubtableKind::Format0(Format0::new(self)?),
1 => SubtableKind::Format1(Format1::new(self)?),
_ => return None,
})
}
}
#[derive(Copy, Clone)]
pub enum SubtableKind<'a> {
Format0(Format0<'a>),
Format1(Format1<'a>),
}
#[derive(Copy, Clone)]
pub struct Format0<'a> {
data: Bytes<'a>,
offset: usize,
count: usize,
}
impl<'a> Format0<'a> {
fn new(subtable: &Subtable<'a>) -> Option<Self> {
let count = subtable.data.read_u16(subtable.offset)? as usize;
Some(Self {
data: subtable.data,
offset: subtable.offset + 8,
count,
})
}
pub fn get(&self, left: u16, right: u16) -> Option<i16> {
let key = (left as u32) << 16 | right as u32;
let base = self.offset;
let reclen = 6;
let b = &self.data;
let mut l = 0;
let mut h = self.count;
while l < h {
use core::cmp::Ordering::*;
let i = (l + h) / 2;
let pair = b.read::<u32>(base + i * reclen)?;
match key.cmp(&pair) {
Greater => l = i + 1,
Less => h = i,
Equal => return b.read_i16(base + i * reclen + 4),
}
}
None
}
}
#[derive(Copy, Clone)]
#[allow(dead_code)]
pub struct Format1<'a> {
data: Bytes<'a>,
state_table: StateTable<'a>,
cross_stream: bool,
}
impl<'a> Format1<'a> {
fn new(subtable: &Subtable<'a>) -> Option<Self> {
let data = Bytes::with_offset(subtable.data.data(), subtable.offset)?;
let state_table = StateTable::new(&data)?;
Some(Self {
data,
state_table,
cross_stream: subtable.cross_stream,
})
}
pub fn next(
&self,
state: &mut Format1State,
index: usize,
glyph_id: u16,
mut f: impl FnMut(usize, i16) -> Option<()>,
) -> Option<usize> {
const PUSH: u16 = 0x8000;
const DONT_ADVANCE: u16 = 0x4000;
let class = self.state_table.class(glyph_id);
let entry = self.state_table.entry::<()>(state.state, class)?;
state.state = entry.new_state;
if entry.flags & PUSH != 0 {
if state.pos == state.stack.len() {
return None;
}
state.stack[state.pos] = index;
state.pos += 1;
} else if entry.flags == 0 {
state.pos = 0;
}
let mut value_offset = (entry.flags & 0x3FFF) as usize;
if value_offset != 0 {
while state.pos > 0 {
let mut value = self.data.read_i16(value_offset)?;
let mut last = false;
if value & 1 != 0 {
last = true;
value &= !1;
}
let pos = state.pos - 1;
state.pos = pos;
if self.cross_stream && value as u16 == 0x8000 {
} else {
f(state.stack[pos], value)?;
}
if last {
state.pos = 0;
break;
}
value_offset += 2;
}
}
let mut advance = entry.flags & DONT_ADVANCE == 0;
if advance {
state.cycles = 0;
} else if state.cycles > MAX_CYCLES {
state.cycles = 0;
advance = true;
} else {
state.cycles += 1;
}
Some(advance as usize)
}
}
#[derive(Copy, Clone, Default)]
pub struct Format1State {
state: u16,
stack: [usize; 8],
pos: usize,
cycles: u16,
}
impl Format1State {
pub fn new() -> Self {
Self::default()
}
}
}