-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
8ddb549
commit d8e5b2d
Showing
8 changed files
with
281 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
//! A [Feature Name Table]( | ||
//! https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html) implementation. | ||
|
||
use crate::parser::{FromData, LazyArray16, Offset, Offset32, Stream}; | ||
|
||
#[derive(Clone, Copy, Debug)] | ||
struct FeatureNameRecord { | ||
feature: u16, | ||
setting_table_records_count: u16, | ||
// Offset from the beginning of the table. | ||
setting_table_offset: Offset32, | ||
flags: u8, | ||
default_setting_index: u8, | ||
name_index: u16, | ||
} | ||
|
||
impl FromData for FeatureNameRecord { | ||
const SIZE: usize = 12; | ||
|
||
#[inline] | ||
fn parse(data: &[u8]) -> Option<Self> { | ||
let mut s = Stream::new(data); | ||
Some(FeatureNameRecord { | ||
feature: s.read::<u16>()?, | ||
setting_table_records_count: s.read::<u16>()?, | ||
setting_table_offset: s.read::<Offset32>()?, | ||
flags: s.read::<u8>()?, | ||
default_setting_index: s.read::<u8>()?, | ||
name_index: s.read::<u16>()?, | ||
}) | ||
} | ||
} | ||
|
||
|
||
/// A setting name. | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct SettingName { | ||
/// The setting. | ||
pub setting: u16, | ||
/// The `name` table index for the feature's name in a 256..32768 range. | ||
pub name_index: u16, | ||
} | ||
|
||
impl FromData for SettingName { | ||
const SIZE: usize = 4; | ||
|
||
#[inline] | ||
fn parse(data: &[u8]) -> Option<Self> { | ||
let mut s = Stream::new(data); | ||
Some(SettingName { | ||
setting: s.read::<u16>()?, | ||
name_index: s.read::<u16>()?, | ||
}) | ||
} | ||
} | ||
|
||
|
||
/// A feature names. | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct FeatureName<'a> { | ||
/// The feature's ID. | ||
pub feature: u16, | ||
/// The feature's setting names. | ||
pub setting_names: LazyArray16<'a, SettingName>, | ||
/// The index of the default setting in the `setting_names`. | ||
pub default_setting_index: u8, | ||
/// The feature's exclusive settings. If set, the feature settings are mutually exclusive. | ||
pub exclusive: bool, | ||
/// The `name` table index for the feature's name in a 256..32768 range. | ||
pub name_index: u16, | ||
} | ||
|
||
|
||
/// A list fo feature names. | ||
#[derive(Clone, Copy)] | ||
pub struct FeatureNames<'a> { | ||
data: &'a [u8], | ||
records: LazyArray16<'a, FeatureNameRecord>, | ||
} | ||
|
||
impl<'a> FeatureNames<'a> { | ||
/// Returns a feature name at an index. | ||
pub fn get(&self, index: u16) -> Option<FeatureName<'a>> { | ||
let record = self.records.get(index)?; | ||
let data = self.data.get(record.setting_table_offset.to_usize()..)?; | ||
let mut s = Stream::new(data); | ||
let setting_names = s.read_array16::<SettingName>(record.setting_table_records_count)?; | ||
Some(FeatureName { | ||
feature: record.feature, | ||
setting_names, | ||
default_setting_index: | ||
if record.flags & 0x40 != 0 { record.default_setting_index } else { 0 }, | ||
exclusive: record.flags & 0x80 != 0, | ||
name_index: record.name_index, | ||
}) | ||
} | ||
|
||
/// Finds a feature name by ID. | ||
pub fn find(&self, feature: u16) -> Option<FeatureName<'a>> { | ||
let index = self.records | ||
.binary_search_by(|name| name.feature.cmp(&feature)).map(|(i, _)| i)?; | ||
self.get(index) | ||
} | ||
|
||
/// Returns the number of feature names. | ||
pub fn len(&self) -> u16 { | ||
self.records.len() | ||
} | ||
} | ||
|
||
impl<'a> core::fmt::Debug for FeatureNames<'a> { | ||
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { | ||
f.debug_list().entries(self.into_iter()).finish() | ||
} | ||
} | ||
|
||
impl<'a> IntoIterator for FeatureNames<'a> { | ||
type Item = FeatureName<'a>; | ||
type IntoIter = FeatureNamesIter<'a>; | ||
|
||
#[inline] | ||
fn into_iter(self) -> Self::IntoIter { | ||
FeatureNamesIter { | ||
names: self, | ||
index: 0, | ||
} | ||
} | ||
} | ||
|
||
/// An iterator over [`FeatureNames`]. | ||
#[allow(missing_debug_implementations)] | ||
pub struct FeatureNamesIter<'a> { | ||
names: FeatureNames<'a>, | ||
index: u16, | ||
} | ||
|
||
impl<'a> Iterator for FeatureNamesIter<'a> { | ||
type Item = FeatureName<'a>; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
if self.index < self.names.len() { | ||
self.index += 1; | ||
self.names.get(self.index - 1) | ||
} else { | ||
None | ||
} | ||
} | ||
} | ||
|
||
|
||
/// A [Feature Name Table]( | ||
/// https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6feat.html). | ||
#[derive(Clone, Copy, Debug)] | ||
pub struct Table<'a> { | ||
/// A list of feature names. Sorted by `FeatureName.feature`. | ||
pub names: FeatureNames<'a>, | ||
} | ||
|
||
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 count = s.read::<u16>()?; | ||
s.advance_checked(6)?; // reserved | ||
let records = s.read_array16::<FeatureNameRecord>(count)?; | ||
|
||
Some(Table { | ||
names: FeatureNames { | ||
data, | ||
records, | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
use ttf_parser::feat::Table; | ||
|
||
#[test] | ||
fn basic() { | ||
let data = &[ | ||
0x00, 0x01, 0x00, 0x00, // version: 1 | ||
0x00, 0x04, // number of features: 4 | ||
0x00, 0x00, // reserved | ||
0x00, 0x00, 0x00, 0x00, // reserved | ||
|
||
// Feature Name [0] | ||
0x00, 0x00, // feature: 0 | ||
0x00, 0x01, // number of settings: 1 | ||
0x00, 0x00, 0x00, 0x3C, // offset to settings table: 60 | ||
0x00, 0x00, // flags: none | ||
0x01, 0x04, // name index: 260 | ||
|
||
// Feature Name [1] | ||
0x00, 0x01, // feature: 1 | ||
0x00, 0x01, // number of settings: 1 | ||
0x00, 0x00, 0x00, 0x40, // offset to settings table: 64 | ||
0x00, 0x00, // flags: none | ||
0x01, 0x00, // name index: 256 | ||
|
||
// Feature Name [2] | ||
0x00, 0x03, // feature: 3 | ||
0x00, 0x03, // number of settings: 3 | ||
0x00, 0x00, 0x00, 0x44, // offset to settings table: 68 | ||
0x80, 0x00, // flags: exclusive | ||
0x01, 0x06, // name index: 262 | ||
|
||
// Feature Name [3] | ||
0x00, 0x06, // feature: 6 | ||
0x00, 0x01, // number of settings: 2 | ||
0x00, 0x00, 0x00, 0x50, // offset to settings table: 80 | ||
0xC0, 0x01, // flags: exclusive and other | ||
0x01, 0x02, // name index: 258 | ||
|
||
// Setting Name [0] | ||
0x00, 0x00, // setting: 0 | ||
0x01, 0x05, // name index: 261 | ||
|
||
// Setting Name [1] | ||
0x00, 0x02, // setting: 2 | ||
0x01, 0x01, // name index: 257 | ||
|
||
// Setting Name [2] | ||
0x00, 0x00, // setting: 0 | ||
0x01, 0x0C, // name index: 268 | ||
0x00, 0x03, // setting: 3 | ||
0x01, 0x08, // name index: 264 | ||
0x00, 0x04, // setting: 4 | ||
0x01, 0x09, // name index: 265 | ||
|
||
// Setting Name [3] | ||
0x00, 0x00, // setting: 0 | ||
0x01, 0x03, // name index: 259 | ||
0x00, 0x00, // setting: 1 | ||
0x01, 0x04, // name index: 260 | ||
]; | ||
|
||
let table = Table::parse(data).unwrap(); | ||
assert_eq!(table.names.len(), 4); | ||
|
||
let feature0 = table.names.get(0).unwrap(); | ||
assert_eq!(feature0.feature, 0); | ||
assert_eq!(feature0.setting_names.len(), 1); | ||
assert_eq!(feature0.exclusive, false); | ||
assert_eq!(feature0.name_index, 260); | ||
|
||
let feature2 = table.names.get(2).unwrap(); | ||
assert_eq!(feature2.feature, 3); | ||
assert_eq!(feature2.setting_names.len(), 3); | ||
assert_eq!(feature2.exclusive, true); | ||
|
||
assert_eq!(feature2.setting_names.get(1).unwrap().setting, 3); | ||
assert_eq!(feature2.setting_names.get(1).unwrap().name_index, 264); | ||
|
||
let feature3 = table.names.get(3).unwrap(); | ||
assert_eq!(feature3.default_setting_index, 1); | ||
assert_eq!(feature3.exclusive, true); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
mod cff1; | ||
mod cmap; | ||
mod feat; | ||
mod hmtx; | ||
mod maxp; | ||
mod sbix; | ||
|