diff --git a/otf/src/lib.rs b/otf/src/lib.rs index 0bdad63..70f3145 100644 --- a/otf/src/lib.rs +++ b/otf/src/lib.rs @@ -3,18 +3,18 @@ mod utils; use std::collections::HashMap; use std::convert::TryFrom; -use std::io::{self, Cursor}; +use std::io::{self, Cursor, Read}; use std::rc::Rc; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; -use tables::{FontTable, Glyph}; +use tables::{FontTable, Glyph, NamedTable}; use utils::limit_read::LimitRead; #[derive(Debug, PartialEq, Clone)] pub struct OpenTypeFont { sfnt_version: SfntVersion, os2_table: tables::os2::Os2Table, - cmap_subtables: Vec, + cmap_table: tables::cmap::CmapTable, glyf_table: tables::glyf::GlyfTable, head_table: tables::head::HeadTable, hhea_table: tables::hhea::HheaTable, @@ -37,53 +37,16 @@ impl OpenTypeFont { let mut cursor = Cursor::new(data.as_ref()); let offset_table = OffsetTable::unpack(&mut cursor, ())?; - let cmap_record = offset_table - .get_table_record("cmap") - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "cmap table missing"))?; - let cmap_table: tables::cmap::CmapTable = - offset_table.unpack_required_table("cmap", (), &mut cursor)?; - - let mut subtables: Vec<(u32, CmapSubtable)> = - Vec::with_capacity(cmap_table.encoding_records.len()); - for record in &cmap_table.encoding_records { - let existing_subtable = subtables - .iter() - .find(|(offset, _)| record.offset == *offset) - .map(|(_, subtable)| Rc::clone(&subtable.subtable)); - if let Some(subtable) = existing_subtable { - subtables.push(( - record.offset, - CmapSubtable { - platform_id: record.platform_id, - encoding_id: record.encoding_id, - subtable, - }, - )); - continue; - } - - cursor.set_position((cmap_record.offset + record.offset) as u64); - let subtable = tables::cmap::Subtable::unpack(&mut cursor, ())?; - subtables.push(( - record.offset, - CmapSubtable { - platform_id: record.platform_id, - encoding_id: record.encoding_id, - subtable: Rc::new(subtable), - }, - )); - } - let cmap_subtables = subtables.into_iter().map(|(_, st)| st).collect(); - let head_table = offset_table.unpack_required_table("head", (), &mut cursor)?; let hhea_table = offset_table.unpack_required_table("hhea", (), &mut cursor)?; let maxp_table = offset_table.unpack_required_table("maxp", (), &mut cursor)?; let loca_table = offset_table.unpack_required_table("loca", (&head_table, &maxp_table), &mut cursor)?; + Ok(OpenTypeFont { sfnt_version: offset_table.sfnt_version, os2_table: offset_table.unpack_required_table("OS/2", (), &mut cursor)?, - cmap_subtables, + cmap_table: offset_table.unpack_required_table("cmap", (), &mut cursor)?, glyf_table: offset_table.unpack_required_table("glyf", &loca_table, &mut cursor)?, hmtx_table: offset_table.unpack_required_table( "hmtx", @@ -101,14 +64,15 @@ impl OpenTypeFont { // TODO: return u32? pub fn glyph_id(&self, codepoint: u32) -> Option { - self.cmap_subtables + self.cmap_table + .encoding_records .first() - .and_then(|subtable| subtable.subtable.glyph_id(codepoint)) + .and_then(|record| record.subtable.glyph_id(codepoint)) } pub fn subset(&self, text: &str) -> Self { - let subtable = match self.cmap_subtables.first() { - Some(s) => s.subtable.clone(), + let subtable = match self.cmap_table.encoding_records.first() { + Some(r) => r.subtable.clone(), // TODO: error instead? None => return self.clone(), }; @@ -134,29 +98,7 @@ impl OpenTypeFont { let glyphs = self.glyf_table.expand_composite_glyphs(&glyphs); let os2_table = self.os2_table.subset(&glyphs, ()).into_owned(); - let mut subsetted_subtables: Vec<(Rc, Rc)> = - Vec::new(); - let cmap_subtables = self - .cmap_subtables - .iter() - .map(|entry| { - let new_subtable = subsetted_subtables - .iter() - .find(|(prev, _)| Rc::ptr_eq(prev, &entry.subtable)) - .map(|(_, new_subtable)| new_subtable.clone()) - .unwrap_or_else(|| { - let new_subtable = Rc::new(entry.subtable.subset(&glyphs, ()).into_owned()); - subsetted_subtables.push((entry.subtable.clone(), new_subtable.clone())); - new_subtable - }); - - CmapSubtable { - platform_id: entry.platform_id, - encoding_id: entry.encoding_id, - subtable: new_subtable, - } - }) - .collect(); + let cmap_table = self.cmap_table.subset(&glyphs, ()).into_owned(); let glyf_table = self.glyf_table.subset(&glyphs, ()).into_owned(); let loca_table = self.loca_table.subset(&glyphs, &glyf_table).into_owned(); let head_table = self @@ -175,7 +117,7 @@ impl OpenTypeFont { OpenTypeFont { sfnt_version: self.sfnt_version, os2_table, - cmap_subtables, + cmap_table, glyf_table, head_table, hhea_table, @@ -188,190 +130,97 @@ impl OpenTypeFont { } /// Note: currently skips all other tables of the font that are not known to the library. - pub fn to_writer(&self, mut wr: impl io::Write) -> Result<(), io::Error> { - let mut tables = Vec::new(); - // reserve space for offset table - let mut offset: usize = 12 + 10 * 16; - - // OS/2 table - let mut os2_data = Vec::new(); - self.os2_table.pack(&mut os2_data)?; - os2_data.resize(os2_data.len() + os2_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "OS/2".to_string(), - check_sum: check_sum(Cursor::new(&os2_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(os2_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += os2_data.len(); + pub fn to_writer(&self, wr: impl io::Write) -> Result<(), io::Error> { + let mut writer = FontWriter::new(10); + writer.pack(&self.os2_table)?; + writer.pack(&self.cmap_table)?; + writer.pack(&self.glyf_table)?; + let check_sum_adjustment_offset = writer.offset() + 8; + writer.pack(&self.head_table)?; + writer.pack(&self.hhea_table)?; + writer.pack(&self.hmtx_table)?; + writer.pack(&self.loca_table)?; + writer.pack(&self.maxp_table)?; + writer.pack(&self.name_table)?; + writer.pack(&self.post_table)?; + writer.finish(self.sfnt_version, check_sum_adjustment_offset, wr)?; - // cmap subtables - let mut cmap_subtables_data = Vec::new(); - let mut cmap_table = tables::cmap::CmapTable { - version: 0, - num_tables: u16::try_from(self.cmap_subtables.len()) - .ok() - .unwrap_or(u16::MAX), - encoding_records: Vec::with_capacity(self.cmap_subtables.len()), - }; - // reserve cmap table data - let mut subtable_offset = 4 + self.cmap_subtables.len() * 8; - let mut written_subtables = Vec::new(); - for subtable in &self.cmap_subtables { - let prev_offset = written_subtables - .iter() - .find(|(_, other)| Rc::ptr_eq(other, &subtable.subtable)) - .map(|(offset, _)| *offset); - if let Some(prev_offset) = prev_offset { - cmap_table - .encoding_records - .push(tables::cmap::EncodingRecord { - platform_id: subtable.platform_id, - encoding_id: subtable.encoding_id, - offset: prev_offset, - }); - continue; - } - - let len_before = cmap_subtables_data.len(); - subtable.subtable.pack(&mut cmap_subtables_data)?; // align to 4 bytes - let record_offset = u32::try_from(subtable_offset).ok().unwrap_or(u32::MAX); - cmap_table - .encoding_records - .push(tables::cmap::EncodingRecord { - platform_id: subtable.platform_id, - encoding_id: subtable.encoding_id, - offset: record_offset, - }); - written_subtables.push((record_offset, subtable.subtable.clone())); - subtable_offset += cmap_subtables_data.len() - len_before; + Ok(()) + } +} + +struct FontWriter { + tables: Vec, + buffer: Vec, +} + +impl FontWriter { + fn new(len: usize) -> Self { + FontWriter { + tables: Vec::with_capacity(len), + buffer: Vec::new(), // TODO: guess a starting size? } - cmap_subtables_data.resize(cmap_subtables_data.len() + cmap_subtables_data.len() % 4, 0); - - // cmap table - let mut cmap_data = Vec::new(); - cmap_table.pack(&mut cmap_data)?; - cmap_data.resize(cmap_data.len() + cmap_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "cmap".to_string(), - check_sum: check_sum(Cursor::new(&cmap_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(cmap_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += cmap_data.len() + cmap_subtables_data.len(); - - // glyf table - let mut glyf_data = Vec::new(); - self.glyf_table.pack(&mut glyf_data)?; - glyf_data.resize(glyf_data.len() + glyf_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "glyf".to_string(), - check_sum: check_sum(Cursor::new(&glyf_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(glyf_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += glyf_data.len(); - - // head table - let mut head_data = Vec::new(); - self.head_table.pack(&mut head_data)?; - head_data.resize(head_data.len() + head_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "head".to_string(), - check_sum: check_sum(Cursor::new(&head_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(head_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += head_data.len(); - - // hhea table - let mut hhea_data = Vec::new(); - self.hhea_table.pack(&mut hhea_data)?; - hhea_data.resize(hhea_data.len() + hhea_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "hhea".to_string(), - check_sum: check_sum(Cursor::new(&hhea_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(hhea_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += hhea_data.len(); - - // hmtx table - let mut hmtx_data = Vec::new(); - self.hmtx_table.pack(&mut hmtx_data)?; - hmtx_data.resize(hmtx_data.len() + hmtx_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "hmtx".to_string(), - check_sum: check_sum(Cursor::new(&hmtx_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(hmtx_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += hmtx_data.len(); - - // loca table - let mut loca_data = Vec::new(); - self.loca_table.pack(&mut loca_data)?; - loca_data.resize(loca_data.len() + loca_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "loca".to_string(), - check_sum: check_sum(Cursor::new(&loca_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(loca_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += loca_data.len(); - - // maxp table - let mut maxp_data = Vec::new(); - self.maxp_table.pack(&mut maxp_data)?; - maxp_data.resize(maxp_data.len() + maxp_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "maxp".to_string(), - check_sum: check_sum(Cursor::new(&maxp_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(maxp_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += maxp_data.len(); - - // name table - let mut name_data = Vec::new(); - self.name_table.pack(&mut name_data)?; - name_data.resize(name_data.len() + name_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "name".to_string(), - check_sum: check_sum(Cursor::new(&name_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(name_data.len()).ok().unwrap_or(u32::MAX), - }); - offset += name_data.len(); - - // post table - let mut post_data = Vec::new(); - self.post_table.pack(&mut post_data)?; - post_data.resize(post_data.len() + post_data.len() % 4, 0); // align to 4 bytes - tables.push(TableRecord { - tag: "post".to_string(), - check_sum: check_sum(Cursor::new(&post_data)), - offset: u32::try_from(offset).ok().unwrap_or(u32::MAX), - length: u32::try_from(post_data.len()).ok().unwrap_or(u32::MAX), + } + + fn offset_table_len(&self) -> usize { + 12 + self.tables.capacity() * 16 + } + + fn offset(&self) -> usize { + self.offset_table_len() + self.buffer.len() + } + + fn pack<'a, T, UD, SD>(&mut self, table: &T) -> Result<(), io::Error> + where + T: FontTable<'a, UnpackDep = UD, SubsetDep = SD> + NamedTable, + { + if self.tables.len() == self.tables.capacity() { + return Err(io::Error::new( + io::ErrorKind::Other, + format!( + "Cannot write another table, already wrote {} tables", + self.tables.len() + ), + )); + } + + let start = self.buffer.len(); + table.pack(&mut self.buffer)?; + let len = self.buffer.len() - start; + self.buffer + .resize(self.buffer.len() + self.buffer.len() % 4, 0); // align to 4 bytes + self.tables.push(TableRecord { + tag: T::name().to_string(), + check_sum: check_sum(&self.buffer[start..]), + offset: u32::try_from(self.offset_table_len() + start) + .ok() + .unwrap_or(u32::MAX), + length: u32::try_from(len).ok().unwrap_or(u32::MAX), }); - // offset += post_data.len(); - // TODO: update head.checkSumAdjustment, see - // https://docs.microsoft.com/en-us/typography/opentype/spec/otff#calculating-checksums + Ok(()) + } - let num_tables = u16::try_from(tables.len()).ok().unwrap_or(u16::MAX); + fn finish( + mut self, + sfnt_version: SfntVersion, + check_sum_adjustment_offset: usize, + mut wr: impl io::Write, + ) -> Result<(), io::Error> { + let num_tables = u16::try_from(self.tables.len()).ok().unwrap_or(u16::MAX); let x = 2u16.pow((num_tables as f32).log2() as u32); let search_range = x * 16; let entry_selector = (x as f32).log2() as u16; let range_shift = num_tables * 16 - search_range; + let check_sum_adjustment_offset = check_sum_adjustment_offset - self.offset_table_len(); let offset_table = OffsetTable { - sfnt_version: self.sfnt_version, + sfnt_version, num_tables, search_range, entry_selector, range_shift, - tables, + tables: self.tables, }; let mut offset_data = Vec::new(); offset_table.pack(&mut offset_data)?; @@ -384,22 +233,11 @@ impl OpenTypeFont { let check_sum_adjustment = 0xB1B0AFBAu32.saturating_sub(check_sum_adjustment); // inject check_sum_adjustment into head table data - let mut cursor = Cursor::new(&mut head_data); - cursor.set_position(8); - cursor.write_u32::(check_sum_adjustment)?; + (&mut self.buffer[check_sum_adjustment_offset..]) + .write_u32::(check_sum_adjustment)?; wr.write_all(&offset_data)?; - wr.write_all(&os2_data)?; - wr.write_all(&cmap_data)?; - wr.write_all(&cmap_subtables_data)?; - wr.write_all(&glyf_data)?; - wr.write_all(&head_data)?; - wr.write_all(&hhea_data)?; - wr.write_all(&hmtx_data)?; - wr.write_all(&loca_data)?; - wr.write_all(&maxp_data)?; - wr.write_all(&name_data)?; - wr.write_all(&post_data)?; + wr.write_all(&self.buffer)?; Ok(()) } @@ -460,7 +298,7 @@ impl OffsetTable { }; cursor.set_position(record.offset as u64); - let mut limit_read = LimitRead::new(cursor, record.length as usize); + let mut limit_read = Cursor::new(LimitRead::from_cursor(cursor, record.length as usize)); Ok(Some(T::unpack(&mut limit_read, dep)?)) } @@ -483,7 +321,10 @@ impl<'a> FontTable<'a> for OffsetTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let sfnt_version = SfntVersion::unpack(&mut rd, ())?; let num_tables = rd.read_u16::()?; let search_range = rd.read_u16::()?; @@ -528,7 +369,10 @@ impl<'a> FontTable<'a> for SfntVersion { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { match rd.read_u32::()? { 0x00010000 => Ok(SfntVersion::TrueType), 0x4F54544F => Ok(SfntVersion::CFF), @@ -560,7 +404,10 @@ impl<'a> FontTable<'a> for TableRecord { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let mut tag = [0; 4]; rd.read_exact(&mut tag)?; Ok(TableRecord { @@ -589,8 +436,8 @@ mod test { #[test] fn test_offset_table_encode_decode() { - let data = include_bytes!("../tests/fonts/Iosevka/iosevka-regular.ttf"); - let table = OffsetTable::unpack(&mut Cursor::new(data.to_vec()), ()).unwrap(); + let data = include_bytes!("../tests/fonts/Iosevka/iosevka-regular.ttf").to_vec(); + let table = OffsetTable::unpack(&mut Cursor::new(&data[..]), ()).unwrap(); assert_eq!(table.sfnt_version, SfntVersion::TrueType); assert_eq!(table.num_tables, 17); assert_eq!(table.search_range, 256); @@ -643,7 +490,7 @@ mod test { let mut buffer = Vec::new(); table.pack(&mut buffer).unwrap(); assert_eq!( - OffsetTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + OffsetTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), table ); } @@ -686,7 +533,7 @@ mod test { let OpenTypeFont { sfnt_version, os2_table, - cmap_subtables, + cmap_table, glyf_table, head_table, hhea_table, @@ -698,7 +545,7 @@ mod test { } = rewritten_subset; assert_eq!(sfnt_version, subset.sfnt_version); assert_eq!(os2_table, subset.os2_table); - assert_eq!(cmap_subtables, subset.cmap_subtables); + assert_eq!(cmap_table, subset.cmap_table); assert_eq!(glyf_table, subset.glyf_table); assert_eq!(head_table, subset.head_table); assert_eq!(hhea_table, subset.hhea_table); diff --git a/otf/src/tables/cmap.rs b/otf/src/tables/cmap.rs index 213a356..946e869 100644 --- a/otf/src/tables/cmap.rs +++ b/otf/src/tables/cmap.rs @@ -2,9 +2,12 @@ mod format12; mod format4; use std::borrow::Cow; -use std::{io, mem}; +use std::convert::TryFrom; +use std::io::{self, Cursor}; +use std::mem; +use std::rc::Rc; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use crate::utils::limit_read::LimitRead; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use format12::Format12; @@ -31,21 +34,31 @@ use format4::Format4; #[derive(Debug, PartialEq, Clone)] pub struct CmapTable { pub(crate) version: u16, - pub(crate) num_tables: u16, pub(crate) encoding_records: Vec, } +impl NamedTable for CmapTable { + fn name() -> &'static str { + "cmap" + } +} + impl<'a> FontTable<'a> for CmapTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { + let offset = rd.position(); + let version = rd.read_u16::()?; let num_tables = rd.read_u16::()?; - let mut encoding_records = Vec::with_capacity(num_tables.min(4) as usize); + let mut raw_records = Vec::with_capacity(num_tables.min(4) as usize); for _ in 0..num_tables { - let record = EncodingRecord::unpack(&mut rd, ())?; + let record = RawEncodingRecord::unpack(&mut rd, ())?; // skip unsupported formats if !matches!( (record.platform_id, record.encoding_id), @@ -53,47 +66,153 @@ impl<'a> FontTable<'a> for CmapTable { ) { continue; } - encoding_records.push(record); + raw_records.push(record); } - if encoding_records.is_empty() { + if raw_records.is_empty() { return Err(io::Error::new( io::ErrorKind::Other, "Font does not contain any supported CMAP", )); } + let mut records: Vec<(u32, EncodingRecord)> = Vec::with_capacity(raw_records.len()); + for raw_record in &raw_records { + let existing_subtable = records + .iter() + .find(|(offset, _)| raw_record.offset == *offset) + .map(|(_, subtable)| Rc::clone(&subtable.subtable)); + if let Some(subtable) = existing_subtable { + records.push(( + raw_record.offset, + EncodingRecord { + platform_id: raw_record.platform_id, + encoding_id: raw_record.encoding_id, + subtable, + }, + )); + continue; + } + + rd.set_position(offset + (raw_record.offset) as u64); + let subtable = Subtable::unpack(&mut rd, ())?; + records.push(( + raw_record.offset, + EncodingRecord { + platform_id: raw_record.platform_id, + encoding_id: raw_record.encoding_id, + subtable: Rc::new(subtable), + }, + )); + } + let encoding_records = records.into_iter().map(|(_, st)| st).collect(); Ok(CmapTable { version, - num_tables, encoding_records, }) } fn pack(&self, mut wr: &mut W) -> Result<(), io::Error> { + // cmap subtables + let mut encoding_records_data = Vec::new(); + let mut raw_recods = Vec::with_capacity(self.encoding_records.len()); + + // reserve cmap table data + let mut subtable_offset = 4 + self.encoding_records.len() * 8; + let mut written_subtables = Vec::new(); + for subtable in &self.encoding_records { + let prev_offset = written_subtables + .iter() + .find(|(_, other)| Rc::ptr_eq(other, &subtable.subtable)) + .map(|(offset, _)| *offset); + if let Some(prev_offset) = prev_offset { + raw_recods.push(RawEncodingRecord { + platform_id: subtable.platform_id, + encoding_id: subtable.encoding_id, + offset: prev_offset, + }); + continue; + } + + let len_before = encoding_records_data.len(); + subtable.subtable.pack(&mut encoding_records_data)?; // align to 4 bytes + let record_offset = u32::try_from(subtable_offset).ok().unwrap_or(u32::MAX); + raw_recods.push(RawEncodingRecord { + platform_id: subtable.platform_id, + encoding_id: subtable.encoding_id, + offset: record_offset, + }); + written_subtables.push((record_offset, subtable.subtable.clone())); + subtable_offset += encoding_records_data.len() - len_before; + } + wr.write_u16::(self.version)?; - wr.write_u16::(self.num_tables)?; - for table in &self.encoding_records { - table.pack(&mut wr)?; + wr.write_u16::(self.encoding_records.len() as u16)?; + for record in raw_recods { + record.pack(&mut wr)?; } + wr.write_all(&encoding_records_data)?; + Ok(()) } + + fn subset(&'a self, glyphs: &[Glyph], _dep: Self::SubsetDep) -> Cow<'a, Self> + where + Self: Clone, + { + let mut subsetted_subtables: Vec<(Rc, Rc)> = Vec::new(); + let encoding_records = self + .encoding_records + .iter() + .map(|entry| { + let new_subtable = subsetted_subtables + .iter() + .find(|(prev, _)| Rc::ptr_eq(prev, &entry.subtable)) + .map(|(_, new_subtable)| new_subtable.clone()) + .unwrap_or_else(|| { + let new_subtable = Rc::new(entry.subtable.subset(&glyphs, ()).into_owned()); + subsetted_subtables.push((entry.subtable.clone(), new_subtable.clone())); + new_subtable + }); + + EncodingRecord { + platform_id: entry.platform_id, + encoding_id: entry.encoding_id, + subtable: new_subtable, + } + }) + .collect(); + + Cow::Owned(CmapTable { + version: self.version, + encoding_records, + }) + } +} + +pub struct RawEncodingRecord { + platform_id: u16, + encoding_id: u16, + /// Byte offset from beginning of table to the subtable for this encoding. + offset: u32, } #[derive(Debug, PartialEq, Clone)] pub struct EncodingRecord { pub(crate) platform_id: u16, pub(crate) encoding_id: u16, - /// Byte offset from beginning of table to the subtable for this encoding. - pub(crate) offset: u32, + pub(crate) subtable: Rc, } -impl<'a> FontTable<'a> for EncodingRecord { +impl<'a> FontTable<'a> for RawEncodingRecord { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { - Ok(EncodingRecord { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { + Ok(RawEncodingRecord { platform_id: rd.read_u16::()?, encoding_id: rd.read_u16::()?, offset: rd.read_u32::()?, @@ -127,7 +246,10 @@ impl<'a> FontTable<'a> for Subtable { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let format = rd.read_u16::()?; match format { @@ -135,7 +257,7 @@ impl<'a> FontTable<'a> for Subtable { let mut length = rd.read_u16::()?; // length excluding format and length length -= (mem::size_of::() * 2) as u16; - let mut rd = LimitRead::new(rd, length as usize); + let mut rd = Cursor::new(LimitRead::from_cursor(rd, length as usize)); Ok(Subtable::Format4(Format4::unpack(&mut rd, ())?)) } 12 => { @@ -143,7 +265,7 @@ impl<'a> FontTable<'a> for Subtable { let mut length = rd.read_u32::()?; // length excluding format, reserved and length length -= (mem::size_of::() * 2 + mem::size_of::()) as u32; - let mut rd = LimitRead::new(rd, length as usize); + let mut rd = Cursor::new(LimitRead::from_cursor(rd, length as usize)); Ok(Subtable::Format12(Format12::unpack(&mut rd, ())?)) } _ => Err(io::Error::new( @@ -225,38 +347,25 @@ mod test { .unwrap(); assert_eq!(cmap_table.version, 0); - assert_eq!(cmap_table.num_tables, 4); - assert_eq!( - cmap_table.encoding_records, - vec![ - EncodingRecord { - platform_id: 0, - encoding_id: 3, - offset: 36, - }, - EncodingRecord { - platform_id: 0, - encoding_id: 4, - offset: 1740, - }, - EncodingRecord { - platform_id: 3, - encoding_id: 1, - offset: 36, - }, - EncodingRecord { - platform_id: 3, - encoding_id: 10, - offset: 1740, - }, - ] - ); + assert_eq!(cmap_table.encoding_records.len(), 4); + + assert_eq!(cmap_table.encoding_records[0].platform_id, 0); + assert_eq!(cmap_table.encoding_records[0].encoding_id, 3); + + assert_eq!(cmap_table.encoding_records[1].platform_id, 0); + assert_eq!(cmap_table.encoding_records[1].encoding_id, 4); + + assert_eq!(cmap_table.encoding_records[2].platform_id, 3); + assert_eq!(cmap_table.encoding_records[2].encoding_id, 1); + + assert_eq!(cmap_table.encoding_records[3].platform_id, 3); + assert_eq!(cmap_table.encoding_records[3].encoding_id, 10); // re-pack and compare let mut buffer = Vec::new(); cmap_table.pack(&mut buffer).unwrap(); assert_eq!( - CmapTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + CmapTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), cmap_table ); } @@ -266,21 +375,17 @@ mod test { let data = include_bytes!("../../tests/fonts/Iosevka/iosevka-regular.ttf").to_vec(); let mut cursor = Cursor::new(&data[..]); let table = OffsetTable::unpack(&mut cursor, ()).unwrap(); - let cmap_record = table.get_table_record("cmap").unwrap(); let cmap_table: CmapTable = table .unpack_required_table("cmap", (), &mut cursor) .unwrap(); for record in &cmap_table.encoding_records { - cursor.set_position((cmap_record.offset + record.offset) as u64); - let subtable = Subtable::unpack(&mut cursor, ()).unwrap(); - // re-pack and compare let mut buffer = Vec::new(); - subtable.pack(&mut buffer).unwrap(); + record.subtable.pack(&mut buffer).unwrap(); assert_eq!( - Subtable::unpack(&mut Cursor::new(buffer), ()).unwrap(), - subtable + &Subtable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), + record.subtable.as_ref() ); } } diff --git a/otf/src/tables/cmap/format12.rs b/otf/src/tables/cmap/format12.rs index 9537b84..49d4934 100644 --- a/otf/src/tables/cmap/format12.rs +++ b/otf/src/tables/cmap/format12.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::io; +use std::io::{self, Cursor}; use crate::tables::{FontTable, Glyph}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; @@ -43,7 +43,10 @@ impl<'a> FontTable<'a> for Format12 { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let language = rd.read_u32::()?; let num_groups = rd.read_u32::()?; @@ -136,7 +139,10 @@ impl<'a> FontTable<'a> for SequentialMapGroup { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { Ok(SequentialMapGroup { start_char_code: rd.read_u32::()?, end_char_code: rd.read_u32::()?, @@ -154,7 +160,7 @@ impl<'a> FontTable<'a> for SequentialMapGroup { #[cfg(test)] mod test { - use std::io::Cursor; + use std::rc::Rc; use super::*; use crate::tables::cmap::{CmapTable, Subtable}; @@ -164,20 +170,17 @@ mod test { let data = include_bytes!("../../../tests/fonts/Iosevka/iosevka-regular.ttf").to_vec(); let mut cursor = Cursor::new(&data[..]); let table = OffsetTable::unpack(&mut cursor, ()).unwrap(); - let cmap_record = table.get_table_record("cmap").unwrap(); let cmap_table: CmapTable = table .unpack_required_table("cmap", (), &mut cursor) .unwrap(); let record = cmap_table .encoding_records - .iter() + .into_iter() .find(|r| r.platform_id == 0 && r.encoding_id == 4) .unwrap(); - cursor.set_position((cmap_record.offset + record.offset) as u64); - let subtable = Subtable::unpack(&mut cursor, ()).unwrap(); - match subtable { + match Rc::try_unwrap(record.subtable).unwrap() { Subtable::Format12(subtable) => subtable, _ => panic!("Expected format 12 subtable"), } @@ -196,7 +199,7 @@ mod test { let mut buffer = Vec::new(); format12.pack(&mut buffer).unwrap(); assert_eq!( - Format12::unpack(&mut Cursor::new(buffer), ()).unwrap(), + Format12::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), format12 ); } diff --git a/otf/src/tables/cmap/format4.rs b/otf/src/tables/cmap/format4.rs index e09ec46..b844203 100644 --- a/otf/src/tables/cmap/format4.rs +++ b/otf/src/tables/cmap/format4.rs @@ -1,6 +1,7 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::{io, iter, mem}; +use std::io::{self, Cursor, Read}; +use std::{iter, mem}; use crate::tables::{FontTable, Glyph}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; @@ -81,7 +82,10 @@ impl<'a> FontTable<'a> for Format4 { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let language = rd.read_u16::()?; let seg_count_x2 = rd.read_u16::()?; let seg_count = (seg_count_x2 / 2) as usize; @@ -239,7 +243,7 @@ impl<'a> FontTable<'a> for Format4 { #[cfg(test)] mod test { - use std::io::Cursor; + use std::rc::Rc; use super::*; use crate::tables::cmap::{CmapTable, Subtable}; @@ -249,20 +253,17 @@ mod test { let data = include_bytes!("../../../tests/fonts/Iosevka/iosevka-regular.ttf").to_vec(); let mut cursor = Cursor::new(&data[..]); let table = OffsetTable::unpack(&mut cursor, ()).unwrap(); - let cmap_record = table.get_table_record("cmap").unwrap(); let cmap_table: CmapTable = table .unpack_required_table("cmap", (), &mut cursor) .unwrap(); let record = cmap_table .encoding_records - .iter() + .into_iter() .find(|r| r.platform_id == 0 && r.encoding_id == 3) .unwrap(); - cursor.set_position((cmap_record.offset + record.offset) as u64); - let subtable = Subtable::unpack(&mut cursor, ()).unwrap(); - match subtable { + match Rc::try_unwrap(record.subtable).unwrap() { Subtable::Format4(subtable) => subtable, _ => panic!("Expected format 4 subtable"), } @@ -282,7 +283,7 @@ mod test { let mut buffer = Vec::new(); format4.pack(&mut buffer).unwrap(); assert_eq!( - Format4::unpack(&mut Cursor::new(buffer), ()).unwrap(), + Format4::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), format4 ); } @@ -339,7 +340,7 @@ mod test { let mut buffer = Vec::new(); format4.pack(&mut buffer).unwrap(); assert_eq!( - Format4::unpack(&mut Cursor::new(buffer), ()).unwrap(), + Format4::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), format4 ); } diff --git a/otf/src/tables/glyf.rs b/otf/src/tables/glyf.rs index 9e35d43..b0fd7c1 100644 --- a/otf/src/tables/glyf.rs +++ b/otf/src/tables/glyf.rs @@ -4,7 +4,7 @@ use std::io::{self, Cursor, Read}; use std::{iter, mem}; use super::loca::LocaTable; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use crate::utils::limit_read::LimitRead; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; @@ -80,11 +80,20 @@ impl GlyfTable { } } +impl NamedTable for GlyfTable { + fn name() -> &'static str { + "glyf" + } +} + impl<'a> FontTable<'a> for GlyfTable { type UnpackDep = &'a LocaTable; type SubsetDep = (); - fn unpack(mut rd: &mut R, loca: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + loca: Self::UnpackDep, + ) -> Result { let mut glyphs = Vec::with_capacity(loca.offsets.len().saturating_sub(1)); let mut pos = 0; @@ -244,7 +253,7 @@ mod test { let mut buffer = Vec::new(); glyf_table.pack(&mut buffer).unwrap(); assert_eq!( - GlyfTable::unpack(&mut Cursor::new(buffer), &loca_table).unwrap(), + GlyfTable::unpack(&mut Cursor::new(&buffer[..]), &loca_table).unwrap(), glyf_table ); } diff --git a/otf/src/tables/head.rs b/otf/src/tables/head.rs index c743b21..4883da3 100644 --- a/otf/src/tables/head.rs +++ b/otf/src/tables/head.rs @@ -1,9 +1,9 @@ use std::borrow::Cow; -use std::io; +use std::io::{self, Cursor}; use super::glyf::GlyfTable; use super::loca::{Format as LocaFormat, LocaTable}; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// See https://docs.microsoft.com/en-us/typography/opentype/spec/head @@ -75,11 +75,20 @@ pub struct HeadTable { pub(crate) glyph_data_format: i16, } +impl NamedTable for HeadTable { + fn name() -> &'static str { + "head" + } +} + impl<'a> FontTable<'a> for HeadTable { type UnpackDep = (); type SubsetDep = (&'a GlyfTable, &'a LocaTable); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let major_version = rd.read_u16::()?; let minor_version = rd.read_u16::()?; @@ -211,7 +220,7 @@ mod test { let mut buffer = Vec::new(); head_table.pack(&mut buffer).unwrap(); assert_eq!( - HeadTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + HeadTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), head_table ); } diff --git a/otf/src/tables/hhea.rs b/otf/src/tables/hhea.rs index c5781c4..b1b7cc7 100644 --- a/otf/src/tables/hhea.rs +++ b/otf/src/tables/hhea.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::io; +use std::io::{self, Cursor}; use super::head::HeadTable; use super::hmtx::HmtxTable; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table contains information for horizontal layout. @@ -45,11 +45,20 @@ pub struct HheaTable { pub number_of_h_metrics: u16, } +impl NamedTable for HheaTable { + fn name() -> &'static str { + "hhea" + } +} + impl<'a> FontTable<'a> for HheaTable { type UnpackDep = (); type SubsetDep = (&'a HeadTable, &'a HmtxTable); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let major_version = rd.read_u16::()?; let minor_version = rd.read_u16::()?; let ascent = rd.read_i16::()?; @@ -180,7 +189,7 @@ mod test { let mut buffer = Vec::new(); hhea_table.pack(&mut buffer).unwrap(); assert_eq!( - HheaTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + HheaTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), hhea_table ); } diff --git a/otf/src/tables/hmtx.rs b/otf/src/tables/hmtx.rs index 66f0236..1529e28 100644 --- a/otf/src/tables/hmtx.rs +++ b/otf/src/tables/hmtx.rs @@ -1,9 +1,10 @@ use std::borrow::Cow; -use std::{io, iter}; +use std::io::{self, Cursor}; +use std::iter; use super::hhea::HheaTable; use super::maxp::MaxpTable; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table contains glyph metrics used for horizontal text layout. @@ -27,12 +28,18 @@ pub struct LongHorMetric { pub(crate) lsb: i16, } +impl NamedTable for HmtxTable { + fn name() -> &'static str { + "hmtx" + } +} + impl<'a> FontTable<'a> for HmtxTable { type UnpackDep = (&'a HheaTable, &'a MaxpTable); type SubsetDep = (); - fn unpack( - mut rd: &mut R, + fn unpack>( + mut rd: &mut Cursor, (hhea, maxp): Self::UnpackDep, ) -> Result { let mut h_metrics = Vec::with_capacity(hhea.number_of_h_metrics as usize); @@ -102,7 +109,10 @@ impl<'a> FontTable<'a> for LongHorMetric { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { Ok(LongHorMetric { advance_width: rd.read_u16::()?, lsb: rd.read_i16::()?, @@ -151,7 +161,7 @@ mod test { let mut buffer = Vec::new(); hmtx_table.pack(&mut buffer).unwrap(); assert_eq!( - HmtxTable::unpack(&mut Cursor::new(buffer), (&hhea_table, &maxp_table)).unwrap(), + HmtxTable::unpack(&mut Cursor::new(&buffer[..]), (&hhea_table, &maxp_table)).unwrap(), hmtx_table ); } diff --git a/otf/src/tables/loca.rs b/otf/src/tables/loca.rs index a9b6010..a81ef2b 100644 --- a/otf/src/tables/loca.rs +++ b/otf/src/tables/loca.rs @@ -1,11 +1,12 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::{io, iter}; +use std::io::{self, Cursor}; +use std::iter; use super::glyf::GlyfTable; use super::head::HeadTable; use super::maxp::MaxpTable; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table stores the offsets to the locations of the glyphs in the font, relative to the @@ -28,11 +29,20 @@ pub(crate) enum Format { Long, } +impl NamedTable for LocaTable { + fn name() -> &'static str { + "loca" + } +} + impl<'a> FontTable<'a> for LocaTable { type UnpackDep = (&'a HeadTable, &'a MaxpTable); type SubsetDep = &'a GlyfTable; - fn unpack(rd: &mut R, (head, maxp): Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + (head, maxp): Self::UnpackDep, + ) -> Result { let n = maxp.num_glyphs() as usize + 1; let mut offsets = Vec::with_capacity(n); for _ in 0..n { @@ -121,7 +131,7 @@ mod test { let mut buffer = Vec::new(); loca_table.pack(&mut buffer).unwrap(); assert_eq!( - LocaTable::unpack(&mut Cursor::new(buffer), (&head_table, &maxp_table)).unwrap(), + LocaTable::unpack(&mut Cursor::new(&buffer[..]), (&head_table, &maxp_table)).unwrap(), loca_table ); } diff --git a/otf/src/tables/maxp.rs b/otf/src/tables/maxp.rs index b57992a..f767909 100644 --- a/otf/src/tables/maxp.rs +++ b/otf/src/tables/maxp.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::io; +use std::io::{self, Cursor}; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table establishes the memory requirements for this font. @@ -66,11 +66,20 @@ impl MaxpTable { } } +impl NamedTable for MaxpTable { + fn name() -> &'static str { + "maxp" + } +} + impl<'a> FontTable<'a> for MaxpTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let version = rd.read_u32::()?; match version { 0x00005000 => Ok(MaxpTable::CFF(CffMaxpTable::unpack(&mut rd, ())?)), @@ -120,7 +129,10 @@ impl<'a> FontTable<'a> for CffMaxpTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { Ok(CffMaxpTable { num_glyphs: rd.read_u16::()?, }) @@ -146,7 +158,10 @@ impl<'a> FontTable<'a> for TrueTypeMaxpTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { Ok(TrueTypeMaxpTable { num_glyphs: rd.read_u16::()?, max_points: rd.read_u16::()?, @@ -235,7 +250,7 @@ mod test { let mut buffer = Vec::new(); maxp_table.pack(&mut buffer).unwrap(); assert_eq!( - MaxpTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + MaxpTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), maxp_table ); } @@ -246,7 +261,7 @@ mod test { 0x00, 0x00, 0x50, 0x00, // version 0x22, 0xC2, // number glyphs ]; - let maxp_table = MaxpTable::unpack(&mut &data[..], ()).unwrap(); + let maxp_table = MaxpTable::unpack(&mut Cursor::new(&data[..]), ()).unwrap(); match &maxp_table { MaxpTable::CFF(table) => { @@ -259,7 +274,7 @@ mod test { let mut buffer = Vec::new(); maxp_table.pack(&mut buffer).unwrap(); assert_eq!( - MaxpTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + MaxpTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), maxp_table ); } diff --git a/otf/src/tables/mod.rs b/otf/src/tables/mod.rs index 35e0443..82b426f 100644 --- a/otf/src/tables/mod.rs +++ b/otf/src/tables/mod.rs @@ -10,13 +10,20 @@ pub mod os2; pub mod post; use std::borrow::Cow; -use std::io; +use std::io::{self, Cursor}; + +pub trait NamedTable { + fn name() -> &'static str; +} pub trait FontTable<'a>: Sized { type UnpackDep; type SubsetDep; - fn unpack(rd: &mut R, _dep: Self::UnpackDep) -> Result; + fn unpack>( + rd: &mut Cursor, + _dep: Self::UnpackDep, + ) -> Result; fn pack(&self, wr: &mut W) -> Result<(), io::Error>; fn subset(&'a self, _glyphs: &[Glyph], _dep: Self::SubsetDep) -> Cow<'a, Self> diff --git a/otf/src/tables/name.rs b/otf/src/tables/name.rs index c871d8e..4e935e8 100644 --- a/otf/src/tables/name.rs +++ b/otf/src/tables/name.rs @@ -1,6 +1,6 @@ -use std::io; +use std::io::{self, Cursor, Read}; -use super::FontTable; +use super::{FontTable, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table includes human-readable names for features and settings, copyright notices, @@ -14,11 +14,20 @@ pub enum NameTable { Format1(Format1NameTable), } +impl NamedTable for NameTable { + fn name() -> &'static str { + "name" + } +} + impl<'a> FontTable<'a> for NameTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let format = rd.read_u16::()?; match format { 0 => Ok(NameTable::Format0(Format0NameTable::unpack(&mut rd, ())?)), @@ -64,7 +73,10 @@ impl<'a> FontTable<'a> for Format0NameTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let count = rd.read_u16::()?; let offset = rd.read_u16::()?; let mut name_records = Vec::with_capacity(count as usize); @@ -113,7 +125,10 @@ impl<'a> FontTable<'a> for Format1NameTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(mut rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + mut rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let count = rd.read_u16::()?; let offset = rd.read_u16::()?; let mut name_records = Vec::with_capacity(count as usize); @@ -174,7 +189,10 @@ impl<'a> FontTable<'a> for NameRecord { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { Ok(NameRecord { platform_id: rd.read_u16::()?, encoding_id: rd.read_u16::()?, @@ -208,7 +226,10 @@ impl<'a> FontTable<'a> for LangTagRecord { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { Ok(LangTagRecord { length: rd.read_u16::()?, offset: rd.read_u16::()?, @@ -249,7 +270,7 @@ mod test { let mut buffer = Vec::new(); name_table.pack(&mut buffer).unwrap(); assert_eq!( - NameTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + NameTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), name_table ); } diff --git a/otf/src/tables/os2.rs b/otf/src/tables/os2.rs index 2769148..5d129de 100644 --- a/otf/src/tables/os2.rs +++ b/otf/src/tables/os2.rs @@ -1,8 +1,8 @@ use std::borrow::Cow; use std::convert::TryFrom; -use std::io; +use std::io::{self, Cursor, Read}; -use super::{FontTable, Glyph}; +use super::{FontTable, Glyph, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table consists of a set of metrics and other data that are required for a font. @@ -121,11 +121,20 @@ pub struct Os2Table { us_upper_optical_point_size: u16, } +impl NamedTable for Os2Table { + fn name() -> &'static str { + "OS/2" + } +} + impl<'a> FontTable<'a> for Os2Table { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let version = rd.read_u16::()?; let x_avg_char_width = rd.read_i16::()?; let us_weight_class = rd.read_u16::()?; @@ -384,7 +393,7 @@ mod test { let mut buffer = Vec::new(); os2_table.pack(&mut buffer).unwrap(); assert_eq!( - Os2Table::unpack(&mut Cursor::new(buffer), ()).unwrap(), + Os2Table::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), os2_table ); } diff --git a/otf/src/tables/post.rs b/otf/src/tables/post.rs index 6c8dc7a..43082e5 100644 --- a/otf/src/tables/post.rs +++ b/otf/src/tables/post.rs @@ -1,6 +1,6 @@ -use std::io; +use std::io::{self, Cursor, Read}; -use super::FontTable; +use super::{FontTable, NamedTable}; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; /// This table contains additional information needed to use OTF fonts on PostScript printers. @@ -37,11 +37,20 @@ pub struct PostTable { addition: Vec, } +impl NamedTable for PostTable { + fn name() -> &'static str { + "post" + } +} + impl<'a> FontTable<'a> for PostTable { type UnpackDep = (); type SubsetDep = (); - fn unpack(rd: &mut R, _: Self::UnpackDep) -> Result { + fn unpack>( + rd: &mut Cursor, + _: Self::UnpackDep, + ) -> Result { let major_version = rd.read_u16::()?; let minor_version = rd.read_u16::()?; let italic_angle = rd.read_i32::()?; @@ -119,7 +128,7 @@ mod test { let mut buffer = Vec::new(); post_table.pack(&mut buffer).unwrap(); assert_eq!( - PostTable::unpack(&mut Cursor::new(buffer), ()).unwrap(), + PostTable::unpack(&mut Cursor::new(&buffer[..]), ()).unwrap(), post_table ); } diff --git a/otf/src/utils/limit_read.rs b/otf/src/utils/limit_read.rs index 1774bbd..2a433a6 100644 --- a/otf/src/utils/limit_read.rs +++ b/otf/src/utils/limit_read.rs @@ -1,4 +1,5 @@ -use std::io; +use std::convert::AsRef; +use std::io::{self, Cursor}; pub struct LimitRead { inner: T, @@ -19,6 +20,20 @@ where } } +impl<'a> LimitRead<&'a [u8]> { + pub fn from_cursor(inner: &'a Cursor, limit: usize) -> Self + where + C: AsRef<[u8]> + 'a, + { + let pos = inner.position() as usize; + Self { + inner: &inner.get_ref().as_ref()[pos..], + limit, + already_read: 0, + } + } +} + impl io::Read for LimitRead where T: io::Read, @@ -35,6 +50,15 @@ where } } +impl AsRef<[u8]> for LimitRead +where + T: io::Read + AsRef<[u8]>, +{ + fn as_ref(&self) -> &[u8] { + &self.inner.as_ref()[..self.limit] + } +} + #[cfg(test)] mod test { use std::io::{Cursor, Read};