Add more tests for table parsers 🔋

This commit is contained in:
Laurenz 2019-06-25 21:07:06 +02:00
parent 030d301f0c
commit 7eec0b8dd7
3 changed files with 41 additions and 22 deletions

View File

@ -270,10 +270,18 @@ impl PdfFont {
}
// Subset the font using the selected characters.
let subsetted = font.subsetted(
let subset_result = font.subsetted(
chars.iter().cloned(),
&["head", "hhea", "hmtx", "maxp", "cmap", "cvt ", "fpgm", "prep", "loca", "glyf"][..]
)?;
);
// Check if the subsetting was successful and if it could not handle this
// font we just copy it plainly.
let subsetted = match subset_result {
Ok(font) => font,
Err(FontError::UnsupportedFont(_)) => font.clone(),
Err(err) => return Err(err.into()),
};
// Specify flags for the font.
let mut flags = FontFlags::empty();

View File

@ -83,14 +83,15 @@ impl Font {
// Create a conversion function between font units and sizes.
let font_unit_ratio = 1.0 / (head.units_per_em as f32);
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x as f32);
let font_unit_to_size = |x| Size::pt(font_unit_ratio * x);
// Find out the name of the font.
let font_name = name.get_decoded(NameEntry::PostScriptName)
.unwrap_or_else(|| "unknown".to_owned());
// Convert the widths from font units to sizes.
let widths = hmtx.metrics.iter().map(|m| font_unit_to_size(m.advance_width)).collect();
let widths = hmtx.metrics.iter()
.map(|m| font_unit_to_size(m.advance_width as f32)).collect();
// Calculate the typesetting-relevant metrics.
let metrics = FontMetrics {
@ -98,14 +99,14 @@ impl Font {
monospace: post.is_fixed_pitch,
italic_angle: post.italic_angle.to_f32(),
bounding_box: [
font_unit_to_size(head.x_min),
font_unit_to_size(head.y_min),
font_unit_to_size(head.x_max),
font_unit_to_size(head.y_max),
font_unit_to_size(head.x_min as f32),
font_unit_to_size(head.y_min as f32),
font_unit_to_size(head.x_max as f32),
font_unit_to_size(head.y_max as f32),
],
ascender: font_unit_to_size(os2.s_typo_ascender),
descender: font_unit_to_size(os2.s_typo_descender),
cap_height: font_unit_to_size(os2.s_cap_height.unwrap_or(os2.s_typo_ascender)),
ascender: font_unit_to_size(os2.s_typo_ascender as f32),
descender: font_unit_to_size(os2.s_typo_descender as f32),
cap_height: font_unit_to_size(os2.s_cap_height.unwrap_or(os2.s_typo_ascender) as f32),
weight_class: os2.us_weight_class,
};
@ -352,7 +353,6 @@ error_type! {
OpentypeError::InvalidFont(message) => FontError::InvalidFont(message),
OpentypeError::MissingTable(tag) => FontError::MissingTable(tag.to_string()),
OpentypeError::Io(err) => FontError::Io(err),
_ => panic!("unexpected extensible variant"),
}),
}

View File

@ -5,7 +5,7 @@ use std::io::{Cursor, Seek, SeekFrom};
use byteorder::{BE, ReadBytesExt, WriteBytesExt};
use opentype::{OpenTypeReader, Outlines, Table, TableRecord, Tag};
use opentype::tables::{CharMap, Locations, HorizontalMetrics, Glyphs};
use opentype::tables::{Header, CharMap, Locations, HorizontalMetrics, Glyphs};
use crate::size::Size;
use super::{Font, FontError, FontResult};
@ -56,8 +56,7 @@ impl<'a> Subsetter<'a> {
/// Do the subsetting.
fn run<I, S>(mut self, tables: I) -> FontResult<Font>
where I: IntoIterator<Item=S>, S: AsRef<str> {
// Quit early if we cannot handle the font.
if self.outlines != Outlines::TrueType {
if self.outlines == Outlines::CFF {
return Err(FontError::UnsupportedFont("CFF outlines".to_string()));
}
@ -216,7 +215,7 @@ impl<'a> Subsetter<'a> {
})
}
/// Subset the `hhea` table by changing the glyph count in it.
/// Subset the `hhea` table by changing the number of horizontal metrics in it.
fn subset_hhea(&mut self) -> FontResult<()> {
let tag = "hhea".parse().unwrap();
let hhea = self.read_table_data(tag)?;
@ -235,7 +234,7 @@ impl<'a> Subsetter<'a> {
self.write_table_body(tag, |this| {
for &glyph in &this.glyphs {
let metrics = hmtx.get(glyph).take_invalid("missing glyph metrics")?;
this.body.write_i16::<BE>(metrics.advance_width)?;
this.body.write_u16::<BE>(metrics.advance_width)?;
this.body.write_i16::<BE>(metrics.left_side_bearing)?;
}
Ok(())
@ -352,8 +351,14 @@ impl<'a> Subsetter<'a> {
break;
}
let args_len = if flags & 0x0001 == 1 { 4 } else { 2 };
cursor.seek(SeekFrom::Current(args_len))?;
// Skip additional arguments.
let skip = if flags & 1 != 0 { 4 } else { 2 }
+ if flags & 8 != 0 { 2 }
else if flags & 64 != 0 { 4 }
else if flags & 128 != 0 { 8 }
else { 0 };
cursor.seek(SeekFrom::Current(skip))?;
}
}
@ -365,13 +370,19 @@ impl<'a> Subsetter<'a> {
/// Subset the `loca` table by changing to the new offsets.
fn subset_loca(&mut self) -> FontResult<()> {
let format = self.read_table::<Header>()?.index_to_loc_format;
let tag = "loca".parse().unwrap();
let loca = self.read_table::<Locations>()?;
self.write_table_body(tag, |this| {
let mut offset = 0;
for &glyph in &this.glyphs {
this.body.write_u32::<BE>(offset)?;
if format == 0 {
this.body.write_u16::<BE>((offset / 2) as u16)?;
} else {
this.body.write_u32::<BE>(offset)?;
}
let len = loca.length(glyph).take_invalid("missing loca entry")?;
offset += len;
}
@ -458,7 +469,7 @@ mod tests {
#[test]
fn subset() {
let program = std::fs::read("../fonts/NotoSans-Regular.ttf").unwrap();
let program = std::fs::read("../fonts/SourceSansPro-Regular.ttf").unwrap();
let font = Font::new(program).unwrap();
let subsetted = font.subsetted(
@ -467,6 +478,6 @@ mod tests {
"cvt ", "fpgm", "prep", "loca", "glyf"][..]
).unwrap();
std::fs::write("../target/NotoSans-Subsetted.ttf", &subsetted.program).unwrap();
std::fs::write("../target/SourceSansPro-Subsetted.ttf", &subsetted.program).unwrap();
}
}