diff options
| author | Laurenz <laurmaedje@gmail.com> | 2019-03-11 17:24:00 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2019-03-11 17:24:00 +0100 |
| commit | 67281c4f469716c7f2341676f2ad656d8c544ea3 (patch) | |
| tree | 4a5bd6602306369db2b9e99b7cbb405f72b816d5 /src/font.rs | |
| parent | 5942c3ba2ab1cd71f86749a91bc04e45da175f96 (diff) | |
Redesign document representation 🧱
Diffstat (limited to 'src/font.rs')
| -rw-r--r-- | src/font.rs | 77 |
1 files changed, 67 insertions, 10 deletions
diff --git a/src/font.rs b/src/font.rs index 46082286..1e22e47d 100644 --- a/src/font.rs +++ b/src/font.rs @@ -6,18 +6,62 @@ use std::io::{self, Cursor, Seek, SeekFrom}; use std::collections::HashMap; use byteorder::{BE, ReadBytesExt, WriteBytesExt}; use opentype::{OpenTypeReader, Outlines, TableRecord, Tag}; -use opentype::tables::{Header, CharMap, MaximumProfile, HorizontalMetrics}; +use opentype::tables::{Header, Name, NameEntry, CharMap, MaximumProfile, HorizontalMetrics, OS2}; +use crate::doc::Size; /// An font wrapper which allows to subset a font. +#[derive(Debug, Clone, PartialEq)] pub struct Font { - program: Vec<u8>, + pub name: String, + pub program: Vec<u8>, + pub mapping: HashMap<char, u16>, + pub widths: Vec<Size>, + pub default_glyph: u16, } impl Font { /// Create a new font from a font program. - pub fn new(program: Vec<u8>) -> Font { - Font { program } + pub fn new(program: Vec<u8>) -> Result<Font, opentype::Error> { + let mut readable = Cursor::new(&program); + let mut reader = OpenTypeReader::new(&mut readable); + + let head = reader.read_table::<Header>()?; + let name = reader.read_table::<Name>()?; + let os2 = reader.read_table::<OS2>()?; + let charmap = reader.read_table::<CharMap>()?; + let hmtx = reader.read_table::<HorizontalMetrics>()?; + + let unit_ratio = 1.0 / (head.units_per_em as f32); + let convert = |x| Size::from_points(unit_ratio * x as f32); + + let base_font = name.get_decoded(NameEntry::PostScriptName); + let font_name = base_font.unwrap_or_else(|| "unknown".to_owned()); + let widths = hmtx.metrics.iter().map(|m| convert(m.advance_width)).collect(); + + Ok(Font { + name: font_name, + program, + mapping: charmap.mapping, + widths, + default_glyph: os2.us_default_char.unwrap_or(0), + }) + } + + /// Map a character to it's glyph index. + pub fn map(&self, c: char) -> u16 { + self.mapping.get(&c).map(|&g| g).unwrap_or(self.default_glyph) + } + + /// Encode the given text for our font (into glyph ids). + pub fn encode(&self, text: &str) -> Vec<u8> { + println!("encoding {} with {:?}", text, self.mapping); + let mut bytes = Vec::with_capacity(2 * text.len()); + for glyph in text.chars().map(|c| self.map(c)) { + bytes.push((glyph >> 8) as u8); + bytes.push((glyph & 0xff) as u8); + } + bytes } /// Generate a subsetted version of this font including only the chars listed in @@ -33,7 +77,7 @@ impl Font { chars: C, needed_tables: I1, optional_tables: I2 - ) -> Result<(Vec<u8>, HashMap<char, u16>), SubsettingError> + ) -> Result<Font, SubsettingError> where C: IntoIterator<Item=char>, I1: IntoIterator<Item=S1>, S1: AsRef<str>, @@ -48,7 +92,7 @@ impl Font { tables.sort_by_key(|r| r.tag); Subsetter { - program: &self.program, + font: &self, reader, outlines, tables, @@ -65,7 +109,7 @@ impl Font { struct Subsetter<'p> { // Original font - program: &'p [u8], + font: &'p Font, reader: OpenTypeReader<'p, Cursor<&'p Vec<u8>>>, outlines: Outlines, tables: Vec<TableRecord>, @@ -82,7 +126,7 @@ struct Subsetter<'p> { impl<'p> Subsetter<'p> { fn subset<I1, S1, I2, S2>(mut self, needed_tables: I1, optional_tables: I2) - -> SubsettingResult<(Vec<u8>, HashMap<char, u16>)> + -> SubsettingResult<Font> where I1: IntoIterator<Item=S1>, S1: AsRef<str>, I2: IntoIterator<Item=S2>, S2: AsRef<str> @@ -117,10 +161,21 @@ impl<'p> Subsetter<'p> { self.write_header()?; + let widths = self.glyphs.iter() + .map(|&glyph| self.font.widths.get(glyph as usize).map(|&w| w) + .take_invalid("missing glyph metrics")) + .collect::<SubsettingResult<Vec<_>>>()?; + let mapping = self.chars.into_iter().enumerate().map(|(i, c)| (c, i as u16)) .collect::<HashMap<char, u16>>(); - Ok((self.body, mapping)) + Ok(Font { + name: self.font.name.clone(), + program: self.body, + mapping, + widths, + default_glyph: self.font.default_glyph, + }) } fn build_glyphs(&mut self) -> SubsettingResult<()> { @@ -131,6 +186,8 @@ impl<'p> Subsetter<'p> { self.glyphs.push(cmap.get(c).ok_or_else(|| SubsettingError::MissingCharacter(c))?) } + self.glyphs.push(self.font.default_glyph); + // Composite glyphs may need additional glyphs we have not yet in our list. // So now we have a look at the glyf table to check that and add glyphs // we need additionally. @@ -400,7 +457,7 @@ impl<'p> Subsetter<'p> { Err(_) => return Err(SubsettingError::MissingTable(tag.to_string())), }; - self.program.get(record.offset as usize .. (record.offset + record.length) as usize) + self.font.program.get(record.offset as usize .. (record.offset + record.length) as usize) .take_bytes() } |
