5736 lines
196 KiB
Rust
5736 lines
196 KiB
Rust
// THIS FILE IS AUTOGENERATED.
|
||
// Any changes to this file will be overwritten.
|
||
// For more information about how codegen works, see font-codegen/README.md
|
||
|
||
#[allow(unused_imports)]
|
||
use crate::codegen_prelude::*;
|
||
|
||
/// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ScriptListMarker {
|
||
script_records_byte_len: usize,
|
||
}
|
||
|
||
impl ScriptListMarker {
|
||
pub fn script_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn script_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.script_count_byte_range().end;
|
||
start..start + self.script_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ScriptListMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.script_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ScriptList<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let script_count: u16 = cursor.read()?;
|
||
let script_records_byte_len = (script_count as usize)
|
||
.checked_mul(ScriptRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(script_records_byte_len);
|
||
cursor.finish(ScriptListMarker {
|
||
script_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Script List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
|
||
pub type ScriptList<'a> = TableRef<'a, ScriptListMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ScriptList<'a> {
|
||
/// Number of ScriptRecords
|
||
pub fn script_count(&self) -> u16 {
|
||
let range = self.shape.script_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of ScriptRecords, listed alphabetically by script tag
|
||
pub fn script_records(&self) -> &'a [ScriptRecord] {
|
||
let range = self.shape.script_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ScriptList<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ScriptList"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("script_count", self.script_count())),
|
||
1usize => Some(Field::new(
|
||
"script_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(ScriptRecord),
|
||
self.script_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ScriptList<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// [Script Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record)
|
||
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct ScriptRecord {
|
||
/// 4-byte script tag identifier
|
||
pub script_tag: BigEndian<Tag>,
|
||
/// Offset to Script table, from beginning of ScriptList
|
||
pub script_offset: BigEndian<Offset16>,
|
||
}
|
||
|
||
impl ScriptRecord {
|
||
/// 4-byte script tag identifier
|
||
pub fn script_tag(&self) -> Tag {
|
||
self.script_tag.get()
|
||
}
|
||
|
||
/// Offset to Script table, from beginning of ScriptList
|
||
pub fn script_offset(&self) -> Offset16 {
|
||
self.script_offset.get()
|
||
}
|
||
|
||
/// Offset to Script table, from beginning of ScriptList
|
||
///
|
||
/// The `data` argument should be retrieved from the parent table
|
||
/// By calling its `offset_data` method.
|
||
pub fn script<'a>(&self, data: FontData<'a>) -> Result<Script<'a>, ReadError> {
|
||
self.script_offset().resolve(data)
|
||
}
|
||
}
|
||
|
||
impl FixedSize for ScriptRecord {
|
||
const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for ScriptRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "ScriptRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("script_tag", self.script_tag())),
|
||
1usize => Some(Field::new(
|
||
"script_offset",
|
||
FieldType::offset(self.script_offset(), self.script(_data)),
|
||
)),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ScriptMarker {
|
||
lang_sys_records_byte_len: usize,
|
||
}
|
||
|
||
impl ScriptMarker {
|
||
pub fn default_lang_sys_offset_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lang_sys_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.default_lang_sys_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lang_sys_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.lang_sys_count_byte_range().end;
|
||
start..start + self.lang_sys_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ScriptMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.lang_sys_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for Script<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<Offset16>();
|
||
let lang_sys_count: u16 = cursor.read()?;
|
||
let lang_sys_records_byte_len = (lang_sys_count as usize)
|
||
.checked_mul(LangSysRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(lang_sys_records_byte_len);
|
||
cursor.finish(ScriptMarker {
|
||
lang_sys_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Script Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#script-table-and-language-system-record)
|
||
pub type Script<'a> = TableRef<'a, ScriptMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> Script<'a> {
|
||
/// Offset to default LangSys table, from beginning of Script table
|
||
/// — may be NULL
|
||
pub fn default_lang_sys_offset(&self) -> Nullable<Offset16> {
|
||
let range = self.shape.default_lang_sys_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`default_lang_sys_offset`][Self::default_lang_sys_offset].
|
||
pub fn default_lang_sys(&self) -> Option<Result<LangSys<'a>, ReadError>> {
|
||
let data = self.data;
|
||
self.default_lang_sys_offset().resolve(data)
|
||
}
|
||
|
||
/// Number of LangSysRecords for this script — excluding the
|
||
/// default LangSys
|
||
pub fn lang_sys_count(&self) -> u16 {
|
||
let range = self.shape.lang_sys_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of LangSysRecords, listed alphabetically by LangSys tag
|
||
pub fn lang_sys_records(&self) -> &'a [LangSysRecord] {
|
||
let range = self.shape.lang_sys_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for Script<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"Script"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"default_lang_sys_offset",
|
||
FieldType::offset(self.default_lang_sys_offset(), self.default_lang_sys()),
|
||
)),
|
||
1usize => Some(Field::new("lang_sys_count", self.lang_sys_count())),
|
||
2usize => Some(Field::new(
|
||
"lang_sys_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(LangSysRecord),
|
||
self.lang_sys_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for Script<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct LangSysRecord {
|
||
/// 4-byte LangSysTag identifier
|
||
pub lang_sys_tag: BigEndian<Tag>,
|
||
/// Offset to LangSys table, from beginning of Script table
|
||
pub lang_sys_offset: BigEndian<Offset16>,
|
||
}
|
||
|
||
impl LangSysRecord {
|
||
/// 4-byte LangSysTag identifier
|
||
pub fn lang_sys_tag(&self) -> Tag {
|
||
self.lang_sys_tag.get()
|
||
}
|
||
|
||
/// Offset to LangSys table, from beginning of Script table
|
||
pub fn lang_sys_offset(&self) -> Offset16 {
|
||
self.lang_sys_offset.get()
|
||
}
|
||
|
||
/// Offset to LangSys table, from beginning of Script table
|
||
///
|
||
/// The `data` argument should be retrieved from the parent table
|
||
/// By calling its `offset_data` method.
|
||
pub fn lang_sys<'a>(&self, data: FontData<'a>) -> Result<LangSys<'a>, ReadError> {
|
||
self.lang_sys_offset().resolve(data)
|
||
}
|
||
}
|
||
|
||
impl FixedSize for LangSysRecord {
|
||
const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for LangSysRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "LangSysRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("lang_sys_tag", self.lang_sys_tag())),
|
||
1usize => Some(Field::new(
|
||
"lang_sys_offset",
|
||
FieldType::offset(self.lang_sys_offset(), self.lang_sys(_data)),
|
||
)),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct LangSysMarker {
|
||
feature_indices_byte_len: usize,
|
||
}
|
||
|
||
impl LangSysMarker {
|
||
pub fn lookup_order_offset_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn required_feature_index_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookup_order_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feature_index_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.required_feature_index_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feature_indices_byte_range(&self) -> Range<usize> {
|
||
let start = self.feature_index_count_byte_range().end;
|
||
start..start + self.feature_indices_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for LangSysMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.feature_indices_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for LangSys<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
let feature_index_count: u16 = cursor.read()?;
|
||
let feature_indices_byte_len = (feature_index_count as usize)
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(feature_indices_byte_len);
|
||
cursor.finish(LangSysMarker {
|
||
feature_indices_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Language System Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#language-system-table)
|
||
pub type LangSys<'a> = TableRef<'a, LangSysMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> LangSys<'a> {
|
||
/// Index of a feature required for this language system; if no
|
||
/// required features = 0xFFFF
|
||
pub fn required_feature_index(&self) -> u16 {
|
||
let range = self.shape.required_feature_index_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of feature index values for this language system —
|
||
/// excludes the required feature
|
||
pub fn feature_index_count(&self) -> u16 {
|
||
let range = self.shape.feature_index_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of indices into the FeatureList, in arbitrary order
|
||
pub fn feature_indices(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.feature_indices_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for LangSys<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"LangSys"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"required_feature_index",
|
||
self.required_feature_index(),
|
||
)),
|
||
1usize => Some(Field::new(
|
||
"feature_index_count",
|
||
self.feature_index_count(),
|
||
)),
|
||
2usize => Some(Field::new("feature_indices", self.feature_indices())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for LangSys<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct FeatureListMarker {
|
||
feature_records_byte_len: usize,
|
||
}
|
||
|
||
impl FeatureListMarker {
|
||
pub fn feature_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feature_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.feature_count_byte_range().end;
|
||
start..start + self.feature_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for FeatureListMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.feature_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for FeatureList<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let feature_count: u16 = cursor.read()?;
|
||
let feature_records_byte_len = (feature_count as usize)
|
||
.checked_mul(FeatureRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(feature_records_byte_len);
|
||
cursor.finish(FeatureListMarker {
|
||
feature_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Feature List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-list-table)
|
||
pub type FeatureList<'a> = TableRef<'a, FeatureListMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> FeatureList<'a> {
|
||
/// Number of FeatureRecords in this table
|
||
pub fn feature_count(&self) -> u16 {
|
||
let range = self.shape.feature_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of FeatureRecords — zero-based (first feature has
|
||
/// FeatureIndex = 0), listed alphabetically by feature tag
|
||
pub fn feature_records(&self) -> &'a [FeatureRecord] {
|
||
let range = self.shape.feature_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for FeatureList<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"FeatureList"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("feature_count", self.feature_count())),
|
||
1usize => Some(Field::new(
|
||
"feature_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(FeatureRecord),
|
||
self.feature_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for FeatureList<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [FeatureList]
|
||
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct FeatureRecord {
|
||
/// 4-byte feature identification tag
|
||
pub feature_tag: BigEndian<Tag>,
|
||
/// Offset to Feature table, from beginning of FeatureList
|
||
pub feature_offset: BigEndian<Offset16>,
|
||
}
|
||
|
||
impl FeatureRecord {
|
||
/// 4-byte feature identification tag
|
||
pub fn feature_tag(&self) -> Tag {
|
||
self.feature_tag.get()
|
||
}
|
||
|
||
/// Offset to Feature table, from beginning of FeatureList
|
||
pub fn feature_offset(&self) -> Offset16 {
|
||
self.feature_offset.get()
|
||
}
|
||
|
||
/// Offset to Feature table, from beginning of FeatureList
|
||
///
|
||
/// The `data` argument should be retrieved from the parent table
|
||
/// By calling its `offset_data` method.
|
||
pub fn feature<'a>(&self, data: FontData<'a>) -> Result<Feature<'a>, ReadError> {
|
||
let args = self.feature_tag();
|
||
self.feature_offset().resolve_with_args(data, &args)
|
||
}
|
||
}
|
||
|
||
impl FixedSize for FeatureRecord {
|
||
const RAW_BYTE_LEN: usize = Tag::RAW_BYTE_LEN + Offset16::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for FeatureRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "FeatureRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("feature_tag", self.feature_tag())),
|
||
1usize => Some(Field::new(
|
||
"feature_offset",
|
||
FieldType::offset(self.feature_offset(), self.feature(_data)),
|
||
)),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct FeatureMarker {
|
||
feature_tag: Tag,
|
||
lookup_list_indices_byte_len: usize,
|
||
}
|
||
|
||
impl FeatureMarker {
|
||
pub fn feature_params_offset_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookup_index_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.feature_params_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookup_list_indices_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookup_index_count_byte_range().end;
|
||
start..start + self.lookup_list_indices_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for FeatureMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.lookup_list_indices_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl ReadArgs for Feature<'_> {
|
||
type Args = Tag;
|
||
}
|
||
|
||
impl<'a> FontReadWithArgs<'a> for Feature<'a> {
|
||
fn read_with_args(data: FontData<'a>, args: &Tag) -> Result<Self, ReadError> {
|
||
let feature_tag = *args;
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<Offset16>();
|
||
let lookup_index_count: u16 = cursor.read()?;
|
||
let lookup_list_indices_byte_len = (lookup_index_count as usize)
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(lookup_list_indices_byte_len);
|
||
cursor.finish(FeatureMarker {
|
||
feature_tag,
|
||
lookup_list_indices_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
impl<'a> Feature<'a> {
|
||
/// A constructor that requires additional arguments.
|
||
///
|
||
/// This type requires some external state in order to be
|
||
/// parsed.
|
||
pub fn read(data: FontData<'a>, feature_tag: Tag) -> Result<Self, ReadError> {
|
||
let args = feature_tag;
|
||
Self::read_with_args(data, &args)
|
||
}
|
||
}
|
||
|
||
/// [Feature Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#feature-table)
|
||
pub type Feature<'a> = TableRef<'a, FeatureMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> Feature<'a> {
|
||
/// Offset from start of Feature table to FeatureParams table, if defined for the feature and present, else NULL
|
||
pub fn feature_params_offset(&self) -> Nullable<Offset16> {
|
||
let range = self.shape.feature_params_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`feature_params_offset`][Self::feature_params_offset].
|
||
pub fn feature_params(&self) -> Option<Result<FeatureParams<'a>, ReadError>> {
|
||
let data = self.data;
|
||
let args = self.feature_tag();
|
||
self.feature_params_offset().resolve_with_args(data, &args)
|
||
}
|
||
|
||
/// Number of LookupList indices for this feature
|
||
pub fn lookup_index_count(&self) -> u16 {
|
||
let range = self.shape.lookup_index_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of indices into the LookupList — zero-based (first
|
||
/// lookup is LookupListIndex = 0)
|
||
pub fn lookup_list_indices(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.lookup_list_indices_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
pub(crate) fn feature_tag(&self) -> Tag {
|
||
self.shape.feature_tag
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for Feature<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"Feature"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"feature_params_offset",
|
||
FieldType::offset(self.feature_params_offset(), self.feature_params()),
|
||
)),
|
||
1usize => Some(Field::new("lookup_index_count", self.lookup_index_count())),
|
||
2usize => Some(Field::new(
|
||
"lookup_list_indices",
|
||
self.lookup_list_indices(),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for Feature<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table)
|
||
#[derive(Debug)]
|
||
#[doc(hidden)]
|
||
pub struct LookupListMarker<T = ()> {
|
||
lookup_offsets_byte_len: usize,
|
||
offset_type: std::marker::PhantomData<*const T>,
|
||
}
|
||
|
||
impl<T> LookupListMarker<T> {
|
||
pub fn lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookup_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookup_count_byte_range().end;
|
||
start..start + self.lookup_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for LookupListMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.lookup_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<T> Clone for LookupListMarker<T> {
|
||
fn clone(&self) -> Self {
|
||
*self
|
||
}
|
||
}
|
||
|
||
impl<T> Copy for LookupListMarker<T> {}
|
||
|
||
impl<'a, T> FontRead<'a> for LookupList<'a, T> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let lookup_count: u16 = cursor.read()?;
|
||
let lookup_offsets_byte_len = (lookup_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(lookup_offsets_byte_len);
|
||
cursor.finish(LookupListMarker {
|
||
lookup_offsets_byte_len,
|
||
offset_type: std::marker::PhantomData,
|
||
})
|
||
}
|
||
}
|
||
|
||
impl<'a> LookupList<'a, ()> {
|
||
#[allow(dead_code)]
|
||
pub(crate) fn into_concrete<T>(self) -> LookupList<'a, T> {
|
||
let TableRef { data, shape } = self;
|
||
TableRef {
|
||
shape: LookupListMarker {
|
||
lookup_offsets_byte_len: shape.lookup_offsets_byte_len,
|
||
offset_type: std::marker::PhantomData,
|
||
},
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a, T> LookupList<'a, T> {
|
||
#[allow(dead_code)]
|
||
/// Replace the specific generic type on this implementation with `()`
|
||
pub(crate) fn of_unit_type(&self) -> LookupList<'a, ()> {
|
||
let TableRef { data, shape } = self;
|
||
TableRef {
|
||
shape: LookupListMarker {
|
||
lookup_offsets_byte_len: shape.lookup_offsets_byte_len,
|
||
offset_type: std::marker::PhantomData,
|
||
},
|
||
data: *data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [Lookup List Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-list-table)
|
||
pub type LookupList<'a, T> = TableRef<'a, LookupListMarker<T>>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a, T> LookupList<'a, T> {
|
||
/// Number of lookups in this table
|
||
pub fn lookup_count(&self) -> u16 {
|
||
let range = self.shape.lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to Lookup tables, from beginning of LookupList
|
||
/// — zero based (first lookup is Lookup index = 0)
|
||
pub fn lookup_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.lookup_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`lookup_offsets`][Self::lookup_offsets].
|
||
pub fn lookups(&self) -> ArrayOfOffsets<'a, T, Offset16>
|
||
where
|
||
T: FontRead<'a>,
|
||
{
|
||
let data = self.data;
|
||
let offsets = self.lookup_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for LookupList<'a, T> {
|
||
fn type_name(&self) -> &str {
|
||
"LookupList"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("lookup_count", self.lookup_count())),
|
||
1usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"lookup_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<T>(),
|
||
self.lookup_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<T>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for LookupList<'a, T> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table)
|
||
#[derive(Debug)]
|
||
#[doc(hidden)]
|
||
pub struct LookupMarker<T = ()> {
|
||
subtable_offsets_byte_len: usize,
|
||
mark_filtering_set_byte_start: Option<usize>,
|
||
offset_type: std::marker::PhantomData<*const T>,
|
||
}
|
||
|
||
impl<T> LookupMarker<T> {
|
||
pub fn lookup_type_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookup_flag_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookup_type_byte_range().end;
|
||
start..start + LookupFlag::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn sub_table_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookup_flag_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn subtable_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.sub_table_count_byte_range().end;
|
||
start..start + self.subtable_offsets_byte_len
|
||
}
|
||
|
||
pub fn mark_filtering_set_byte_range(&self) -> Option<Range<usize>> {
|
||
let start = self.mark_filtering_set_byte_start?;
|
||
Some(start..start + u16::RAW_BYTE_LEN)
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for LookupMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.subtable_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<T> Clone for LookupMarker<T> {
|
||
fn clone(&self) -> Self {
|
||
*self
|
||
}
|
||
}
|
||
|
||
impl<T> Copy for LookupMarker<T> {}
|
||
|
||
impl<'a, T> FontRead<'a> for Lookup<'a, T> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let lookup_flag: LookupFlag = cursor.read()?;
|
||
let sub_table_count: u16 = cursor.read()?;
|
||
let subtable_offsets_byte_len = (sub_table_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(subtable_offsets_byte_len);
|
||
let mark_filtering_set_byte_start = lookup_flag
|
||
.contains(LookupFlag::USE_MARK_FILTERING_SET)
|
||
.then(|| cursor.position())
|
||
.transpose()?;
|
||
lookup_flag
|
||
.contains(LookupFlag::USE_MARK_FILTERING_SET)
|
||
.then(|| cursor.advance::<u16>());
|
||
cursor.finish(LookupMarker {
|
||
subtable_offsets_byte_len,
|
||
mark_filtering_set_byte_start,
|
||
offset_type: std::marker::PhantomData,
|
||
})
|
||
}
|
||
}
|
||
|
||
impl<'a> Lookup<'a, ()> {
|
||
#[allow(dead_code)]
|
||
pub(crate) fn into_concrete<T>(self) -> Lookup<'a, T> {
|
||
let TableRef { data, shape } = self;
|
||
TableRef {
|
||
shape: LookupMarker {
|
||
subtable_offsets_byte_len: shape.subtable_offsets_byte_len,
|
||
mark_filtering_set_byte_start: shape.mark_filtering_set_byte_start,
|
||
offset_type: std::marker::PhantomData,
|
||
},
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a, T> Lookup<'a, T> {
|
||
#[allow(dead_code)]
|
||
/// Replace the specific generic type on this implementation with `()`
|
||
pub(crate) fn of_unit_type(&self) -> Lookup<'a, ()> {
|
||
let TableRef { data, shape } = self;
|
||
TableRef {
|
||
shape: LookupMarker {
|
||
subtable_offsets_byte_len: shape.subtable_offsets_byte_len,
|
||
mark_filtering_set_byte_start: shape.mark_filtering_set_byte_start,
|
||
offset_type: std::marker::PhantomData,
|
||
},
|
||
data: *data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [Lookup Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#lookup-table)
|
||
pub type Lookup<'a, T> = TableRef<'a, LookupMarker<T>>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a, T> Lookup<'a, T> {
|
||
/// Different enumerations for GSUB and GPOS
|
||
pub fn lookup_type(&self) -> u16 {
|
||
let range = self.shape.lookup_type_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Lookup qualifiers
|
||
pub fn lookup_flag(&self) -> LookupFlag {
|
||
let range = self.shape.lookup_flag_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of subtables for this lookup
|
||
pub fn sub_table_count(&self) -> u16 {
|
||
let range = self.shape.sub_table_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to lookup subtables, from beginning of Lookup
|
||
/// table
|
||
pub fn subtable_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.subtable_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`subtable_offsets`][Self::subtable_offsets].
|
||
pub fn subtables(&self) -> ArrayOfOffsets<'a, T, Offset16>
|
||
where
|
||
T: FontRead<'a>,
|
||
{
|
||
let data = self.data;
|
||
let offsets = self.subtable_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
|
||
/// Index (base 0) into GDEF mark glyph sets structure. This field
|
||
/// is only present if the USE_MARK_FILTERING_SET lookup flag is
|
||
/// set.
|
||
pub fn mark_filtering_set(&self) -> Option<u16> {
|
||
let range = self.shape.mark_filtering_set_byte_range()?;
|
||
Some(self.data.read_at(range.start).unwrap())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> SomeTable<'a> for Lookup<'a, T> {
|
||
fn type_name(&self) -> &str {
|
||
"Lookup"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
let lookup_flag = self.lookup_flag();
|
||
match idx {
|
||
0usize => Some(Field::new("lookup_type", self.lookup_type())),
|
||
1usize => Some(Field::new("lookup_flag", self.traverse_lookup_flag())),
|
||
2usize => Some(Field::new("sub_table_count", self.sub_table_count())),
|
||
3usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"subtable_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<T>(),
|
||
self.subtable_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<T>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
4usize if lookup_flag.contains(LookupFlag::USE_MARK_FILTERING_SET) => Some(Field::new(
|
||
"mark_filtering_set",
|
||
self.mark_filtering_set().unwrap(),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a, T: FontRead<'a> + SomeTable<'a> + 'a> std::fmt::Debug for Lookup<'a, T> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for CoverageFormat1Marker {
|
||
const FORMAT: u16 = 1;
|
||
}
|
||
|
||
/// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct CoverageFormat1Marker {
|
||
glyph_array_byte_len: usize,
|
||
}
|
||
|
||
impl CoverageFormat1Marker {
|
||
pub fn coverage_format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_format_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn glyph_array_byte_range(&self) -> Range<usize> {
|
||
let start = self.glyph_count_byte_range().end;
|
||
start..start + self.glyph_array_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for CoverageFormat1Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.glyph_array_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for CoverageFormat1<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let glyph_count: u16 = cursor.read()?;
|
||
let glyph_array_byte_len = (glyph_count as usize)
|
||
.checked_mul(GlyphId16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(glyph_array_byte_len);
|
||
cursor.finish(CoverageFormat1Marker {
|
||
glyph_array_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Coverage Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-1)
|
||
pub type CoverageFormat1<'a> = TableRef<'a, CoverageFormat1Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> CoverageFormat1<'a> {
|
||
/// Format identifier — format = 1
|
||
pub fn coverage_format(&self) -> u16 {
|
||
let range = self.shape.coverage_format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of glyphs in the glyph array
|
||
pub fn glyph_count(&self) -> u16 {
|
||
let range = self.shape.glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of glyph IDs — in numerical order
|
||
pub fn glyph_array(&self) -> &'a [BigEndian<GlyphId16>] {
|
||
let range = self.shape.glyph_array_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for CoverageFormat1<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"CoverageFormat1"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("coverage_format", self.coverage_format())),
|
||
1usize => Some(Field::new("glyph_count", self.glyph_count())),
|
||
2usize => Some(Field::new("glyph_array", self.glyph_array())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for CoverageFormat1<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for CoverageFormat2Marker {
|
||
const FORMAT: u16 = 2;
|
||
}
|
||
|
||
/// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct CoverageFormat2Marker {
|
||
range_records_byte_len: usize,
|
||
}
|
||
|
||
impl CoverageFormat2Marker {
|
||
pub fn coverage_format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn range_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_format_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn range_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.range_count_byte_range().end;
|
||
start..start + self.range_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for CoverageFormat2Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.range_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for CoverageFormat2<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let range_count: u16 = cursor.read()?;
|
||
let range_records_byte_len = (range_count as usize)
|
||
.checked_mul(RangeRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(range_records_byte_len);
|
||
cursor.finish(CoverageFormat2Marker {
|
||
range_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Coverage Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-format-2)
|
||
pub type CoverageFormat2<'a> = TableRef<'a, CoverageFormat2Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> CoverageFormat2<'a> {
|
||
/// Format identifier — format = 2
|
||
pub fn coverage_format(&self) -> u16 {
|
||
let range = self.shape.coverage_format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of RangeRecords
|
||
pub fn range_count(&self) -> u16 {
|
||
let range = self.shape.range_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of glyph ranges — ordered by startGlyphID.
|
||
pub fn range_records(&self) -> &'a [RangeRecord] {
|
||
let range = self.shape.range_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for CoverageFormat2<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"CoverageFormat2"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("coverage_format", self.coverage_format())),
|
||
1usize => Some(Field::new("range_count", self.range_count())),
|
||
2usize => Some(Field::new(
|
||
"range_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(RangeRecord),
|
||
self.range_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for CoverageFormat2<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Used in [CoverageFormat2]
|
||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct RangeRecord {
|
||
/// First glyph ID in the range
|
||
pub start_glyph_id: BigEndian<GlyphId16>,
|
||
/// Last glyph ID in the range
|
||
pub end_glyph_id: BigEndian<GlyphId16>,
|
||
/// Coverage Index of first glyph ID in range
|
||
pub start_coverage_index: BigEndian<u16>,
|
||
}
|
||
|
||
impl RangeRecord {
|
||
/// First glyph ID in the range
|
||
pub fn start_glyph_id(&self) -> GlyphId16 {
|
||
self.start_glyph_id.get()
|
||
}
|
||
|
||
/// Last glyph ID in the range
|
||
pub fn end_glyph_id(&self) -> GlyphId16 {
|
||
self.end_glyph_id.get()
|
||
}
|
||
|
||
/// Coverage Index of first glyph ID in range
|
||
pub fn start_coverage_index(&self) -> u16 {
|
||
self.start_coverage_index.get()
|
||
}
|
||
}
|
||
|
||
impl FixedSize for RangeRecord {
|
||
const RAW_BYTE_LEN: usize =
|
||
GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for RangeRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "RangeRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
|
||
1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())),
|
||
2usize => Some(Field::new(
|
||
"start_coverage_index",
|
||
self.start_coverage_index(),
|
||
)),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [Coverage Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#coverage-table)
|
||
#[derive(Clone)]
|
||
pub enum CoverageTable<'a> {
|
||
Format1(CoverageFormat1<'a>),
|
||
Format2(CoverageFormat2<'a>),
|
||
}
|
||
|
||
impl<'a> CoverageTable<'a> {
|
||
///Return the `FontData` used to resolve offsets for this table.
|
||
pub fn offset_data(&self) -> FontData<'a> {
|
||
match self {
|
||
Self::Format1(item) => item.offset_data(),
|
||
Self::Format2(item) => item.offset_data(),
|
||
}
|
||
}
|
||
|
||
/// Format identifier — format = 1
|
||
pub fn coverage_format(&self) -> u16 {
|
||
match self {
|
||
Self::Format1(item) => item.coverage_format(),
|
||
Self::Format2(item) => item.coverage_format(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for CoverageTable<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let format: u16 = data.read_at(0usize)?;
|
||
match format {
|
||
CoverageFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
|
||
CoverageFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
|
||
other => Err(ReadError::InvalidFormat(other.into())),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for CoverageTable<'_> {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
match self {
|
||
Self::Format1(item) => item.min_byte_range(),
|
||
Self::Format2(item) => item.min_byte_range(),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> CoverageTable<'a> {
|
||
fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
|
||
match self {
|
||
Self::Format1(table) => table,
|
||
Self::Format2(table) => table,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl std::fmt::Debug for CoverageTable<'_> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
self.dyn_inner().fmt(f)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for CoverageTable<'a> {
|
||
fn type_name(&self) -> &str {
|
||
self.dyn_inner().type_name()
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
self.dyn_inner().get_field(idx)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ClassDefFormat1Marker {
|
||
const FORMAT: u16 = 1;
|
||
}
|
||
|
||
/// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ClassDefFormat1Marker {
|
||
class_value_array_byte_len: usize,
|
||
}
|
||
|
||
impl ClassDefFormat1Marker {
|
||
pub fn class_format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn start_glyph_id_byte_range(&self) -> Range<usize> {
|
||
let start = self.class_format_byte_range().end;
|
||
start..start + GlyphId16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.start_glyph_id_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_value_array_byte_range(&self) -> Range<usize> {
|
||
let start = self.glyph_count_byte_range().end;
|
||
start..start + self.class_value_array_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ClassDefFormat1Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.class_value_array_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ClassDefFormat1<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<GlyphId16>();
|
||
let glyph_count: u16 = cursor.read()?;
|
||
let class_value_array_byte_len = (glyph_count as usize)
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(class_value_array_byte_len);
|
||
cursor.finish(ClassDefFormat1Marker {
|
||
class_value_array_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Class Definition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-1)
|
||
pub type ClassDefFormat1<'a> = TableRef<'a, ClassDefFormat1Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ClassDefFormat1<'a> {
|
||
/// Format identifier — format = 1
|
||
pub fn class_format(&self) -> u16 {
|
||
let range = self.shape.class_format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// First glyph ID of the classValueArray
|
||
pub fn start_glyph_id(&self) -> GlyphId16 {
|
||
let range = self.shape.start_glyph_id_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Size of the classValueArray
|
||
pub fn glyph_count(&self) -> u16 {
|
||
let range = self.shape.glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of Class Values — one per glyph ID
|
||
pub fn class_value_array(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.class_value_array_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ClassDefFormat1<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ClassDefFormat1"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("class_format", self.class_format())),
|
||
1usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
|
||
2usize => Some(Field::new("glyph_count", self.glyph_count())),
|
||
3usize => Some(Field::new("class_value_array", self.class_value_array())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ClassDefFormat1<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ClassDefFormat2Marker {
|
||
const FORMAT: u16 = 2;
|
||
}
|
||
|
||
/// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ClassDefFormat2Marker {
|
||
class_range_records_byte_len: usize,
|
||
}
|
||
|
||
impl ClassDefFormat2Marker {
|
||
pub fn class_format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_range_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.class_format_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_range_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.class_range_count_byte_range().end;
|
||
start..start + self.class_range_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ClassDefFormat2Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.class_range_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ClassDefFormat2<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let class_range_count: u16 = cursor.read()?;
|
||
let class_range_records_byte_len = (class_range_count as usize)
|
||
.checked_mul(ClassRangeRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(class_range_records_byte_len);
|
||
cursor.finish(ClassDefFormat2Marker {
|
||
class_range_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Class Definition Table Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table-format-2)
|
||
pub type ClassDefFormat2<'a> = TableRef<'a, ClassDefFormat2Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ClassDefFormat2<'a> {
|
||
/// Format identifier — format = 2
|
||
pub fn class_format(&self) -> u16 {
|
||
let range = self.shape.class_format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of ClassRangeRecords
|
||
pub fn class_range_count(&self) -> u16 {
|
||
let range = self.shape.class_range_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of ClassRangeRecords — ordered by startGlyphID
|
||
pub fn class_range_records(&self) -> &'a [ClassRangeRecord] {
|
||
let range = self.shape.class_range_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ClassDefFormat2<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ClassDefFormat2"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("class_format", self.class_format())),
|
||
1usize => Some(Field::new("class_range_count", self.class_range_count())),
|
||
2usize => Some(Field::new(
|
||
"class_range_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(ClassRangeRecord),
|
||
self.class_range_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ClassDefFormat2<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Used in [ClassDefFormat2]
|
||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct ClassRangeRecord {
|
||
/// First glyph ID in the range
|
||
pub start_glyph_id: BigEndian<GlyphId16>,
|
||
/// Last glyph ID in the range
|
||
pub end_glyph_id: BigEndian<GlyphId16>,
|
||
/// Applied to all glyphs in the range
|
||
pub class: BigEndian<u16>,
|
||
}
|
||
|
||
impl ClassRangeRecord {
|
||
/// First glyph ID in the range
|
||
pub fn start_glyph_id(&self) -> GlyphId16 {
|
||
self.start_glyph_id.get()
|
||
}
|
||
|
||
/// Last glyph ID in the range
|
||
pub fn end_glyph_id(&self) -> GlyphId16 {
|
||
self.end_glyph_id.get()
|
||
}
|
||
|
||
/// Applied to all glyphs in the range
|
||
pub fn class(&self) -> u16 {
|
||
self.class.get()
|
||
}
|
||
}
|
||
|
||
impl FixedSize for ClassRangeRecord {
|
||
const RAW_BYTE_LEN: usize =
|
||
GlyphId16::RAW_BYTE_LEN + GlyphId16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for ClassRangeRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "ClassRangeRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("start_glyph_id", self.start_glyph_id())),
|
||
1usize => Some(Field::new("end_glyph_id", self.end_glyph_id())),
|
||
2usize => Some(Field::new("class", self.class())),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// A [Class Definition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#class-definition-table)
|
||
#[derive(Clone)]
|
||
pub enum ClassDef<'a> {
|
||
Format1(ClassDefFormat1<'a>),
|
||
Format2(ClassDefFormat2<'a>),
|
||
}
|
||
|
||
impl<'a> ClassDef<'a> {
|
||
///Return the `FontData` used to resolve offsets for this table.
|
||
pub fn offset_data(&self) -> FontData<'a> {
|
||
match self {
|
||
Self::Format1(item) => item.offset_data(),
|
||
Self::Format2(item) => item.offset_data(),
|
||
}
|
||
}
|
||
|
||
/// Format identifier — format = 1
|
||
pub fn class_format(&self) -> u16 {
|
||
match self {
|
||
Self::Format1(item) => item.class_format(),
|
||
Self::Format2(item) => item.class_format(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ClassDef<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let format: u16 = data.read_at(0usize)?;
|
||
match format {
|
||
ClassDefFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
|
||
ClassDefFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
|
||
other => Err(ReadError::InvalidFormat(other.into())),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ClassDef<'_> {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
match self {
|
||
Self::Format1(item) => item.min_byte_range(),
|
||
Self::Format2(item) => item.min_byte_range(),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> ClassDef<'a> {
|
||
fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
|
||
match self {
|
||
Self::Format1(table) => table,
|
||
Self::Format2(table) => table,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl std::fmt::Debug for ClassDef<'_> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
self.dyn_inner().fmt(f)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ClassDef<'a> {
|
||
fn type_name(&self) -> &str {
|
||
self.dyn_inner().type_name()
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
self.dyn_inner().get_field(idx)
|
||
}
|
||
}
|
||
|
||
/// [Sequence Lookup Record](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-lookup-record)
|
||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct SequenceLookupRecord {
|
||
/// Index (zero-based) into the input glyph sequence
|
||
pub sequence_index: BigEndian<u16>,
|
||
/// Index (zero-based) into the LookupList
|
||
pub lookup_list_index: BigEndian<u16>,
|
||
}
|
||
|
||
impl SequenceLookupRecord {
|
||
/// Index (zero-based) into the input glyph sequence
|
||
pub fn sequence_index(&self) -> u16 {
|
||
self.sequence_index.get()
|
||
}
|
||
|
||
/// Index (zero-based) into the LookupList
|
||
pub fn lookup_list_index(&self) -> u16 {
|
||
self.lookup_list_index.get()
|
||
}
|
||
}
|
||
|
||
impl FixedSize for SequenceLookupRecord {
|
||
const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + u16::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for SequenceLookupRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "SequenceLookupRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("sequence_index", self.sequence_index())),
|
||
1usize => Some(Field::new("lookup_list_index", self.lookup_list_index())),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for SequenceContextFormat1Marker {
|
||
const FORMAT: u16 = 1;
|
||
}
|
||
|
||
/// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct SequenceContextFormat1Marker {
|
||
seq_rule_set_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl SequenceContextFormat1Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn coverage_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_rule_set_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_rule_set_count_byte_range().end;
|
||
start..start + self.seq_rule_set_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SequenceContextFormat1Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_rule_set_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SequenceContextFormat1<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<Offset16>();
|
||
let seq_rule_set_count: u16 = cursor.read()?;
|
||
let seq_rule_set_offsets_byte_len = (seq_rule_set_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_rule_set_offsets_byte_len);
|
||
cursor.finish(SequenceContextFormat1Marker {
|
||
seq_rule_set_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-1-simple-glyph-contexts)
|
||
pub type SequenceContextFormat1<'a> = TableRef<'a, SequenceContextFormat1Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> SequenceContextFormat1<'a> {
|
||
/// Format identifier: format = 1
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Offset to Coverage table, from beginning of
|
||
/// SequenceContextFormat1 table
|
||
pub fn coverage_offset(&self) -> Offset16 {
|
||
let range = self.shape.coverage_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
|
||
pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.coverage_offset().resolve(data)
|
||
}
|
||
|
||
/// Number of SequenceRuleSet tables
|
||
pub fn seq_rule_set_count(&self) -> u16 {
|
||
let range = self.shape.seq_rule_set_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to SequenceRuleSet tables, from beginning of
|
||
/// SequenceContextFormat1 table (offsets may be NULL)
|
||
pub fn seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
|
||
let range = self.shape.seq_rule_set_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`seq_rule_set_offsets`][Self::seq_rule_set_offsets].
|
||
pub fn seq_rule_sets(&self) -> ArrayOfNullableOffsets<'a, SequenceRuleSet<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.seq_rule_set_offsets();
|
||
ArrayOfNullableOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SequenceContextFormat1<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"SequenceContextFormat1"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"coverage_offset",
|
||
FieldType::offset(self.coverage_offset(), self.coverage()),
|
||
)),
|
||
2usize => Some(Field::new("seq_rule_set_count", self.seq_rule_set_count())),
|
||
3usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"seq_rule_set_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<SequenceRuleSet>(),
|
||
self.seq_rule_set_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<SequenceRuleSet>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for SequenceContextFormat1<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat1]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct SequenceRuleSetMarker {
|
||
seq_rule_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl SequenceRuleSetMarker {
|
||
pub fn seq_rule_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_rule_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_rule_count_byte_range().end;
|
||
start..start + self.seq_rule_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SequenceRuleSetMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_rule_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SequenceRuleSet<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let seq_rule_count: u16 = cursor.read()?;
|
||
let seq_rule_offsets_byte_len = (seq_rule_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_rule_offsets_byte_len);
|
||
cursor.finish(SequenceRuleSetMarker {
|
||
seq_rule_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat1]
|
||
pub type SequenceRuleSet<'a> = TableRef<'a, SequenceRuleSetMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> SequenceRuleSet<'a> {
|
||
/// Number of SequenceRule tables
|
||
pub fn seq_rule_count(&self) -> u16 {
|
||
let range = self.shape.seq_rule_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to SequenceRule tables, from beginning of the
|
||
/// SequenceRuleSet table
|
||
pub fn seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.seq_rule_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`seq_rule_offsets`][Self::seq_rule_offsets].
|
||
pub fn seq_rules(&self) -> ArrayOfOffsets<'a, SequenceRule<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.seq_rule_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SequenceRuleSet<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"SequenceRuleSet"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("seq_rule_count", self.seq_rule_count())),
|
||
1usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"seq_rule_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<SequenceRule>(),
|
||
self.seq_rule_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<SequenceRule>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for SequenceRuleSet<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat1]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct SequenceRuleMarker {
|
||
input_sequence_byte_len: usize,
|
||
seq_lookup_records_byte_len: usize,
|
||
}
|
||
|
||
impl SequenceRuleMarker {
|
||
pub fn glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.glyph_count_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn input_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_lookup_count_byte_range().end;
|
||
start..start + self.input_sequence_byte_len
|
||
}
|
||
|
||
pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_sequence_byte_range().end;
|
||
start..start + self.seq_lookup_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SequenceRuleMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_lookup_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SequenceRule<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let glyph_count: u16 = cursor.read()?;
|
||
let seq_lookup_count: u16 = cursor.read()?;
|
||
let input_sequence_byte_len = (transforms::subtract(glyph_count, 1_usize))
|
||
.checked_mul(GlyphId16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(input_sequence_byte_len);
|
||
let seq_lookup_records_byte_len = (seq_lookup_count as usize)
|
||
.checked_mul(SequenceLookupRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_lookup_records_byte_len);
|
||
cursor.finish(SequenceRuleMarker {
|
||
input_sequence_byte_len,
|
||
seq_lookup_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat1]
|
||
pub type SequenceRule<'a> = TableRef<'a, SequenceRuleMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> SequenceRule<'a> {
|
||
/// Number of glyphs in the input glyph sequence
|
||
pub fn glyph_count(&self) -> u16 {
|
||
let range = self.shape.glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of SequenceLookupRecords
|
||
pub fn seq_lookup_count(&self) -> u16 {
|
||
let range = self.shape.seq_lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of input glyph IDs—starting with the second glyph
|
||
pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
|
||
let range = self.shape.input_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Array of Sequence lookup records
|
||
pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
|
||
let range = self.shape.seq_lookup_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SequenceRule<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"SequenceRule"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("glyph_count", self.glyph_count())),
|
||
1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
|
||
2usize => Some(Field::new("input_sequence", self.input_sequence())),
|
||
3usize => Some(Field::new(
|
||
"seq_lookup_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(SequenceLookupRecord),
|
||
self.seq_lookup_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for SequenceRule<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for SequenceContextFormat2Marker {
|
||
const FORMAT: u16 = 2;
|
||
}
|
||
|
||
/// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct SequenceContextFormat2Marker {
|
||
class_seq_rule_set_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl SequenceContextFormat2Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn coverage_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_def_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_offset_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.class_def_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.class_seq_rule_set_count_byte_range().end;
|
||
start..start + self.class_seq_rule_set_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SequenceContextFormat2Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.class_seq_rule_set_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SequenceContextFormat2<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<Offset16>();
|
||
cursor.advance::<Offset16>();
|
||
let class_seq_rule_set_count: u16 = cursor.read()?;
|
||
let class_seq_rule_set_offsets_byte_len = (class_seq_rule_set_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(class_seq_rule_set_offsets_byte_len);
|
||
cursor.finish(SequenceContextFormat2Marker {
|
||
class_seq_rule_set_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-2-class-based-glyph-contexts)
|
||
pub type SequenceContextFormat2<'a> = TableRef<'a, SequenceContextFormat2Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> SequenceContextFormat2<'a> {
|
||
/// Format identifier: format = 2
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Offset to Coverage table, from beginning of
|
||
/// SequenceContextFormat2 table
|
||
pub fn coverage_offset(&self) -> Offset16 {
|
||
let range = self.shape.coverage_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
|
||
pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.coverage_offset().resolve(data)
|
||
}
|
||
|
||
/// Offset to ClassDef table, from beginning of
|
||
/// SequenceContextFormat2 table
|
||
pub fn class_def_offset(&self) -> Offset16 {
|
||
let range = self.shape.class_def_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`class_def_offset`][Self::class_def_offset].
|
||
pub fn class_def(&self) -> Result<ClassDef<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.class_def_offset().resolve(data)
|
||
}
|
||
|
||
/// Number of ClassSequenceRuleSet tables
|
||
pub fn class_seq_rule_set_count(&self) -> u16 {
|
||
let range = self.shape.class_seq_rule_set_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to ClassSequenceRuleSet tables, from beginning
|
||
/// of SequenceContextFormat2 table (may be NULL)
|
||
pub fn class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
|
||
let range = self.shape.class_seq_rule_set_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`class_seq_rule_set_offsets`][Self::class_seq_rule_set_offsets].
|
||
pub fn class_seq_rule_sets(
|
||
&self,
|
||
) -> ArrayOfNullableOffsets<'a, ClassSequenceRuleSet<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.class_seq_rule_set_offsets();
|
||
ArrayOfNullableOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SequenceContextFormat2<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"SequenceContextFormat2"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"coverage_offset",
|
||
FieldType::offset(self.coverage_offset(), self.coverage()),
|
||
)),
|
||
2usize => Some(Field::new(
|
||
"class_def_offset",
|
||
FieldType::offset(self.class_def_offset(), self.class_def()),
|
||
)),
|
||
3usize => Some(Field::new(
|
||
"class_seq_rule_set_count",
|
||
self.class_seq_rule_set_count(),
|
||
)),
|
||
4usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"class_seq_rule_set_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<ClassSequenceRuleSet>(),
|
||
self.class_seq_rule_set_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<ClassSequenceRuleSet>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for SequenceContextFormat2<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat2]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ClassSequenceRuleSetMarker {
|
||
class_seq_rule_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ClassSequenceRuleSetMarker {
|
||
pub fn class_seq_rule_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.class_seq_rule_count_byte_range().end;
|
||
start..start + self.class_seq_rule_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ClassSequenceRuleSetMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.class_seq_rule_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ClassSequenceRuleSet<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let class_seq_rule_count: u16 = cursor.read()?;
|
||
let class_seq_rule_offsets_byte_len = (class_seq_rule_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(class_seq_rule_offsets_byte_len);
|
||
cursor.finish(ClassSequenceRuleSetMarker {
|
||
class_seq_rule_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat2]
|
||
pub type ClassSequenceRuleSet<'a> = TableRef<'a, ClassSequenceRuleSetMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ClassSequenceRuleSet<'a> {
|
||
/// Number of ClassSequenceRule tables
|
||
pub fn class_seq_rule_count(&self) -> u16 {
|
||
let range = self.shape.class_seq_rule_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to ClassSequenceRule tables, from beginning of
|
||
/// ClassSequenceRuleSet table
|
||
pub fn class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.class_seq_rule_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`class_seq_rule_offsets`][Self::class_seq_rule_offsets].
|
||
pub fn class_seq_rules(&self) -> ArrayOfOffsets<'a, ClassSequenceRule<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.class_seq_rule_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ClassSequenceRuleSet<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ClassSequenceRuleSet"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"class_seq_rule_count",
|
||
self.class_seq_rule_count(),
|
||
)),
|
||
1usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"class_seq_rule_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<ClassSequenceRule>(),
|
||
self.class_seq_rule_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<ClassSequenceRule>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ClassSequenceRuleSet<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat2]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ClassSequenceRuleMarker {
|
||
input_sequence_byte_len: usize,
|
||
seq_lookup_records_byte_len: usize,
|
||
}
|
||
|
||
impl ClassSequenceRuleMarker {
|
||
pub fn glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.glyph_count_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn input_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_lookup_count_byte_range().end;
|
||
start..start + self.input_sequence_byte_len
|
||
}
|
||
|
||
pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_sequence_byte_range().end;
|
||
start..start + self.seq_lookup_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ClassSequenceRuleMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_lookup_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ClassSequenceRule<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let glyph_count: u16 = cursor.read()?;
|
||
let seq_lookup_count: u16 = cursor.read()?;
|
||
let input_sequence_byte_len = (transforms::subtract(glyph_count, 1_usize))
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(input_sequence_byte_len);
|
||
let seq_lookup_records_byte_len = (seq_lookup_count as usize)
|
||
.checked_mul(SequenceLookupRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_lookup_records_byte_len);
|
||
cursor.finish(ClassSequenceRuleMarker {
|
||
input_sequence_byte_len,
|
||
seq_lookup_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [SequenceContextFormat2]
|
||
pub type ClassSequenceRule<'a> = TableRef<'a, ClassSequenceRuleMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ClassSequenceRule<'a> {
|
||
/// Number of glyphs to be matched
|
||
pub fn glyph_count(&self) -> u16 {
|
||
let range = self.shape.glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of SequenceLookupRecords
|
||
pub fn seq_lookup_count(&self) -> u16 {
|
||
let range = self.shape.seq_lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Sequence of classes to be matched to the input glyph sequence,
|
||
/// beginning with the second glyph position
|
||
pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.input_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Array of SequenceLookupRecords
|
||
pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
|
||
let range = self.shape.seq_lookup_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ClassSequenceRule<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ClassSequenceRule"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("glyph_count", self.glyph_count())),
|
||
1usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
|
||
2usize => Some(Field::new("input_sequence", self.input_sequence())),
|
||
3usize => Some(Field::new(
|
||
"seq_lookup_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(SequenceLookupRecord),
|
||
self.seq_lookup_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ClassSequenceRule<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for SequenceContextFormat3Marker {
|
||
const FORMAT: u16 = 3;
|
||
}
|
||
|
||
/// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct SequenceContextFormat3Marker {
|
||
coverage_offsets_byte_len: usize,
|
||
seq_lookup_records_byte_len: usize,
|
||
}
|
||
|
||
impl SequenceContextFormat3Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.glyph_count_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn coverage_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_lookup_count_byte_range().end;
|
||
start..start + self.coverage_offsets_byte_len
|
||
}
|
||
|
||
pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_offsets_byte_range().end;
|
||
start..start + self.seq_lookup_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SequenceContextFormat3Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_lookup_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SequenceContextFormat3<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let glyph_count: u16 = cursor.read()?;
|
||
let seq_lookup_count: u16 = cursor.read()?;
|
||
let coverage_offsets_byte_len = (glyph_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(coverage_offsets_byte_len);
|
||
let seq_lookup_records_byte_len = (seq_lookup_count as usize)
|
||
.checked_mul(SequenceLookupRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_lookup_records_byte_len);
|
||
cursor.finish(SequenceContextFormat3Marker {
|
||
coverage_offsets_byte_len,
|
||
seq_lookup_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#sequence-context-format-3-coverage-based-glyph-contexts)
|
||
pub type SequenceContextFormat3<'a> = TableRef<'a, SequenceContextFormat3Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> SequenceContextFormat3<'a> {
|
||
/// Format identifier: format = 3
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of glyphs in the input sequence
|
||
pub fn glyph_count(&self) -> u16 {
|
||
let range = self.shape.glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of SequenceLookupRecords
|
||
pub fn seq_lookup_count(&self) -> u16 {
|
||
let range = self.shape.seq_lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to Coverage tables, from beginning of
|
||
/// SequenceContextFormat3 subtable
|
||
pub fn coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.coverage_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`coverage_offsets`][Self::coverage_offsets].
|
||
pub fn coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.coverage_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
|
||
/// Array of SequenceLookupRecords
|
||
pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
|
||
let range = self.shape.seq_lookup_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SequenceContextFormat3<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"SequenceContextFormat3"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new("glyph_count", self.glyph_count())),
|
||
2usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
|
||
3usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"coverage_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<CoverageTable>(),
|
||
self.coverage_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<CoverageTable>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
4usize => Some(Field::new(
|
||
"seq_lookup_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(SequenceLookupRecord),
|
||
self.seq_lookup_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for SequenceContextFormat3<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
#[derive(Clone)]
|
||
pub enum SequenceContext<'a> {
|
||
Format1(SequenceContextFormat1<'a>),
|
||
Format2(SequenceContextFormat2<'a>),
|
||
Format3(SequenceContextFormat3<'a>),
|
||
}
|
||
|
||
impl<'a> SequenceContext<'a> {
|
||
///Return the `FontData` used to resolve offsets for this table.
|
||
pub fn offset_data(&self) -> FontData<'a> {
|
||
match self {
|
||
Self::Format1(item) => item.offset_data(),
|
||
Self::Format2(item) => item.offset_data(),
|
||
Self::Format3(item) => item.offset_data(),
|
||
}
|
||
}
|
||
|
||
/// Format identifier: format = 1
|
||
pub fn format(&self) -> u16 {
|
||
match self {
|
||
Self::Format1(item) => item.format(),
|
||
Self::Format2(item) => item.format(),
|
||
Self::Format3(item) => item.format(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SequenceContext<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let format: u16 = data.read_at(0usize)?;
|
||
match format {
|
||
SequenceContextFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
|
||
SequenceContextFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
|
||
SequenceContextFormat3Marker::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
|
||
other => Err(ReadError::InvalidFormat(other.into())),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SequenceContext<'_> {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
match self {
|
||
Self::Format1(item) => item.min_byte_range(),
|
||
Self::Format2(item) => item.min_byte_range(),
|
||
Self::Format3(item) => item.min_byte_range(),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SequenceContext<'a> {
|
||
fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
|
||
match self {
|
||
Self::Format1(table) => table,
|
||
Self::Format2(table) => table,
|
||
Self::Format3(table) => table,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl std::fmt::Debug for SequenceContext<'_> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
self.dyn_inner().fmt(f)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SequenceContext<'a> {
|
||
fn type_name(&self) -> &str {
|
||
self.dyn_inner().type_name()
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
self.dyn_inner().get_field(idx)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ChainedSequenceContextFormat1Marker {
|
||
const FORMAT: u16 = 1;
|
||
}
|
||
|
||
/// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedSequenceContextFormat1Marker {
|
||
chained_seq_rule_set_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedSequenceContextFormat1Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn coverage_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn chained_seq_rule_set_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn chained_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.chained_seq_rule_set_count_byte_range().end;
|
||
start..start + self.chained_seq_rule_set_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedSequenceContextFormat1Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.chained_seq_rule_set_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedSequenceContextFormat1<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<Offset16>();
|
||
let chained_seq_rule_set_count: u16 = cursor.read()?;
|
||
let chained_seq_rule_set_offsets_byte_len = (chained_seq_rule_set_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(chained_seq_rule_set_offsets_byte_len);
|
||
cursor.finish(ChainedSequenceContextFormat1Marker {
|
||
chained_seq_rule_set_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Chained Sequence Context Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-1-simple-glyph-contexts)
|
||
pub type ChainedSequenceContextFormat1<'a> = TableRef<'a, ChainedSequenceContextFormat1Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedSequenceContextFormat1<'a> {
|
||
/// Format identifier: format = 1
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Offset to Coverage table, from beginning of
|
||
/// ChainSequenceContextFormat1 table
|
||
pub fn coverage_offset(&self) -> Offset16 {
|
||
let range = self.shape.coverage_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
|
||
pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.coverage_offset().resolve(data)
|
||
}
|
||
|
||
/// Number of ChainedSequenceRuleSet tables
|
||
pub fn chained_seq_rule_set_count(&self) -> u16 {
|
||
let range = self.shape.chained_seq_rule_set_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to ChainedSeqRuleSet tables, from beginning of
|
||
/// ChainedSequenceContextFormat1 table (may be NULL)
|
||
pub fn chained_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
|
||
let range = self.shape.chained_seq_rule_set_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`chained_seq_rule_set_offsets`][Self::chained_seq_rule_set_offsets].
|
||
pub fn chained_seq_rule_sets(
|
||
&self,
|
||
) -> ArrayOfNullableOffsets<'a, ChainedSequenceRuleSet<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.chained_seq_rule_set_offsets();
|
||
ArrayOfNullableOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedSequenceContextFormat1<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedSequenceContextFormat1"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"coverage_offset",
|
||
FieldType::offset(self.coverage_offset(), self.coverage()),
|
||
)),
|
||
2usize => Some(Field::new(
|
||
"chained_seq_rule_set_count",
|
||
self.chained_seq_rule_set_count(),
|
||
)),
|
||
3usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"chained_seq_rule_set_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<ChainedSequenceRuleSet>(),
|
||
self.chained_seq_rule_set_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<ChainedSequenceRuleSet>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedSequenceContextFormat1<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat1]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedSequenceRuleSetMarker {
|
||
chained_seq_rule_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedSequenceRuleSetMarker {
|
||
pub fn chained_seq_rule_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn chained_seq_rule_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.chained_seq_rule_count_byte_range().end;
|
||
start..start + self.chained_seq_rule_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedSequenceRuleSetMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.chained_seq_rule_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedSequenceRuleSet<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let chained_seq_rule_count: u16 = cursor.read()?;
|
||
let chained_seq_rule_offsets_byte_len = (chained_seq_rule_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(chained_seq_rule_offsets_byte_len);
|
||
cursor.finish(ChainedSequenceRuleSetMarker {
|
||
chained_seq_rule_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat1]
|
||
pub type ChainedSequenceRuleSet<'a> = TableRef<'a, ChainedSequenceRuleSetMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedSequenceRuleSet<'a> {
|
||
/// Number of ChainedSequenceRule tables
|
||
pub fn chained_seq_rule_count(&self) -> u16 {
|
||
let range = self.shape.chained_seq_rule_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to ChainedSequenceRule tables, from beginning
|
||
/// of ChainedSequenceRuleSet table
|
||
pub fn chained_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.chained_seq_rule_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`chained_seq_rule_offsets`][Self::chained_seq_rule_offsets].
|
||
pub fn chained_seq_rules(&self) -> ArrayOfOffsets<'a, ChainedSequenceRule<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.chained_seq_rule_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedSequenceRuleSet<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedSequenceRuleSet"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"chained_seq_rule_count",
|
||
self.chained_seq_rule_count(),
|
||
)),
|
||
1usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"chained_seq_rule_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<ChainedSequenceRule>(),
|
||
self.chained_seq_rule_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<ChainedSequenceRule>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedSequenceRuleSet<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat1]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedSequenceRuleMarker {
|
||
backtrack_sequence_byte_len: usize,
|
||
input_sequence_byte_len: usize,
|
||
lookahead_sequence_byte_len: usize,
|
||
seq_lookup_records_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedSequenceRuleMarker {
|
||
pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_glyph_count_byte_range().end;
|
||
start..start + self.backtrack_sequence_byte_len
|
||
}
|
||
|
||
pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_sequence_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn input_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_glyph_count_byte_range().end;
|
||
start..start + self.input_sequence_byte_len
|
||
}
|
||
|
||
pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_sequence_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_glyph_count_byte_range().end;
|
||
start..start + self.lookahead_sequence_byte_len
|
||
}
|
||
|
||
pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_sequence_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_lookup_count_byte_range().end;
|
||
start..start + self.seq_lookup_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedSequenceRuleMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_lookup_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedSequenceRule<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let backtrack_glyph_count: u16 = cursor.read()?;
|
||
let backtrack_sequence_byte_len = (backtrack_glyph_count as usize)
|
||
.checked_mul(GlyphId16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(backtrack_sequence_byte_len);
|
||
let input_glyph_count: u16 = cursor.read()?;
|
||
let input_sequence_byte_len = (transforms::subtract(input_glyph_count, 1_usize))
|
||
.checked_mul(GlyphId16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(input_sequence_byte_len);
|
||
let lookahead_glyph_count: u16 = cursor.read()?;
|
||
let lookahead_sequence_byte_len = (lookahead_glyph_count as usize)
|
||
.checked_mul(GlyphId16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(lookahead_sequence_byte_len);
|
||
let seq_lookup_count: u16 = cursor.read()?;
|
||
let seq_lookup_records_byte_len = (seq_lookup_count as usize)
|
||
.checked_mul(SequenceLookupRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_lookup_records_byte_len);
|
||
cursor.finish(ChainedSequenceRuleMarker {
|
||
backtrack_sequence_byte_len,
|
||
input_sequence_byte_len,
|
||
lookahead_sequence_byte_len,
|
||
seq_lookup_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat1]
|
||
pub type ChainedSequenceRule<'a> = TableRef<'a, ChainedSequenceRuleMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedSequenceRule<'a> {
|
||
/// Number of glyphs in the backtrack sequence
|
||
pub fn backtrack_glyph_count(&self) -> u16 {
|
||
let range = self.shape.backtrack_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of backtrack glyph IDs
|
||
pub fn backtrack_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
|
||
let range = self.shape.backtrack_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Number of glyphs in the input sequence
|
||
pub fn input_glyph_count(&self) -> u16 {
|
||
let range = self.shape.input_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of input glyph IDs—start with second glyph
|
||
pub fn input_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
|
||
let range = self.shape.input_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Number of glyphs in the lookahead sequence
|
||
pub fn lookahead_glyph_count(&self) -> u16 {
|
||
let range = self.shape.lookahead_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of lookahead glyph IDs
|
||
pub fn lookahead_sequence(&self) -> &'a [BigEndian<GlyphId16>] {
|
||
let range = self.shape.lookahead_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Number of SequenceLookupRecords
|
||
pub fn seq_lookup_count(&self) -> u16 {
|
||
let range = self.shape.seq_lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of SequenceLookupRecords
|
||
pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
|
||
let range = self.shape.seq_lookup_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedSequenceRule<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedSequenceRule"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"backtrack_glyph_count",
|
||
self.backtrack_glyph_count(),
|
||
)),
|
||
1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())),
|
||
2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
|
||
3usize => Some(Field::new("input_sequence", self.input_sequence())),
|
||
4usize => Some(Field::new(
|
||
"lookahead_glyph_count",
|
||
self.lookahead_glyph_count(),
|
||
)),
|
||
5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())),
|
||
6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
|
||
7usize => Some(Field::new(
|
||
"seq_lookup_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(SequenceLookupRecord),
|
||
self.seq_lookup_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedSequenceRule<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ChainedSequenceContextFormat2Marker {
|
||
const FORMAT: u16 = 2;
|
||
}
|
||
|
||
/// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedSequenceContextFormat2Marker {
|
||
chained_class_seq_rule_set_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedSequenceContextFormat2Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn coverage_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn backtrack_class_def_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.coverage_offset_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn input_class_def_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_class_def_offset_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookahead_class_def_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_class_def_offset_byte_range().end;
|
||
start..start + Offset16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn chained_class_seq_rule_set_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_class_def_offset_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn chained_class_seq_rule_set_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.chained_class_seq_rule_set_count_byte_range().end;
|
||
start..start + self.chained_class_seq_rule_set_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedSequenceContextFormat2Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.chained_class_seq_rule_set_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedSequenceContextFormat2<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<Offset16>();
|
||
cursor.advance::<Offset16>();
|
||
cursor.advance::<Offset16>();
|
||
cursor.advance::<Offset16>();
|
||
let chained_class_seq_rule_set_count: u16 = cursor.read()?;
|
||
let chained_class_seq_rule_set_offsets_byte_len = (chained_class_seq_rule_set_count
|
||
as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(chained_class_seq_rule_set_offsets_byte_len);
|
||
cursor.finish(ChainedSequenceContextFormat2Marker {
|
||
chained_class_seq_rule_set_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Chained Sequence Context Format 2](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-2-class-based-glyph-contexts)
|
||
pub type ChainedSequenceContextFormat2<'a> = TableRef<'a, ChainedSequenceContextFormat2Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedSequenceContextFormat2<'a> {
|
||
/// Format identifier: format = 2
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Offset to Coverage table, from beginning of
|
||
/// ChainedSequenceContextFormat2 table
|
||
pub fn coverage_offset(&self) -> Offset16 {
|
||
let range = self.shape.coverage_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`coverage_offset`][Self::coverage_offset].
|
||
pub fn coverage(&self) -> Result<CoverageTable<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.coverage_offset().resolve(data)
|
||
}
|
||
|
||
/// Offset to ClassDef table containing backtrack sequence context,
|
||
/// from beginning of ChainedSequenceContextFormat2 table
|
||
pub fn backtrack_class_def_offset(&self) -> Offset16 {
|
||
let range = self.shape.backtrack_class_def_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`backtrack_class_def_offset`][Self::backtrack_class_def_offset].
|
||
pub fn backtrack_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.backtrack_class_def_offset().resolve(data)
|
||
}
|
||
|
||
/// Offset to ClassDef table containing input sequence context,
|
||
/// from beginning of ChainedSequenceContextFormat2 table
|
||
pub fn input_class_def_offset(&self) -> Offset16 {
|
||
let range = self.shape.input_class_def_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`input_class_def_offset`][Self::input_class_def_offset].
|
||
pub fn input_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.input_class_def_offset().resolve(data)
|
||
}
|
||
|
||
/// Offset to ClassDef table containing lookahead sequence context,
|
||
/// from beginning of ChainedSequenceContextFormat2 table
|
||
pub fn lookahead_class_def_offset(&self) -> Offset16 {
|
||
let range = self.shape.lookahead_class_def_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`lookahead_class_def_offset`][Self::lookahead_class_def_offset].
|
||
pub fn lookahead_class_def(&self) -> Result<ClassDef<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.lookahead_class_def_offset().resolve(data)
|
||
}
|
||
|
||
/// Number of ChainedClassSequenceRuleSet tables
|
||
pub fn chained_class_seq_rule_set_count(&self) -> u16 {
|
||
let range = self.shape.chained_class_seq_rule_set_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to ChainedClassSequenceRuleSet tables, from
|
||
/// beginning of ChainedSequenceContextFormat2 table (may be NULL)
|
||
pub fn chained_class_seq_rule_set_offsets(&self) -> &'a [BigEndian<Nullable<Offset16>>] {
|
||
let range = self.shape.chained_class_seq_rule_set_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`chained_class_seq_rule_set_offsets`][Self::chained_class_seq_rule_set_offsets].
|
||
pub fn chained_class_seq_rule_sets(
|
||
&self,
|
||
) -> ArrayOfNullableOffsets<'a, ChainedClassSequenceRuleSet<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.chained_class_seq_rule_set_offsets();
|
||
ArrayOfNullableOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedSequenceContextFormat2<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedSequenceContextFormat2"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"coverage_offset",
|
||
FieldType::offset(self.coverage_offset(), self.coverage()),
|
||
)),
|
||
2usize => Some(Field::new(
|
||
"backtrack_class_def_offset",
|
||
FieldType::offset(
|
||
self.backtrack_class_def_offset(),
|
||
self.backtrack_class_def(),
|
||
),
|
||
)),
|
||
3usize => Some(Field::new(
|
||
"input_class_def_offset",
|
||
FieldType::offset(self.input_class_def_offset(), self.input_class_def()),
|
||
)),
|
||
4usize => Some(Field::new(
|
||
"lookahead_class_def_offset",
|
||
FieldType::offset(
|
||
self.lookahead_class_def_offset(),
|
||
self.lookahead_class_def(),
|
||
),
|
||
)),
|
||
5usize => Some(Field::new(
|
||
"chained_class_seq_rule_set_count",
|
||
self.chained_class_seq_rule_set_count(),
|
||
)),
|
||
6usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"chained_class_seq_rule_set_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<ChainedClassSequenceRuleSet>(),
|
||
self.chained_class_seq_rule_set_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<ChainedClassSequenceRuleSet>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedSequenceContextFormat2<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat2]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedClassSequenceRuleSetMarker {
|
||
chained_class_seq_rule_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedClassSequenceRuleSetMarker {
|
||
pub fn chained_class_seq_rule_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn chained_class_seq_rule_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.chained_class_seq_rule_count_byte_range().end;
|
||
start..start + self.chained_class_seq_rule_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedClassSequenceRuleSetMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.chained_class_seq_rule_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedClassSequenceRuleSet<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let chained_class_seq_rule_count: u16 = cursor.read()?;
|
||
let chained_class_seq_rule_offsets_byte_len = (chained_class_seq_rule_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(chained_class_seq_rule_offsets_byte_len);
|
||
cursor.finish(ChainedClassSequenceRuleSetMarker {
|
||
chained_class_seq_rule_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat2]
|
||
pub type ChainedClassSequenceRuleSet<'a> = TableRef<'a, ChainedClassSequenceRuleSetMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedClassSequenceRuleSet<'a> {
|
||
/// Number of ChainedClassSequenceRule tables
|
||
pub fn chained_class_seq_rule_count(&self) -> u16 {
|
||
let range = self.shape.chained_class_seq_rule_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to ChainedClassSequenceRule tables, from
|
||
/// beginning of ChainedClassSequenceRuleSet
|
||
pub fn chained_class_seq_rule_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.chained_class_seq_rule_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`chained_class_seq_rule_offsets`][Self::chained_class_seq_rule_offsets].
|
||
pub fn chained_class_seq_rules(
|
||
&self,
|
||
) -> ArrayOfOffsets<'a, ChainedClassSequenceRule<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.chained_class_seq_rule_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedClassSequenceRuleSet<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedClassSequenceRuleSet"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"chained_class_seq_rule_count",
|
||
self.chained_class_seq_rule_count(),
|
||
)),
|
||
1usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"chained_class_seq_rule_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<ChainedClassSequenceRule>(),
|
||
self.chained_class_seq_rule_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<ChainedClassSequenceRule>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedClassSequenceRuleSet<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat2]
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedClassSequenceRuleMarker {
|
||
backtrack_sequence_byte_len: usize,
|
||
input_sequence_byte_len: usize,
|
||
lookahead_sequence_byte_len: usize,
|
||
seq_lookup_records_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedClassSequenceRuleMarker {
|
||
pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn backtrack_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_glyph_count_byte_range().end;
|
||
start..start + self.backtrack_sequence_byte_len
|
||
}
|
||
|
||
pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_sequence_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn input_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_glyph_count_byte_range().end;
|
||
start..start + self.input_sequence_byte_len
|
||
}
|
||
|
||
pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_sequence_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookahead_sequence_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_glyph_count_byte_range().end;
|
||
start..start + self.lookahead_sequence_byte_len
|
||
}
|
||
|
||
pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_sequence_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_lookup_count_byte_range().end;
|
||
start..start + self.seq_lookup_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedClassSequenceRuleMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_lookup_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedClassSequenceRule<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let backtrack_glyph_count: u16 = cursor.read()?;
|
||
let backtrack_sequence_byte_len = (backtrack_glyph_count as usize)
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(backtrack_sequence_byte_len);
|
||
let input_glyph_count: u16 = cursor.read()?;
|
||
let input_sequence_byte_len = (transforms::subtract(input_glyph_count, 1_usize))
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(input_sequence_byte_len);
|
||
let lookahead_glyph_count: u16 = cursor.read()?;
|
||
let lookahead_sequence_byte_len = (lookahead_glyph_count as usize)
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(lookahead_sequence_byte_len);
|
||
let seq_lookup_count: u16 = cursor.read()?;
|
||
let seq_lookup_records_byte_len = (seq_lookup_count as usize)
|
||
.checked_mul(SequenceLookupRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_lookup_records_byte_len);
|
||
cursor.finish(ChainedClassSequenceRuleMarker {
|
||
backtrack_sequence_byte_len,
|
||
input_sequence_byte_len,
|
||
lookahead_sequence_byte_len,
|
||
seq_lookup_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// Part of [ChainedSequenceContextFormat2]
|
||
pub type ChainedClassSequenceRule<'a> = TableRef<'a, ChainedClassSequenceRuleMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedClassSequenceRule<'a> {
|
||
/// Number of glyphs in the backtrack sequence
|
||
pub fn backtrack_glyph_count(&self) -> u16 {
|
||
let range = self.shape.backtrack_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of backtrack-sequence classes
|
||
pub fn backtrack_sequence(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.backtrack_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Total number of glyphs in the input sequence
|
||
pub fn input_glyph_count(&self) -> u16 {
|
||
let range = self.shape.input_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of input sequence classes, beginning with the second
|
||
/// glyph position
|
||
pub fn input_sequence(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.input_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Number of glyphs in the lookahead sequence
|
||
pub fn lookahead_glyph_count(&self) -> u16 {
|
||
let range = self.shape.lookahead_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of lookahead-sequence classes
|
||
pub fn lookahead_sequence(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.lookahead_sequence_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// Number of SequenceLookupRecords
|
||
pub fn seq_lookup_count(&self) -> u16 {
|
||
let range = self.shape.seq_lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of SequenceLookupRecords
|
||
pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
|
||
let range = self.shape.seq_lookup_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedClassSequenceRule<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedClassSequenceRule"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"backtrack_glyph_count",
|
||
self.backtrack_glyph_count(),
|
||
)),
|
||
1usize => Some(Field::new("backtrack_sequence", self.backtrack_sequence())),
|
||
2usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
|
||
3usize => Some(Field::new("input_sequence", self.input_sequence())),
|
||
4usize => Some(Field::new(
|
||
"lookahead_glyph_count",
|
||
self.lookahead_glyph_count(),
|
||
)),
|
||
5usize => Some(Field::new("lookahead_sequence", self.lookahead_sequence())),
|
||
6usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
|
||
7usize => Some(Field::new(
|
||
"seq_lookup_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(SequenceLookupRecord),
|
||
self.seq_lookup_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedClassSequenceRule<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ChainedSequenceContextFormat3Marker {
|
||
const FORMAT: u16 = 3;
|
||
}
|
||
|
||
/// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ChainedSequenceContextFormat3Marker {
|
||
backtrack_coverage_offsets_byte_len: usize,
|
||
input_coverage_offsets_byte_len: usize,
|
||
lookahead_coverage_offsets_byte_len: usize,
|
||
seq_lookup_records_byte_len: usize,
|
||
}
|
||
|
||
impl ChainedSequenceContextFormat3Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn backtrack_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn backtrack_coverage_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_glyph_count_byte_range().end;
|
||
start..start + self.backtrack_coverage_offsets_byte_len
|
||
}
|
||
|
||
pub fn input_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.backtrack_coverage_offsets_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn input_coverage_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_glyph_count_byte_range().end;
|
||
start..start + self.input_coverage_offsets_byte_len
|
||
}
|
||
|
||
pub fn lookahead_glyph_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.input_coverage_offsets_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn lookahead_coverage_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_glyph_count_byte_range().end;
|
||
start..start + self.lookahead_coverage_offsets_byte_len
|
||
}
|
||
|
||
pub fn seq_lookup_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.lookahead_coverage_offsets_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn seq_lookup_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.seq_lookup_count_byte_range().end;
|
||
start..start + self.seq_lookup_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedSequenceContextFormat3Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.seq_lookup_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedSequenceContextFormat3<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let backtrack_glyph_count: u16 = cursor.read()?;
|
||
let backtrack_coverage_offsets_byte_len = (backtrack_glyph_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(backtrack_coverage_offsets_byte_len);
|
||
let input_glyph_count: u16 = cursor.read()?;
|
||
let input_coverage_offsets_byte_len = (input_glyph_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(input_coverage_offsets_byte_len);
|
||
let lookahead_glyph_count: u16 = cursor.read()?;
|
||
let lookahead_coverage_offsets_byte_len = (lookahead_glyph_count as usize)
|
||
.checked_mul(Offset16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(lookahead_coverage_offsets_byte_len);
|
||
let seq_lookup_count: u16 = cursor.read()?;
|
||
let seq_lookup_records_byte_len = (seq_lookup_count as usize)
|
||
.checked_mul(SequenceLookupRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(seq_lookup_records_byte_len);
|
||
cursor.finish(ChainedSequenceContextFormat3Marker {
|
||
backtrack_coverage_offsets_byte_len,
|
||
input_coverage_offsets_byte_len,
|
||
lookahead_coverage_offsets_byte_len,
|
||
seq_lookup_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Chained Sequence Context Format 3](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#chained-sequence-context-format-3-coverage-based-glyph-contexts)
|
||
pub type ChainedSequenceContextFormat3<'a> = TableRef<'a, ChainedSequenceContextFormat3Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ChainedSequenceContextFormat3<'a> {
|
||
/// Format identifier: format = 3
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of glyphs in the backtrack sequence
|
||
pub fn backtrack_glyph_count(&self) -> u16 {
|
||
let range = self.shape.backtrack_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to coverage tables for the backtrack sequence
|
||
pub fn backtrack_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.backtrack_coverage_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`backtrack_coverage_offsets`][Self::backtrack_coverage_offsets].
|
||
pub fn backtrack_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.backtrack_coverage_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
|
||
/// Number of glyphs in the input sequence
|
||
pub fn input_glyph_count(&self) -> u16 {
|
||
let range = self.shape.input_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to coverage tables for the input sequence
|
||
pub fn input_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.input_coverage_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`input_coverage_offsets`][Self::input_coverage_offsets].
|
||
pub fn input_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.input_coverage_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
|
||
/// Number of glyphs in the lookahead sequence
|
||
pub fn lookahead_glyph_count(&self) -> u16 {
|
||
let range = self.shape.lookahead_glyph_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to coverage tables for the lookahead sequence
|
||
pub fn lookahead_coverage_offsets(&self) -> &'a [BigEndian<Offset16>] {
|
||
let range = self.shape.lookahead_coverage_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`lookahead_coverage_offsets`][Self::lookahead_coverage_offsets].
|
||
pub fn lookahead_coverages(&self) -> ArrayOfOffsets<'a, CoverageTable<'a>, Offset16> {
|
||
let data = self.data;
|
||
let offsets = self.lookahead_coverage_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
|
||
/// Number of SequenceLookupRecords
|
||
pub fn seq_lookup_count(&self) -> u16 {
|
||
let range = self.shape.seq_lookup_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of SequenceLookupRecords
|
||
pub fn seq_lookup_records(&self) -> &'a [SequenceLookupRecord] {
|
||
let range = self.shape.seq_lookup_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedSequenceContextFormat3<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ChainedSequenceContextFormat3"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"backtrack_glyph_count",
|
||
self.backtrack_glyph_count(),
|
||
)),
|
||
2usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"backtrack_coverage_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<CoverageTable>(),
|
||
self.backtrack_coverage_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<CoverageTable>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
3usize => Some(Field::new("input_glyph_count", self.input_glyph_count())),
|
||
4usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"input_coverage_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<CoverageTable>(),
|
||
self.input_coverage_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<CoverageTable>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
5usize => Some(Field::new(
|
||
"lookahead_glyph_count",
|
||
self.lookahead_glyph_count(),
|
||
)),
|
||
6usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"lookahead_coverage_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<CoverageTable>(),
|
||
self.lookahead_coverage_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<CoverageTable>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
7usize => Some(Field::new("seq_lookup_count", self.seq_lookup_count())),
|
||
8usize => Some(Field::new(
|
||
"seq_lookup_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(SequenceLookupRecord),
|
||
self.seq_lookup_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ChainedSequenceContextFormat3<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
#[derive(Clone)]
|
||
pub enum ChainedSequenceContext<'a> {
|
||
Format1(ChainedSequenceContextFormat1<'a>),
|
||
Format2(ChainedSequenceContextFormat2<'a>),
|
||
Format3(ChainedSequenceContextFormat3<'a>),
|
||
}
|
||
|
||
impl<'a> ChainedSequenceContext<'a> {
|
||
///Return the `FontData` used to resolve offsets for this table.
|
||
pub fn offset_data(&self) -> FontData<'a> {
|
||
match self {
|
||
Self::Format1(item) => item.offset_data(),
|
||
Self::Format2(item) => item.offset_data(),
|
||
Self::Format3(item) => item.offset_data(),
|
||
}
|
||
}
|
||
|
||
/// Format identifier: format = 1
|
||
pub fn format(&self) -> u16 {
|
||
match self {
|
||
Self::Format1(item) => item.format(),
|
||
Self::Format2(item) => item.format(),
|
||
Self::Format3(item) => item.format(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ChainedSequenceContext<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let format: u16 = data.read_at(0usize)?;
|
||
match format {
|
||
ChainedSequenceContextFormat1Marker::FORMAT => Ok(Self::Format1(FontRead::read(data)?)),
|
||
ChainedSequenceContextFormat2Marker::FORMAT => Ok(Self::Format2(FontRead::read(data)?)),
|
||
ChainedSequenceContextFormat3Marker::FORMAT => Ok(Self::Format3(FontRead::read(data)?)),
|
||
other => Err(ReadError::InvalidFormat(other.into())),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ChainedSequenceContext<'_> {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
match self {
|
||
Self::Format1(item) => item.min_byte_range(),
|
||
Self::Format2(item) => item.min_byte_range(),
|
||
Self::Format3(item) => item.min_byte_range(),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> ChainedSequenceContext<'a> {
|
||
fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
|
||
match self {
|
||
Self::Format1(table) => table,
|
||
Self::Format2(table) => table,
|
||
Self::Format3(table) => table,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl std::fmt::Debug for ChainedSequenceContext<'_> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
self.dyn_inner().fmt(f)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ChainedSequenceContext<'a> {
|
||
fn type_name(&self) -> &str {
|
||
self.dyn_inner().type_name()
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
self.dyn_inner().get_field(idx)
|
||
}
|
||
}
|
||
|
||
/// [Device](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
|
||
/// delta formats
|
||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||
#[repr(u16)]
|
||
#[allow(clippy::manual_non_exhaustive)]
|
||
pub enum DeltaFormat {
|
||
/// Signed 2-bit value, 8 values per uint16
|
||
#[default]
|
||
Local2BitDeltas = 0x0001,
|
||
/// Signed 4-bit value, 4 values per uint16
|
||
Local4BitDeltas = 0x0002,
|
||
/// Signed 8-bit value, 2 values per uint16
|
||
Local8BitDeltas = 0x0003,
|
||
/// VariationIndex table, contains a delta-set index pair.
|
||
VariationIndex = 0x8000,
|
||
#[doc(hidden)]
|
||
/// If font data is malformed we will map unknown values to this variant
|
||
Unknown,
|
||
}
|
||
|
||
impl DeltaFormat {
|
||
/// Create from a raw scalar.
|
||
///
|
||
/// This will never fail; unknown values will be mapped to the `Unknown` variant
|
||
pub fn new(raw: u16) -> Self {
|
||
match raw {
|
||
0x0001 => Self::Local2BitDeltas,
|
||
0x0002 => Self::Local4BitDeltas,
|
||
0x0003 => Self::Local8BitDeltas,
|
||
0x8000 => Self::VariationIndex,
|
||
_ => Self::Unknown,
|
||
}
|
||
}
|
||
}
|
||
|
||
impl font_types::Scalar for DeltaFormat {
|
||
type Raw = <u16 as font_types::Scalar>::Raw;
|
||
fn to_raw(self) -> Self::Raw {
|
||
(self as u16).to_raw()
|
||
}
|
||
fn from_raw(raw: Self::Raw) -> Self {
|
||
let t = <u16>::from_raw(raw);
|
||
Self::new(t)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> From<DeltaFormat> for FieldType<'a> {
|
||
fn from(src: DeltaFormat) -> FieldType<'a> {
|
||
(src as u16).into()
|
||
}
|
||
}
|
||
|
||
/// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct DeviceMarker {
|
||
delta_value_byte_len: usize,
|
||
}
|
||
|
||
impl DeviceMarker {
|
||
pub fn start_size_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn end_size_byte_range(&self) -> Range<usize> {
|
||
let start = self.start_size_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn delta_format_byte_range(&self) -> Range<usize> {
|
||
let start = self.end_size_byte_range().end;
|
||
start..start + DeltaFormat::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn delta_value_byte_range(&self) -> Range<usize> {
|
||
let start = self.delta_format_byte_range().end;
|
||
start..start + self.delta_value_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for DeviceMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.delta_value_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for Device<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let start_size: u16 = cursor.read()?;
|
||
let end_size: u16 = cursor.read()?;
|
||
let delta_format: DeltaFormat = cursor.read()?;
|
||
let delta_value_byte_len = (DeltaFormat::value_count(delta_format, start_size, end_size))
|
||
.checked_mul(u16::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(delta_value_byte_len);
|
||
cursor.finish(DeviceMarker {
|
||
delta_value_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Device Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#device-and-variationindex-tables)
|
||
pub type Device<'a> = TableRef<'a, DeviceMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> Device<'a> {
|
||
/// Smallest size to correct, in ppem
|
||
pub fn start_size(&self) -> u16 {
|
||
let range = self.shape.start_size_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Largest size to correct, in ppem
|
||
pub fn end_size(&self) -> u16 {
|
||
let range = self.shape.end_size_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Format of deltaValue array data: 0x0001, 0x0002, or 0x0003
|
||
pub fn delta_format(&self) -> DeltaFormat {
|
||
let range = self.shape.delta_format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of compressed data
|
||
pub fn delta_value(&self) -> &'a [BigEndian<u16>] {
|
||
let range = self.shape.delta_value_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for Device<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"Device"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("start_size", self.start_size())),
|
||
1usize => Some(Field::new("end_size", self.end_size())),
|
||
2usize => Some(Field::new("delta_format", self.delta_format())),
|
||
3usize => Some(Field::new("delta_value", self.delta_value())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for Device<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Variation index table
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct VariationIndexMarker {}
|
||
|
||
impl VariationIndexMarker {
|
||
pub fn delta_set_outer_index_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn delta_set_inner_index_byte_range(&self) -> Range<usize> {
|
||
let start = self.delta_set_outer_index_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn delta_format_byte_range(&self) -> Range<usize> {
|
||
let start = self.delta_set_inner_index_byte_range().end;
|
||
start..start + DeltaFormat::RAW_BYTE_LEN
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for VariationIndexMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.delta_format_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for VariationIndex<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<DeltaFormat>();
|
||
cursor.finish(VariationIndexMarker {})
|
||
}
|
||
}
|
||
|
||
/// Variation index table
|
||
pub type VariationIndex<'a> = TableRef<'a, VariationIndexMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> VariationIndex<'a> {
|
||
/// A delta-set outer index — used to select an item variation
|
||
/// data subtable within the item variation store.
|
||
pub fn delta_set_outer_index(&self) -> u16 {
|
||
let range = self.shape.delta_set_outer_index_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// A delta-set inner index — used to select a delta-set row
|
||
/// within an item variation data subtable.
|
||
pub fn delta_set_inner_index(&self) -> u16 {
|
||
let range = self.shape.delta_set_inner_index_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Format, = 0x8000
|
||
pub fn delta_format(&self) -> DeltaFormat {
|
||
let range = self.shape.delta_format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for VariationIndex<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"VariationIndex"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new(
|
||
"delta_set_outer_index",
|
||
self.delta_set_outer_index(),
|
||
)),
|
||
1usize => Some(Field::new(
|
||
"delta_set_inner_index",
|
||
self.delta_set_inner_index(),
|
||
)),
|
||
2usize => Some(Field::new("delta_format", self.delta_format())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for VariationIndex<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Either a [Device] table (in a non-variable font) or a [VariationIndex] table (in a variable font)
|
||
#[derive(Clone)]
|
||
pub enum DeviceOrVariationIndex<'a> {
|
||
Device(Device<'a>),
|
||
VariationIndex(VariationIndex<'a>),
|
||
}
|
||
|
||
impl<'a> DeviceOrVariationIndex<'a> {
|
||
///Return the `FontData` used to resolve offsets for this table.
|
||
pub fn offset_data(&self) -> FontData<'a> {
|
||
match self {
|
||
Self::Device(item) => item.offset_data(),
|
||
Self::VariationIndex(item) => item.offset_data(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for DeviceOrVariationIndex<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let format: DeltaFormat = data.read_at(4usize)?;
|
||
|
||
#[allow(clippy::redundant_guards)]
|
||
match format {
|
||
format if format != DeltaFormat::VariationIndex => {
|
||
Ok(Self::Device(FontRead::read(data)?))
|
||
}
|
||
format if format == DeltaFormat::VariationIndex => {
|
||
Ok(Self::VariationIndex(FontRead::read(data)?))
|
||
}
|
||
other => Err(ReadError::InvalidFormat(other.into())),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for DeviceOrVariationIndex<'_> {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
match self {
|
||
Self::Device(item) => item.min_byte_range(),
|
||
Self::VariationIndex(item) => item.min_byte_range(),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> DeviceOrVariationIndex<'a> {
|
||
fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
|
||
match self {
|
||
Self::Device(table) => table,
|
||
Self::VariationIndex(table) => table,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl std::fmt::Debug for DeviceOrVariationIndex<'_> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
self.dyn_inner().fmt(f)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for DeviceOrVariationIndex<'a> {
|
||
fn type_name(&self) -> &str {
|
||
self.dyn_inner().type_name()
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
self.dyn_inner().get_field(idx)
|
||
}
|
||
}
|
||
|
||
/// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct FeatureVariationsMarker {
|
||
feature_variation_records_byte_len: usize,
|
||
}
|
||
|
||
impl FeatureVariationsMarker {
|
||
pub fn version_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + MajorMinor::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feature_variation_record_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.version_byte_range().end;
|
||
start..start + u32::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feature_variation_records_byte_range(&self) -> Range<usize> {
|
||
let start = self.feature_variation_record_count_byte_range().end;
|
||
start..start + self.feature_variation_records_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for FeatureVariationsMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.feature_variation_records_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for FeatureVariations<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<MajorMinor>();
|
||
let feature_variation_record_count: u32 = cursor.read()?;
|
||
let feature_variation_records_byte_len = (feature_variation_record_count as usize)
|
||
.checked_mul(FeatureVariationRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(feature_variation_records_byte_len);
|
||
cursor.finish(FeatureVariationsMarker {
|
||
feature_variation_records_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [FeatureVariations Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featurevariations-table)
|
||
pub type FeatureVariations<'a> = TableRef<'a, FeatureVariationsMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> FeatureVariations<'a> {
|
||
pub fn version(&self) -> MajorMinor {
|
||
let range = self.shape.version_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of feature variation records.
|
||
pub fn feature_variation_record_count(&self) -> u32 {
|
||
let range = self.shape.feature_variation_record_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of feature variation records.
|
||
pub fn feature_variation_records(&self) -> &'a [FeatureVariationRecord] {
|
||
let range = self.shape.feature_variation_records_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for FeatureVariations<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"FeatureVariations"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("version", self.version())),
|
||
1usize => Some(Field::new(
|
||
"feature_variation_record_count",
|
||
self.feature_variation_record_count(),
|
||
)),
|
||
2usize => Some(Field::new(
|
||
"feature_variation_records",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(FeatureVariationRecord),
|
||
self.feature_variation_records(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for FeatureVariations<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Part of [FeatureVariations]
|
||
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct FeatureVariationRecord {
|
||
/// Offset to a condition set table, from beginning of
|
||
/// FeatureVariations table.
|
||
pub condition_set_offset: BigEndian<Nullable<Offset32>>,
|
||
/// Offset to a feature table substitution table, from beginning of
|
||
/// the FeatureVariations table.
|
||
pub feature_table_substitution_offset: BigEndian<Nullable<Offset32>>,
|
||
}
|
||
|
||
impl FeatureVariationRecord {
|
||
/// Offset to a condition set table, from beginning of
|
||
/// FeatureVariations table.
|
||
pub fn condition_set_offset(&self) -> Nullable<Offset32> {
|
||
self.condition_set_offset.get()
|
||
}
|
||
|
||
/// Offset to a condition set table, from beginning of
|
||
/// FeatureVariations table.
|
||
///
|
||
/// The `data` argument should be retrieved from the parent table
|
||
/// By calling its `offset_data` method.
|
||
pub fn condition_set<'a>(
|
||
&self,
|
||
data: FontData<'a>,
|
||
) -> Option<Result<ConditionSet<'a>, ReadError>> {
|
||
self.condition_set_offset().resolve(data)
|
||
}
|
||
|
||
/// Offset to a feature table substitution table, from beginning of
|
||
/// the FeatureVariations table.
|
||
pub fn feature_table_substitution_offset(&self) -> Nullable<Offset32> {
|
||
self.feature_table_substitution_offset.get()
|
||
}
|
||
|
||
/// Offset to a feature table substitution table, from beginning of
|
||
/// the FeatureVariations table.
|
||
///
|
||
/// The `data` argument should be retrieved from the parent table
|
||
/// By calling its `offset_data` method.
|
||
pub fn feature_table_substitution<'a>(
|
||
&self,
|
||
data: FontData<'a>,
|
||
) -> Option<Result<FeatureTableSubstitution<'a>, ReadError>> {
|
||
self.feature_table_substitution_offset().resolve(data)
|
||
}
|
||
}
|
||
|
||
impl FixedSize for FeatureVariationRecord {
|
||
const RAW_BYTE_LEN: usize = Offset32::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for FeatureVariationRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "FeatureVariationRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new(
|
||
"condition_set_offset",
|
||
FieldType::offset(self.condition_set_offset(), self.condition_set(_data)),
|
||
)),
|
||
1usize => Some(Field::new(
|
||
"feature_table_substitution_offset",
|
||
FieldType::offset(
|
||
self.feature_table_substitution_offset(),
|
||
self.feature_table_substitution(_data),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ConditionSetMarker {
|
||
condition_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ConditionSetMarker {
|
||
pub fn condition_count_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn condition_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.condition_count_byte_range().end;
|
||
start..start + self.condition_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ConditionSetMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.condition_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ConditionSet<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
let condition_count: u16 = cursor.read()?;
|
||
let condition_offsets_byte_len = (condition_count as usize)
|
||
.checked_mul(Offset32::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(condition_offsets_byte_len);
|
||
cursor.finish(ConditionSetMarker {
|
||
condition_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [ConditionSet Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#conditionset-table)
|
||
pub type ConditionSet<'a> = TableRef<'a, ConditionSetMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ConditionSet<'a> {
|
||
/// Number of conditions for this condition set.
|
||
pub fn condition_count(&self) -> u16 {
|
||
let range = self.shape.condition_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of offsets to condition tables, from beginning of the
|
||
/// ConditionSet table.
|
||
pub fn condition_offsets(&self) -> &'a [BigEndian<Offset32>] {
|
||
let range = self.shape.condition_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
|
||
pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset32> {
|
||
let data = self.data;
|
||
let offsets = self.condition_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ConditionSet<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ConditionSet"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("condition_count", self.condition_count())),
|
||
1usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"condition_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<Condition>(),
|
||
self.condition_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<Condition>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ConditionSet<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// [Condition Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table)
|
||
///
|
||
/// Formats 2..5 are implementations of specification changes currently under debate at ISO for an OFF
|
||
/// update. For the time being the specification is <https://github.com/harfbuzz/boring-expansion-spec/blob/main/ConditionTree.md>.
|
||
#[derive(Clone)]
|
||
pub enum Condition<'a> {
|
||
Format1AxisRange(ConditionFormat1<'a>),
|
||
Format2VariableValue(ConditionFormat2<'a>),
|
||
Format3And(ConditionFormat3<'a>),
|
||
Format4Or(ConditionFormat4<'a>),
|
||
Format5Negate(ConditionFormat5<'a>),
|
||
}
|
||
|
||
impl<'a> Condition<'a> {
|
||
///Return the `FontData` used to resolve offsets for this table.
|
||
pub fn offset_data(&self) -> FontData<'a> {
|
||
match self {
|
||
Self::Format1AxisRange(item) => item.offset_data(),
|
||
Self::Format2VariableValue(item) => item.offset_data(),
|
||
Self::Format3And(item) => item.offset_data(),
|
||
Self::Format4Or(item) => item.offset_data(),
|
||
Self::Format5Negate(item) => item.offset_data(),
|
||
}
|
||
}
|
||
|
||
/// Format, = 1
|
||
pub fn format(&self) -> u16 {
|
||
match self {
|
||
Self::Format1AxisRange(item) => item.format(),
|
||
Self::Format2VariableValue(item) => item.format(),
|
||
Self::Format3And(item) => item.format(),
|
||
Self::Format4Or(item) => item.format(),
|
||
Self::Format5Negate(item) => item.format(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for Condition<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let format: u16 = data.read_at(0usize)?;
|
||
match format {
|
||
ConditionFormat1Marker::FORMAT => Ok(Self::Format1AxisRange(FontRead::read(data)?)),
|
||
ConditionFormat2Marker::FORMAT => Ok(Self::Format2VariableValue(FontRead::read(data)?)),
|
||
ConditionFormat3Marker::FORMAT => Ok(Self::Format3And(FontRead::read(data)?)),
|
||
ConditionFormat4Marker::FORMAT => Ok(Self::Format4Or(FontRead::read(data)?)),
|
||
ConditionFormat5Marker::FORMAT => Ok(Self::Format5Negate(FontRead::read(data)?)),
|
||
other => Err(ReadError::InvalidFormat(other.into())),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for Condition<'_> {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
match self {
|
||
Self::Format1AxisRange(item) => item.min_byte_range(),
|
||
Self::Format2VariableValue(item) => item.min_byte_range(),
|
||
Self::Format3And(item) => item.min_byte_range(),
|
||
Self::Format4Or(item) => item.min_byte_range(),
|
||
Self::Format5Negate(item) => item.min_byte_range(),
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> Condition<'a> {
|
||
fn dyn_inner<'b>(&'b self) -> &'b dyn SomeTable<'a> {
|
||
match self {
|
||
Self::Format1AxisRange(table) => table,
|
||
Self::Format2VariableValue(table) => table,
|
||
Self::Format3And(table) => table,
|
||
Self::Format4Or(table) => table,
|
||
Self::Format5Negate(table) => table,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl std::fmt::Debug for Condition<'_> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
self.dyn_inner().fmt(f)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for Condition<'a> {
|
||
fn type_name(&self) -> &str {
|
||
self.dyn_inner().type_name()
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
self.dyn_inner().get_field(idx)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ConditionFormat1Marker {
|
||
const FORMAT: u16 = 1;
|
||
}
|
||
|
||
/// [Condition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table-format-1-font-variation-axis-range): Font Variation Axis Range
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ConditionFormat1Marker {}
|
||
|
||
impl ConditionFormat1Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn axis_index_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn filter_range_min_value_byte_range(&self) -> Range<usize> {
|
||
let start = self.axis_index_byte_range().end;
|
||
start..start + F2Dot14::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn filter_range_max_value_byte_range(&self) -> Range<usize> {
|
||
let start = self.filter_range_min_value_byte_range().end;
|
||
start..start + F2Dot14::RAW_BYTE_LEN
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ConditionFormat1Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.filter_range_max_value_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ConditionFormat1<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<F2Dot14>();
|
||
cursor.advance::<F2Dot14>();
|
||
cursor.finish(ConditionFormat1Marker {})
|
||
}
|
||
}
|
||
|
||
/// [Condition Table Format 1](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#condition-table-format-1-font-variation-axis-range): Font Variation Axis Range
|
||
pub type ConditionFormat1<'a> = TableRef<'a, ConditionFormat1Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ConditionFormat1<'a> {
|
||
/// Format, = 1
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Index (zero-based) for the variation axis within the 'fvar'
|
||
/// table.
|
||
pub fn axis_index(&self) -> u16 {
|
||
let range = self.shape.axis_index_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Minimum value of the font variation instances that satisfy this
|
||
/// condition.
|
||
pub fn filter_range_min_value(&self) -> F2Dot14 {
|
||
let range = self.shape.filter_range_min_value_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Maximum value of the font variation instances that satisfy this
|
||
/// condition.
|
||
pub fn filter_range_max_value(&self) -> F2Dot14 {
|
||
let range = self.shape.filter_range_max_value_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ConditionFormat1<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ConditionFormat1"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new("axis_index", self.axis_index())),
|
||
2usize => Some(Field::new(
|
||
"filter_range_min_value",
|
||
self.filter_range_min_value(),
|
||
)),
|
||
3usize => Some(Field::new(
|
||
"filter_range_max_value",
|
||
self.filter_range_max_value(),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ConditionFormat1<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ConditionFormat2Marker {
|
||
const FORMAT: u16 = 2;
|
||
}
|
||
|
||
/// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ConditionFormat2Marker {}
|
||
|
||
impl ConditionFormat2Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn default_value_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + i16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn var_index_byte_range(&self) -> Range<usize> {
|
||
let start = self.default_value_byte_range().end;
|
||
start..start + u32::RAW_BYTE_LEN
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ConditionFormat2Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.var_index_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ConditionFormat2<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<i16>();
|
||
cursor.advance::<u32>();
|
||
cursor.finish(ConditionFormat2Marker {})
|
||
}
|
||
}
|
||
|
||
/// [Condition Table Format 2](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3237-L3255): Variation index
|
||
pub type ConditionFormat2<'a> = TableRef<'a, ConditionFormat2Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ConditionFormat2<'a> {
|
||
/// Format, = 2
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Value at default instance.
|
||
pub fn default_value(&self) -> i16 {
|
||
let range = self.shape.default_value_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Variation index to vary the value based on current designspace location.
|
||
pub fn var_index(&self) -> u32 {
|
||
let range = self.shape.var_index_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ConditionFormat2<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ConditionFormat2"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new("default_value", self.default_value())),
|
||
2usize => Some(Field::new("var_index", self.var_index())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ConditionFormat2<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ConditionFormat3Marker {
|
||
const FORMAT: u16 = 3;
|
||
}
|
||
|
||
/// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ConditionFormat3Marker {
|
||
condition_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ConditionFormat3Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn condition_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + u8::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn condition_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.condition_count_byte_range().end;
|
||
start..start + self.condition_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ConditionFormat3Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.condition_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ConditionFormat3<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let condition_count: u8 = cursor.read()?;
|
||
let condition_offsets_byte_len = (condition_count as usize)
|
||
.checked_mul(Offset24::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(condition_offsets_byte_len);
|
||
cursor.finish(ConditionFormat3Marker {
|
||
condition_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Condition Table Format 3](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3257-L3275): AND
|
||
pub type ConditionFormat3<'a> = TableRef<'a, ConditionFormat3Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ConditionFormat3<'a> {
|
||
/// Format, = 3
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of conditions.
|
||
pub fn condition_count(&self) -> u8 {
|
||
let range = self.shape.condition_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of condition tables for this conjunction (AND) expression.
|
||
pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
|
||
let range = self.shape.condition_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
|
||
pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
|
||
let data = self.data;
|
||
let offsets = self.condition_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ConditionFormat3<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ConditionFormat3"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new("condition_count", self.condition_count())),
|
||
2usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"condition_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<Condition>(),
|
||
self.condition_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<Condition>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ConditionFormat3<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ConditionFormat4Marker {
|
||
const FORMAT: u16 = 4;
|
||
}
|
||
|
||
/// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ConditionFormat4Marker {
|
||
condition_offsets_byte_len: usize,
|
||
}
|
||
|
||
impl ConditionFormat4Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn condition_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + u8::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn condition_offsets_byte_range(&self) -> Range<usize> {
|
||
let start = self.condition_count_byte_range().end;
|
||
start..start + self.condition_offsets_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ConditionFormat4Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.condition_offsets_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ConditionFormat4<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
let condition_count: u8 = cursor.read()?;
|
||
let condition_offsets_byte_len = (condition_count as usize)
|
||
.checked_mul(Offset24::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(condition_offsets_byte_len);
|
||
cursor.finish(ConditionFormat4Marker {
|
||
condition_offsets_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [Condition Table Format 4](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3276-L3295): OR
|
||
pub type ConditionFormat4<'a> = TableRef<'a, ConditionFormat4Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ConditionFormat4<'a> {
|
||
/// Format, = 4
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of conditions.
|
||
pub fn condition_count(&self) -> u8 {
|
||
let range = self.shape.condition_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of condition tables for this disjunction (OR) expression.
|
||
pub fn condition_offsets(&self) -> &'a [BigEndian<Offset24>] {
|
||
let range = self.shape.condition_offsets_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
|
||
/// A dynamically resolving wrapper for [`condition_offsets`][Self::condition_offsets].
|
||
pub fn conditions(&self) -> ArrayOfOffsets<'a, Condition<'a>, Offset24> {
|
||
let data = self.data;
|
||
let offsets = self.condition_offsets();
|
||
ArrayOfOffsets::new(offsets, data, ())
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ConditionFormat4<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ConditionFormat4"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new("condition_count", self.condition_count())),
|
||
2usize => Some({
|
||
let data = self.data;
|
||
Field::new(
|
||
"condition_offsets",
|
||
FieldType::array_of_offsets(
|
||
better_type_name::<Condition>(),
|
||
self.condition_offsets(),
|
||
move |off| {
|
||
let target = off.get().resolve::<Condition>(data);
|
||
FieldType::offset(off.get(), target)
|
||
},
|
||
),
|
||
)
|
||
}),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ConditionFormat4<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for ConditionFormat5Marker {
|
||
const FORMAT: u16 = 5;
|
||
}
|
||
|
||
/// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct ConditionFormat5Marker {}
|
||
|
||
impl ConditionFormat5Marker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn condition_offset_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + Offset24::RAW_BYTE_LEN
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for ConditionFormat5Marker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.condition_offset_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for ConditionFormat5<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<Offset24>();
|
||
cursor.finish(ConditionFormat5Marker {})
|
||
}
|
||
}
|
||
|
||
/// [Condition Table Format 5](https://github.com/fonttools/fonttools/blob/5e6b12d12fa08abafbeb7570f47707fbedf69a45/Lib/fontTools/ttLib/tables/otData.py#L3296-L3308): NOT
|
||
pub type ConditionFormat5<'a> = TableRef<'a, ConditionFormat5Marker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> ConditionFormat5<'a> {
|
||
/// Format, = 5
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Condition to negate.
|
||
pub fn condition_offset(&self) -> Offset24 {
|
||
let range = self.shape.condition_offset_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Attempt to resolve [`condition_offset`][Self::condition_offset].
|
||
pub fn condition(&self) -> Result<Condition<'a>, ReadError> {
|
||
let data = self.data;
|
||
self.condition_offset().resolve(data)
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for ConditionFormat5<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"ConditionFormat5"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"condition_offset",
|
||
FieldType::offset(self.condition_offset(), self.condition()),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for ConditionFormat5<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct FeatureTableSubstitutionMarker {
|
||
substitutions_byte_len: usize,
|
||
}
|
||
|
||
impl FeatureTableSubstitutionMarker {
|
||
pub fn version_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + MajorMinor::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn substitution_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.version_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn substitutions_byte_range(&self) -> Range<usize> {
|
||
let start = self.substitution_count_byte_range().end;
|
||
start..start + self.substitutions_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for FeatureTableSubstitutionMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.substitutions_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for FeatureTableSubstitution<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<MajorMinor>();
|
||
let substitution_count: u16 = cursor.read()?;
|
||
let substitutions_byte_len = (substitution_count as usize)
|
||
.checked_mul(FeatureTableSubstitutionRecord::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(substitutions_byte_len);
|
||
cursor.finish(FeatureTableSubstitutionMarker {
|
||
substitutions_byte_len,
|
||
})
|
||
}
|
||
}
|
||
|
||
/// [FeatureTableSubstitution Table](https://docs.microsoft.com/en-us/typography/opentype/spec/chapter2#featuretablesubstitution-table)
|
||
pub type FeatureTableSubstitution<'a> = TableRef<'a, FeatureTableSubstitutionMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> FeatureTableSubstitution<'a> {
|
||
/// Major & minor version of the table: (1, 0)
|
||
pub fn version(&self) -> MajorMinor {
|
||
let range = self.shape.version_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of feature table substitution records.
|
||
pub fn substitution_count(&self) -> u16 {
|
||
let range = self.shape.substitution_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Array of feature table substitution records.
|
||
pub fn substitutions(&self) -> &'a [FeatureTableSubstitutionRecord] {
|
||
let range = self.shape.substitutions_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for FeatureTableSubstitution<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"FeatureTableSubstitution"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("version", self.version())),
|
||
1usize => Some(Field::new("substitution_count", self.substitution_count())),
|
||
2usize => Some(Field::new(
|
||
"substitutions",
|
||
traversal::FieldType::array_of_records(
|
||
stringify!(FeatureTableSubstitutionRecord),
|
||
self.substitutions(),
|
||
self.offset_data(),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for FeatureTableSubstitution<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
/// Used in [FeatureTableSubstitution]
|
||
#[derive(Clone, Debug, Copy, bytemuck :: AnyBitPattern)]
|
||
#[repr(C)]
|
||
#[repr(packed)]
|
||
pub struct FeatureTableSubstitutionRecord {
|
||
/// The feature table index to match.
|
||
pub feature_index: BigEndian<u16>,
|
||
/// Offset to an alternate feature table, from start of the
|
||
/// FeatureTableSubstitution table.
|
||
pub alternate_feature_offset: BigEndian<Offset32>,
|
||
}
|
||
|
||
impl FeatureTableSubstitutionRecord {
|
||
/// The feature table index to match.
|
||
pub fn feature_index(&self) -> u16 {
|
||
self.feature_index.get()
|
||
}
|
||
|
||
/// Offset to an alternate feature table, from start of the
|
||
/// FeatureTableSubstitution table.
|
||
pub fn alternate_feature_offset(&self) -> Offset32 {
|
||
self.alternate_feature_offset.get()
|
||
}
|
||
}
|
||
|
||
impl FixedSize for FeatureTableSubstitutionRecord {
|
||
const RAW_BYTE_LEN: usize = u16::RAW_BYTE_LEN + Offset32::RAW_BYTE_LEN;
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeRecord<'a> for FeatureTableSubstitutionRecord {
|
||
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
|
||
RecordResolver {
|
||
name: "FeatureTableSubstitutionRecord",
|
||
get_field: Box::new(move |idx, _data| match idx {
|
||
0usize => Some(Field::new("feature_index", self.feature_index())),
|
||
1usize => Some(Field::new(
|
||
"alternate_feature_offset",
|
||
FieldType::offset(
|
||
self.alternate_feature_offset(),
|
||
self.alternate_feature(_data),
|
||
),
|
||
)),
|
||
_ => None,
|
||
}),
|
||
data,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct SizeParamsMarker {}
|
||
|
||
impl SizeParamsMarker {
|
||
pub fn design_size_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn identifier_byte_range(&self) -> Range<usize> {
|
||
let start = self.design_size_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn name_entry_byte_range(&self) -> Range<usize> {
|
||
let start = self.identifier_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn range_start_byte_range(&self) -> Range<usize> {
|
||
let start = self.name_entry_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn range_end_byte_range(&self) -> Range<usize> {
|
||
let start = self.range_start_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for SizeParamsMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.range_end_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for SizeParams<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<u16>();
|
||
cursor.finish(SizeParamsMarker {})
|
||
}
|
||
}
|
||
|
||
pub type SizeParams<'a> = TableRef<'a, SizeParamsMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> SizeParams<'a> {
|
||
/// The first value represents the design size in 720/inch units (decipoints).
|
||
///
|
||
/// The design size entry must be non-zero. When there is a design size but
|
||
/// no recommended size range, the rest of the array will consist of zeros.
|
||
pub fn design_size(&self) -> u16 {
|
||
let range = self.shape.design_size_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The second value has no independent meaning, but serves as an identifier that associates fonts in a subfamily.
|
||
///
|
||
/// All fonts which share a Typographic or Font Family name and which differ
|
||
/// only by size range shall have the same subfamily value, and no fonts
|
||
/// which differ in weight or style shall have the same subfamily value.
|
||
/// If this value is zero, the remaining fields in the array will be ignored.
|
||
pub fn identifier(&self) -> u16 {
|
||
let range = self.shape.identifier_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The third value enables applications to use a single name for the subfamily identified by the second value.
|
||
///
|
||
/// If the preceding value is non-zero, this value must be set in the range
|
||
/// 256 – 32767 (inclusive). It records the value of a field in the 'name'
|
||
/// table, which must contain English-language strings encoded in Windows
|
||
/// Unicode and Macintosh Roman, and may contain additional strings localized
|
||
/// to other scripts and languages. Each of these strings is the name
|
||
/// an application should use, in combination with the family name, to
|
||
/// represent the subfamily in a menu. Applications will choose the
|
||
/// appropriate version based on their selection criteria.
|
||
pub fn name_entry(&self) -> u16 {
|
||
let range = self.shape.name_entry_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The fourth and fifth values represent the small end of the recommended
|
||
/// usage range (exclusive) and the large end of the recommended usage range
|
||
/// (inclusive), stored in 720/inch units (decipoints).
|
||
///
|
||
/// Ranges must not overlap, and should generally be contiguous.
|
||
pub fn range_start(&self) -> u16 {
|
||
let range = self.shape.range_start_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
pub fn range_end(&self) -> u16 {
|
||
let range = self.shape.range_end_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for SizeParams<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"SizeParams"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("design_size", self.design_size())),
|
||
1usize => Some(Field::new("identifier", self.identifier())),
|
||
2usize => Some(Field::new("name_entry", self.name_entry())),
|
||
3usize => Some(Field::new("range_start", self.range_start())),
|
||
4usize => Some(Field::new("range_end", self.range_end())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for SizeParams<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct StylisticSetParamsMarker {}
|
||
|
||
impl StylisticSetParamsMarker {
|
||
pub fn version_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn ui_name_id_byte_range(&self) -> Range<usize> {
|
||
let start = self.version_byte_range().end;
|
||
start..start + NameId::RAW_BYTE_LEN
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for StylisticSetParamsMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.ui_name_id_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for StylisticSetParams<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<NameId>();
|
||
cursor.finish(StylisticSetParamsMarker {})
|
||
}
|
||
}
|
||
|
||
pub type StylisticSetParams<'a> = TableRef<'a, StylisticSetParamsMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> StylisticSetParams<'a> {
|
||
pub fn version(&self) -> u16 {
|
||
let range = self.shape.version_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The 'name' table name ID that specifies a string (or strings, for
|
||
/// multiple languages) for a user-interface label for this feature.
|
||
///
|
||
/// The value of uiLabelNameId is expected to be in the font-specific name
|
||
/// ID range (256-32767), though that is not a requirement in this Feature
|
||
/// Parameters specification. The user-interface label for the feature can
|
||
/// be provided in multiple languages. An English string should be included
|
||
/// as a fallback. The string should be kept to a minimal length to fit
|
||
/// comfortably with different application interfaces.
|
||
pub fn ui_name_id(&self) -> NameId {
|
||
let range = self.shape.ui_name_id_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for StylisticSetParams<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"StylisticSetParams"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("version", self.version())),
|
||
1usize => Some(Field::new("ui_name_id", self.ui_name_id())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for StylisticSetParams<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|
||
|
||
impl Format<u16> for CharacterVariantParamsMarker {
|
||
const FORMAT: u16 = 0;
|
||
}
|
||
|
||
/// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99)
|
||
#[derive(Debug, Clone, Copy)]
|
||
#[doc(hidden)]
|
||
pub struct CharacterVariantParamsMarker {
|
||
character_byte_len: usize,
|
||
}
|
||
|
||
impl CharacterVariantParamsMarker {
|
||
pub fn format_byte_range(&self) -> Range<usize> {
|
||
let start = 0;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feat_ui_label_name_id_byte_range(&self) -> Range<usize> {
|
||
let start = self.format_byte_range().end;
|
||
start..start + NameId::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn feat_ui_tooltip_text_name_id_byte_range(&self) -> Range<usize> {
|
||
let start = self.feat_ui_label_name_id_byte_range().end;
|
||
start..start + NameId::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn sample_text_name_id_byte_range(&self) -> Range<usize> {
|
||
let start = self.feat_ui_tooltip_text_name_id_byte_range().end;
|
||
start..start + NameId::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn num_named_parameters_byte_range(&self) -> Range<usize> {
|
||
let start = self.sample_text_name_id_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn first_param_ui_label_name_id_byte_range(&self) -> Range<usize> {
|
||
let start = self.num_named_parameters_byte_range().end;
|
||
start..start + NameId::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn char_count_byte_range(&self) -> Range<usize> {
|
||
let start = self.first_param_ui_label_name_id_byte_range().end;
|
||
start..start + u16::RAW_BYTE_LEN
|
||
}
|
||
|
||
pub fn character_byte_range(&self) -> Range<usize> {
|
||
let start = self.char_count_byte_range().end;
|
||
start..start + self.character_byte_len
|
||
}
|
||
}
|
||
|
||
impl MinByteRange for CharacterVariantParamsMarker {
|
||
fn min_byte_range(&self) -> Range<usize> {
|
||
0..self.character_byte_range().end
|
||
}
|
||
}
|
||
|
||
impl<'a> FontRead<'a> for CharacterVariantParams<'a> {
|
||
fn read(data: FontData<'a>) -> Result<Self, ReadError> {
|
||
let mut cursor = data.cursor();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<NameId>();
|
||
cursor.advance::<NameId>();
|
||
cursor.advance::<NameId>();
|
||
cursor.advance::<u16>();
|
||
cursor.advance::<NameId>();
|
||
let char_count: u16 = cursor.read()?;
|
||
let character_byte_len = (char_count as usize)
|
||
.checked_mul(Uint24::RAW_BYTE_LEN)
|
||
.ok_or(ReadError::OutOfBounds)?;
|
||
cursor.advance_by(character_byte_len);
|
||
cursor.finish(CharacterVariantParamsMarker { character_byte_len })
|
||
}
|
||
}
|
||
|
||
/// featureParams for ['cv01'-'cv99'](https://docs.microsoft.com/en-us/typography/opentype/spec/features_ae#cv01-cv99)
|
||
pub type CharacterVariantParams<'a> = TableRef<'a, CharacterVariantParamsMarker>;
|
||
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> CharacterVariantParams<'a> {
|
||
/// Format number is set to 0.
|
||
pub fn format(&self) -> u16 {
|
||
let range = self.shape.format_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The 'name' table name ID that specifies a string (or strings,
|
||
/// for multiple languages) for a user-interface label for this
|
||
/// feature. (May be NULL.)
|
||
pub fn feat_ui_label_name_id(&self) -> NameId {
|
||
let range = self.shape.feat_ui_label_name_id_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The 'name' table name ID that specifies a string (or strings,
|
||
/// for multiple languages) that an application can use for tooltip
|
||
/// text for this feature. (May be NULL.)
|
||
pub fn feat_ui_tooltip_text_name_id(&self) -> NameId {
|
||
let range = self.shape.feat_ui_tooltip_text_name_id_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The 'name' table name ID that specifies sample text that
|
||
/// illustrates the effect of this feature. (May be NULL.)
|
||
pub fn sample_text_name_id(&self) -> NameId {
|
||
let range = self.shape.sample_text_name_id_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// Number of named parameters. (May be zero.)
|
||
pub fn num_named_parameters(&self) -> u16 {
|
||
let range = self.shape.num_named_parameters_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The first 'name' table name ID used to specify strings for
|
||
/// user-interface labels for the feature parameters. (Must be zero
|
||
/// if numParameters is zero.)
|
||
pub fn first_param_ui_label_name_id(&self) -> NameId {
|
||
let range = self.shape.first_param_ui_label_name_id_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The count of characters for which this feature provides glyph
|
||
/// variants. (May be zero.)
|
||
pub fn char_count(&self) -> u16 {
|
||
let range = self.shape.char_count_byte_range();
|
||
self.data.read_at(range.start).unwrap()
|
||
}
|
||
|
||
/// The Unicode Scalar Value of the characters for which this
|
||
/// feature provides glyph variants.
|
||
pub fn character(&self) -> &'a [BigEndian<Uint24>] {
|
||
let range = self.shape.character_byte_range();
|
||
self.data.read_array(range).unwrap()
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
impl<'a> SomeTable<'a> for CharacterVariantParams<'a> {
|
||
fn type_name(&self) -> &str {
|
||
"CharacterVariantParams"
|
||
}
|
||
fn get_field(&self, idx: usize) -> Option<Field<'a>> {
|
||
match idx {
|
||
0usize => Some(Field::new("format", self.format())),
|
||
1usize => Some(Field::new(
|
||
"feat_ui_label_name_id",
|
||
self.feat_ui_label_name_id(),
|
||
)),
|
||
2usize => Some(Field::new(
|
||
"feat_ui_tooltip_text_name_id",
|
||
self.feat_ui_tooltip_text_name_id(),
|
||
)),
|
||
3usize => Some(Field::new(
|
||
"sample_text_name_id",
|
||
self.sample_text_name_id(),
|
||
)),
|
||
4usize => Some(Field::new(
|
||
"num_named_parameters",
|
||
self.num_named_parameters(),
|
||
)),
|
||
5usize => Some(Field::new(
|
||
"first_param_ui_label_name_id",
|
||
self.first_param_ui_label_name_id(),
|
||
)),
|
||
6usize => Some(Field::new("char_count", self.char_count())),
|
||
7usize => Some(Field::new("character", self.character())),
|
||
_ => None,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[cfg(feature = "experimental_traverse")]
|
||
#[allow(clippy::needless_lifetimes)]
|
||
impl<'a> std::fmt::Debug for CharacterVariantParams<'a> {
|
||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||
(self as &dyn SomeTable<'a>).fmt(f)
|
||
}
|
||
}
|