Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion font-codegen/src/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,13 @@ impl Field {
self.attrs.conditional.is_some()
}

fn skip_compile(&self) -> bool {
if let Some(compile) = self.attrs.compile.as_ref() {
return matches!(&compile.attr, CustomCompile::Skip);
}
false
}

/// Sanity check we are in a sane state for the end of phase
fn sanity_check(&self, phase: Phase) -> syn::Result<()> {
check_resolution(phase, &self.typ)?;
Expand Down Expand Up @@ -1398,7 +1405,7 @@ impl Field {
#[allow(clippy::wrong_self_convention)]
fn from_obj_requires_offset_data(&self, in_record: bool) -> bool {
match &self.typ {
_ if self.attrs.to_owned.is_some() => false,
_ if self.attrs.to_owned.is_some() || self.skip_compile() => false,
FieldType::Offset {
target: OffsetTarget::Array(_),
..
Expand Down
34 changes: 24 additions & 10 deletions read-fonts/generated/generated_postscript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ impl<'a> FontRead<'a> for Index1<'a> {
let mut cursor = data.cursor();
let count: u16 = cursor.read()?;
let off_size: u8 = cursor.read()?;
let offsets_byte_len = (transforms::add_multiply(count, 1_usize, off_size))
.checked_mul(u8::RAW_BYTE_LEN)
let offsets_byte_len = (transforms::add(count, 1_usize))
.checked_mul(<VarOffset as ComputeSize>::compute_size(&off_size)?)
.ok_or(ReadError::OutOfBounds)?;
cursor.advance_by(offsets_byte_len);
let data_byte_len = cursor.remaining_bytes() / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN;
Expand Down Expand Up @@ -77,9 +77,9 @@ impl<'a> Index1<'a> {
}

/// Bytes containing `count + 1` offsets each of `off_size`.
pub fn offsets(&self) -> &'a [u8] {
pub fn offsets(&self) -> ComputedArray<'a, VarOffset> {
let range = self.shape.offsets_byte_range();
self.data.read_array(range).unwrap()
self.data.read_with_args(range, &self.off_size()).unwrap()
}

/// Array containing the object data.
Expand All @@ -98,7 +98,14 @@ impl<'a> SomeTable<'a> for Index1<'a> {
match idx {
0usize => Some(Field::new("count", self.count())),
1usize => Some(Field::new("off_size", self.off_size())),
2usize => Some(Field::new("offsets", self.offsets())),
2usize => Some(Field::new(
"offsets",
traversal::FieldType::computed_array(
"VarOffset",
self.offsets(),
self.offset_data(),
),
)),
3usize => Some(Field::new("data", self.data())),
_ => None,
}
Expand Down Expand Up @@ -154,8 +161,8 @@ impl<'a> FontRead<'a> for Index2<'a> {
let mut cursor = data.cursor();
let count: u32 = cursor.read()?;
let off_size: u8 = cursor.read()?;
let offsets_byte_len = (transforms::add_multiply(count, 1_usize, off_size))
.checked_mul(u8::RAW_BYTE_LEN)
let offsets_byte_len = (transforms::add(count, 1_usize))
.checked_mul(<VarOffset as ComputeSize>::compute_size(&off_size)?)
.ok_or(ReadError::OutOfBounds)?;
cursor.advance_by(offsets_byte_len);
let data_byte_len = cursor.remaining_bytes() / u8::RAW_BYTE_LEN * u8::RAW_BYTE_LEN;
Expand Down Expand Up @@ -185,9 +192,9 @@ impl<'a> Index2<'a> {
}

/// Bytes containing `count + 1` offsets each of `off_size`.
pub fn offsets(&self) -> &'a [u8] {
pub fn offsets(&self) -> ComputedArray<'a, VarOffset> {
let range = self.shape.offsets_byte_range();
self.data.read_array(range).unwrap()
self.data.read_with_args(range, &self.off_size()).unwrap()
}

/// Array containing the object data.
Expand All @@ -206,7 +213,14 @@ impl<'a> SomeTable<'a> for Index2<'a> {
match idx {
0usize => Some(Field::new("count", self.count())),
1usize => Some(Field::new("off_size", self.off_size())),
2usize => Some(Field::new("offsets", self.offsets())),
2usize => Some(Field::new(
"offsets",
traversal::FieldType::computed_array(
"VarOffset",
self.offsets(),
self.offset_data(),
),
)),
3usize => Some(Field::new("data", self.data())),
_ => None,
}
Expand Down
2 changes: 1 addition & 1 deletion read-fonts/src/tables/postscript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ include!("../../generated/generated_postscript.rs");

pub use blend::BlendState;
pub use charset::{Charset, CharsetIter};
pub use index::Index;
pub use index::{Index, VarOffset};
pub use stack::{Number, Stack};
pub use string::{Latin1String, StringId, STANDARD_STRINGS};

Expand Down
112 changes: 67 additions & 45 deletions read-fonts/src/tables/postscript/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,65 @@ impl Default for Index<'_> {
}
}

/// An offset that can be encoded using 1-4 bytes.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct VarOffset(u32);

impl ReadArgs for VarOffset {
type Args = u8;
}

impl ComputeSize for VarOffset {
fn compute_size(args: &Self::Args) -> Result<usize, ReadError> {
Ok(*args as usize)
}
}

impl FontReadWithArgs<'_> for VarOffset {
fn read_with_args(data: FontData<'_>, args: &Self::Args) -> Result<Self, ReadError> {
// There are actually count + 1 entries in the offset array.
//
// "Offsets in the offset array are relative to the byte that precedes
// the object data. Therefore the first element of the offset array is
// always 1. (This ensures that every object has a corresponding offset
// which is always nonzero and permits the efficient implementation of
// dynamic object loading.)"
//
// See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-7-index-format>
let raw = match *args {
1 => data.read_at::<u8>(0).map(|x| x as _),
2 => data.read_at::<u16>(0).map(|x| x as _),
3 => data.read_at::<Uint24>(0).map(|x| x.to_u32()),
4 => data.read_at::<u32>(0),
_ => Err(ReadError::MalformedData("invalid cff index offset len")),
}?;

raw.checked_sub(1)
.ok_or(ReadError::OutOfBounds)
.map(VarOffset)
}
}

impl VarOffset {
pub fn to_u32(self) -> u32 {
self.0
}
}

#[cfg(feature = "experimental_traverse")]
impl<'a> SomeRecord<'a> for VarOffset {
fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
RecordResolver {
name: "VarOffset",
get_field: Box::new(move |idx, _data| match idx {
0usize => Some(Field::new("offset", self.0)),
_ => None,
}),
data,
}
}
}

impl<'a> Index1<'a> {
/// Returns the total size in bytes of the index table.
pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
Expand All @@ -130,12 +189,10 @@ impl<'a> Index1<'a> {

/// Returns the offset of the object at the given index.
pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
read_offset(
index,
self.count() as usize,
self.off_size(),
self.offsets(),
)
self.offsets()
.get(index)
.map(|idx| idx.0 as usize)
.map_err(|_| Error::ZeroOffsetInIndex)
}

/// Returns the data for the object at the given index.
Expand Down Expand Up @@ -166,12 +223,10 @@ impl<'a> Index2<'a> {

/// Returns the offset of the object at the given index.
pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
read_offset(
index,
self.count() as usize,
self.off_size(),
self.offsets(),
)
self.offsets()
.get(index)
.map(|idx| idx.0 as usize)
.map_err(|_| Error::ZeroOffsetInIndex)
}

/// Returns the data for the object at the given index.
Expand All @@ -182,39 +237,6 @@ impl<'a> Index2<'a> {
}
}

/// Reads an offset which is encoded as a variable sized integer.
fn read_offset(
index: usize,
count: usize,
offset_size: u8,
offset_data: &[u8],
) -> Result<usize, Error> {
// There are actually count + 1 entries in the offset array.
//
// "Offsets in the offset array are relative to the byte that precedes
// the object data. Therefore the first element of the offset array is
// always 1. (This ensures that every object has a corresponding offset
// which is always nonzero and permits the efficient implementation of
// dynamic object loading.)"
//
// See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-7-index-format>
if index > count {
Err(ReadError::OutOfBounds)?;
}
let data_offset = index * offset_size as usize;
let offset_data = FontData::new(offset_data);
match offset_size {
1 => offset_data.read_at::<u8>(data_offset)? as usize,
2 => offset_data.read_at::<u16>(data_offset)? as usize,
3 => offset_data.read_at::<Uint24>(data_offset)?.to_u32() as usize,
4 => offset_data.read_at::<u32>(data_offset)? as usize,
_ => return Err(Error::InvalidIndexOffsetSize(offset_size)),
}
// As above, subtract one to get the actual offset.
.checked_sub(1)
.ok_or(Error::ZeroOffsetInIndex)
}

#[cfg(test)]
mod tests {
use font_test_data::bebuffer::BeBuffer;
Expand Down
22 changes: 18 additions & 4 deletions resources/codegen_inputs/postscript.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,44 @@
#![parse_module(read_fonts::tables::postscript)]

/// An array of variable-sized objects in a `CFF` table.
#[skip_font_write]
table Index1 {
/// Number of objects stored in INDEX.
#[compile(skip)]
count: u16,
/// Object array element size.
#[compile(skip)]
off_size: u8,
/// Bytes containing `count + 1` offsets each of `off_size`.
#[count(add_multiply($count, 1, $off_size))]
offsets: [u8],
#[count(add($count, 1))]
#[read_with($off_size)]
#[compile(skip)]
offsets: ComputedArray<VarOffset>,
/// Array containing the object data.
#[count(..)]
#[compile_type(Vec<Vec<u8>>)]
#[to_owned(convert_objects_f1(obj))]
data: [u8],
}

/// An array of variable-sized objects in a `CFF2` table.
#[skip_font_write]
table Index2 {
/// Number of objects stored in INDEX.
#[compile(skip)]
count: u32,
/// Object array element size.
#[compile(skip)]
off_size: u8,
/// Bytes containing `count + 1` offsets each of `off_size`.
#[count(add_multiply($count, 1, $off_size))]
offsets: [u8],
#[count(add($count, 1))]
#[read_with($off_size)]
#[compile(skip)]
offsets: ComputedArray<VarOffset>,
/// Array containing the object data.
#[count(..)]
#[compile_type(Vec<Vec<u8>>)]
#[to_owned(convert_objects_f2(obj))]
data: [u8],
}

Expand Down
5 changes: 2 additions & 3 deletions write-fonts/generated/generated_ift.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,6 @@ impl Validate for GlyphMap {

impl<'a> FromObjRef<read_fonts::tables::ift::GlyphMap<'a>> for GlyphMap {
fn from_obj_ref(obj: &read_fonts::tables::ift::GlyphMap<'a>, _: FontData) -> Self {
let offset_data = obj.offset_data();
GlyphMap {
first_mapped_glyph: obj.first_mapped_glyph(),
}
Expand Down Expand Up @@ -428,7 +427,7 @@ impl Validate for FeatureRecord {
}

impl FromObjRef<read_fonts::tables::ift::FeatureRecord> for FeatureRecord {
fn from_obj_ref(obj: &read_fonts::tables::ift::FeatureRecord, offset_data: FontData) -> Self {
fn from_obj_ref(obj: &read_fonts::tables::ift::FeatureRecord, _: FontData) -> Self {
FeatureRecord {
feature_tag: obj.feature_tag(),
}
Expand Down Expand Up @@ -459,7 +458,7 @@ impl Validate for EntryMapRecord {
}

impl FromObjRef<read_fonts::tables::ift::EntryMapRecord> for EntryMapRecord {
fn from_obj_ref(obj: &read_fonts::tables::ift::EntryMapRecord, offset_data: FontData) -> Self {
fn from_obj_ref(obj: &read_fonts::tables::ift::EntryMapRecord, _: FontData) -> Self {
EntryMapRecord {}
}
}
Expand Down
Loading
Loading