mirror of
https://github.com/typst/typst
synced 2025-05-13 20:46:23 +08:00
Unified font error type 🔗
This commit is contained in:
parent
9b3386f8a3
commit
aae8a3a77e
95
src/font.rs
95
src/font.rs
@ -51,7 +51,7 @@ pub struct FontMetrics {
|
|||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
/// Create a new font from a font program.
|
/// Create a new font from a font program.
|
||||||
pub fn new(program: Vec<u8>) -> Result<Font, opentype::Error> {
|
pub fn new(program: Vec<u8>) -> FontResult<Font> {
|
||||||
// Create opentype reader to parse font tables
|
// Create opentype reader to parse font tables
|
||||||
let mut readable = Cursor::new(&program);
|
let mut readable = Cursor::new(&program);
|
||||||
let mut reader = OpenTypeReader::new(&mut readable);
|
let mut reader = OpenTypeReader::new(&mut readable);
|
||||||
@ -131,7 +131,7 @@ impl Font {
|
|||||||
chars: C,
|
chars: C,
|
||||||
needed_tables: I1,
|
needed_tables: I1,
|
||||||
optional_tables: I2
|
optional_tables: I2
|
||||||
) -> Result<Font, SubsettingError>
|
) -> Result<Font, FontError>
|
||||||
where
|
where
|
||||||
C: IntoIterator<Item=char>,
|
C: IntoIterator<Item=char>,
|
||||||
I1: IntoIterator<Item=S1>, S1: AsRef<str>,
|
I1: IntoIterator<Item=S1>, S1: AsRef<str>,
|
||||||
@ -181,7 +181,7 @@ struct Subsetter<'p> {
|
|||||||
|
|
||||||
impl<'p> Subsetter<'p> {
|
impl<'p> Subsetter<'p> {
|
||||||
fn subset<I1, S1, I2, S2>(mut self, needed_tables: I1, optional_tables: I2)
|
fn subset<I1, S1, I2, S2>(mut self, needed_tables: I1, optional_tables: I2)
|
||||||
-> SubsetResult<Font>
|
-> FontResult<Font>
|
||||||
where
|
where
|
||||||
I1: IntoIterator<Item=S1>, S1: AsRef<str>,
|
I1: IntoIterator<Item=S1>, S1: AsRef<str>,
|
||||||
I2: IntoIterator<Item=S2>, S2: AsRef<str>
|
I2: IntoIterator<Item=S2>, S2: AsRef<str>
|
||||||
@ -194,12 +194,12 @@ impl<'p> Subsetter<'p> {
|
|||||||
for table in needed_tables.into_iter() {
|
for table in needed_tables.into_iter() {
|
||||||
let table = table.as_ref();
|
let table = table.as_ref();
|
||||||
let tag: Tag = table.parse()
|
let tag: Tag = table.parse()
|
||||||
.map_err(|_| SubsettingError::UnsupportedTable(table.to_string()))?;
|
.map_err(|_| FontError::UnsupportedTable(table.to_string()))?;
|
||||||
|
|
||||||
if self.contains(tag) {
|
if self.contains(tag) {
|
||||||
self.write_table(tag)?;
|
self.write_table(tag)?;
|
||||||
} else {
|
} else {
|
||||||
return Err(SubsettingError::MissingTable(tag.to_string()));
|
return Err(FontError::MissingTable(tag.to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ impl<'p> Subsetter<'p> {
|
|||||||
for table in optional_tables.into_iter() {
|
for table in optional_tables.into_iter() {
|
||||||
let table = table.as_ref();
|
let table = table.as_ref();
|
||||||
let tag: Tag = table.parse()
|
let tag: Tag = table.parse()
|
||||||
.map_err(|_| SubsettingError::UnsupportedTable(table.to_string()))?;
|
.map_err(|_| FontError::UnsupportedTable(table.to_string()))?;
|
||||||
|
|
||||||
if self.contains(tag) {
|
if self.contains(tag) {
|
||||||
self.write_table(tag)?;
|
self.write_table(tag)?;
|
||||||
@ -219,7 +219,7 @@ impl<'p> Subsetter<'p> {
|
|||||||
let widths = self.glyphs.iter()
|
let widths = self.glyphs.iter()
|
||||||
.map(|&glyph| self.font.widths.get(glyph as usize).map(|&w| w)
|
.map(|&glyph| self.font.widths.get(glyph as usize).map(|&w| w)
|
||||||
.take_invalid("missing glyph metrics"))
|
.take_invalid("missing glyph metrics"))
|
||||||
.collect::<SubsetResult<Vec<_>>>()?;
|
.collect::<FontResult<Vec<_>>>()?;
|
||||||
|
|
||||||
let mapping = self.chars.into_iter().enumerate().map(|(i, c)| (c, i as u16))
|
let mapping = self.chars.into_iter().enumerate().map(|(i, c)| (c, i as u16))
|
||||||
.collect::<HashMap<char, u16>>();
|
.collect::<HashMap<char, u16>>();
|
||||||
@ -234,12 +234,12 @@ impl<'p> Subsetter<'p> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_glyphs(&mut self) -> SubsetResult<()> {
|
fn build_glyphs(&mut self) -> FontResult<()> {
|
||||||
self.read_cmap()?;
|
self.read_cmap()?;
|
||||||
let cmap = self.cmap.as_ref().unwrap();
|
let cmap = self.cmap.as_ref().unwrap();
|
||||||
|
|
||||||
for &c in &self.chars {
|
for &c in &self.chars {
|
||||||
self.glyphs.push(cmap.get(c).ok_or_else(|| SubsettingError::MissingCharacter(c))?)
|
self.glyphs.push(cmap.get(c).ok_or_else(|| FontError::MissingCharacter(c))?)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.glyphs.push(self.font.default_glyph);
|
self.glyphs.push(self.font.default_glyph);
|
||||||
@ -294,7 +294,7 @@ impl<'p> Subsetter<'p> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_header(&mut self) -> SubsetResult<()> {
|
fn write_header(&mut self) -> FontResult<()> {
|
||||||
// Create an output buffer
|
// Create an output buffer
|
||||||
let header_len = 12 + self.records.len() * 16;
|
let header_len = 12 + self.records.len() * 16;
|
||||||
let mut header = Vec::with_capacity(header_len);
|
let mut header = Vec::with_capacity(header_len);
|
||||||
@ -336,7 +336,7 @@ impl<'p> Subsetter<'p> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_table(&mut self, tag: Tag) -> SubsetResult<()> {
|
fn write_table(&mut self, tag: Tag) -> FontResult<()> {
|
||||||
match tag.value() {
|
match tag.value() {
|
||||||
b"head" | b"cvt " | b"prep" | b"fpgm" | b"name" | b"post" | b"OS/2" => {
|
b"head" | b"cvt " | b"prep" | b"fpgm" | b"name" | b"post" | b"OS/2" => {
|
||||||
self.copy_table(tag)
|
self.copy_table(tag)
|
||||||
@ -478,19 +478,19 @@ impl<'p> Subsetter<'p> {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => Err(SubsettingError::UnsupportedTable(tag.to_string())),
|
_ => Err(FontError::UnsupportedTable(tag.to_string())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_table(&mut self, tag: Tag) -> SubsetResult<()> {
|
fn copy_table(&mut self, tag: Tag) -> FontResult<()> {
|
||||||
self.write_table_body(tag, |this| {
|
self.write_table_body(tag, |this| {
|
||||||
let table = this.get_table_data(tag)?;
|
let table = this.get_table_data(tag)?;
|
||||||
Ok(this.body.extend(table))
|
Ok(this.body.extend(table))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_table_body<F>(&mut self, tag: Tag, writer: F) -> SubsetResult<()>
|
fn write_table_body<F>(&mut self, tag: Tag, writer: F) -> FontResult<()>
|
||||||
where F: FnOnce(&mut Self) -> SubsetResult<()> {
|
where F: FnOnce(&mut Self) -> FontResult<()> {
|
||||||
let start = self.body.len();
|
let start = self.body.len();
|
||||||
writer(self)?;
|
writer(self)?;
|
||||||
let end = self.body.len();
|
let end = self.body.len();
|
||||||
@ -506,10 +506,10 @@ impl<'p> Subsetter<'p> {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_table_data(&self, tag: Tag) -> SubsetResult<&'p [u8]> {
|
fn get_table_data(&self, tag: Tag) -> FontResult<&'p [u8]> {
|
||||||
let record = match self.tables.binary_search_by_key(&tag, |r| r.tag) {
|
let record = match self.tables.binary_search_by_key(&tag, |r| r.tag) {
|
||||||
Ok(index) => &self.tables[index],
|
Ok(index) => &self.tables[index],
|
||||||
Err(_) => return Err(SubsettingError::MissingTable(tag.to_string())),
|
Err(_) => return Err(FontError::MissingTable(tag.to_string())),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.font.program
|
self.font.program
|
||||||
@ -521,19 +521,19 @@ impl<'p> Subsetter<'p> {
|
|||||||
self.tables.binary_search_by_key(&tag, |r| r.tag).is_ok()
|
self.tables.binary_search_by_key(&tag, |r| r.tag).is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_cmap(&mut self) -> SubsetResult<()> {
|
fn read_cmap(&mut self) -> FontResult<()> {
|
||||||
Ok(if self.cmap.is_none() {
|
Ok(if self.cmap.is_none() {
|
||||||
self.cmap = Some(self.reader.read_table::<CharMap>()?);
|
self.cmap = Some(self.reader.read_table::<CharMap>()?);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_hmtx(&mut self) -> SubsetResult<()> {
|
fn read_hmtx(&mut self) -> FontResult<()> {
|
||||||
Ok(if self.hmtx.is_none() {
|
Ok(if self.hmtx.is_none() {
|
||||||
self.hmtx = Some(self.reader.read_table::<HorizontalMetrics>()?);
|
self.hmtx = Some(self.reader.read_table::<HorizontalMetrics>()?);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_loca(&mut self) -> SubsetResult<()> {
|
fn read_loca(&mut self) -> FontResult<()> {
|
||||||
Ok(if self.loca.is_none() {
|
Ok(if self.loca.is_none() {
|
||||||
let mut table = self.get_table_data("loca".parse().unwrap())?;
|
let mut table = self.get_table_data("loca".parse().unwrap())?;
|
||||||
let format = self.reader.read_table::<Header>()?.index_to_loc_format;
|
let format = self.reader.read_table::<Header>()?.index_to_loc_format;
|
||||||
@ -571,72 +571,81 @@ fn calculate_check_sum(data: &[u8]) -> u32 {
|
|||||||
trait TakeInvalid<T>: Sized {
|
trait TakeInvalid<T>: Sized {
|
||||||
/// Pull the type out of the option, returning a subsetting error
|
/// Pull the type out of the option, returning a subsetting error
|
||||||
/// about an invalid font wrong.
|
/// about an invalid font wrong.
|
||||||
fn take_invalid<S: Into<String>>(self, message: S) -> SubsetResult<T>;
|
fn take_invalid<S: Into<String>>(self, message: S) -> FontResult<T>;
|
||||||
|
|
||||||
/// Pull the type out of the option, returning an error about missing
|
/// Pull the type out of the option, returning an error about missing
|
||||||
/// bytes if it is nothing.
|
/// bytes if it is nothing.
|
||||||
fn take_bytes(self) -> SubsetResult<T> {
|
fn take_bytes(self) -> FontResult<T> {
|
||||||
self.take_invalid("expected more bytes")
|
self.take_invalid("expected more bytes")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TakeInvalid<T> for Option<T> {
|
impl<T> TakeInvalid<T> for Option<T> {
|
||||||
fn take_invalid<S: Into<String>>(self, message: S) -> SubsetResult<T> {
|
fn take_invalid<S: Into<String>>(self, message: S) -> FontResult<T> {
|
||||||
self.ok_or(SubsettingError::Opentype(opentype::Error::InvalidFont(message.into())))
|
self.ok_or(FontError::InvalidFont(message.into()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SubsetResult<T> = Result<T, SubsettingError>;
|
type FontResult<T> = Result<T, FontError>;
|
||||||
|
|
||||||
/// The error type for font subsetting.
|
/// The error type for font subsetting.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SubsettingError {
|
pub enum FontError {
|
||||||
|
/// The font file is incorrect.
|
||||||
|
InvalidFont(String),
|
||||||
/// A requested table was not present in the source font.
|
/// A requested table was not present in the source font.
|
||||||
MissingTable(String),
|
MissingTable(String),
|
||||||
/// The table is unknown to the engine (unimplemented or invalid).
|
/// The table is unknown to the subsetting engine (unimplemented or invalid).
|
||||||
UnsupportedTable(String),
|
UnsupportedTable(String),
|
||||||
/// A requested character was not present in the source font.
|
/// A requested character was not present in the source font.
|
||||||
MissingCharacter(char),
|
MissingCharacter(char),
|
||||||
/// There was an error while parsing the font file.
|
/// An I/O Error occured while reading the font program.
|
||||||
Opentype(opentype::Error),
|
Io(io::Error),
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
__Extensible,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl error::Error for SubsettingError {
|
impl error::Error for FontError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||||
match self {
|
match self {
|
||||||
SubsettingError::Opentype(err) => Some(err),
|
FontError::Io(err) => Some(err),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for SubsettingError {
|
impl fmt::Display for FontError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
use SubsettingError::*;
|
use FontError::*;
|
||||||
match self {
|
match self {
|
||||||
|
InvalidFont(message) => write!(f, "invalid font: {}", message),
|
||||||
MissingTable(table) => write!(f, "missing table: {}", table),
|
MissingTable(table) => write!(f, "missing table: {}", table),
|
||||||
UnsupportedTable(table) => write!(f, "unsupported table: {}", table),
|
UnsupportedTable(table) => write!(f, "unsupported table: {}", table),
|
||||||
MissingCharacter(c) => write!(f, "missing character: {}", c),
|
MissingCharacter(c) => write!(f, "missing character: '{}'", c),
|
||||||
Opentype(err) => fmt::Display::fmt(err, f),
|
Io(err) => fmt::Display::fmt(err, f),
|
||||||
|
__Extensible => panic!("tried to display extensible variant"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<io::Error> for SubsettingError {
|
impl From<io::Error> for FontError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(err: io::Error) -> SubsettingError {
|
fn from(err: io::Error) -> FontError {
|
||||||
SubsettingError::Opentype(err.into())
|
FontError::Io(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<opentype::Error> for SubsettingError {
|
impl From<opentype::Error> for FontError {
|
||||||
#[inline]
|
fn from(err: opentype::Error) -> FontError {
|
||||||
fn from(err: opentype::Error) -> SubsettingError {
|
use opentype::Error::*;
|
||||||
match err {
|
match err {
|
||||||
opentype::Error::MissingTable(s) => SubsettingError::MissingTable(s),
|
InvalidFont(message) => FontError::InvalidFont(message),
|
||||||
err => SubsettingError::Opentype(err),
|
MissingTable(tag) => FontError::MissingTable(tag.to_string()),
|
||||||
|
Io(err) => FontError::Io(err),
|
||||||
|
_ => panic!("unexpected extensible variant"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
//!
|
//!
|
||||||
//! // Create a compiler and export a PDF.
|
//! // Create a compiler and export a PDF.
|
||||||
//! let src = "Hello World from Typeset!";
|
//! let src = "Hello World from Typeset!";
|
||||||
//! let compiler = Compiler::new(src);
|
//! let compiler = Compiler::new();
|
||||||
//!
|
//!
|
||||||
//! // Write the document into a file as a PDF.
|
//! // Write the document into a file as a PDF.
|
||||||
//! compiler.write_pdf(&mut file).unwrap();
|
//! compiler.write_pdf(src, &mut file).unwrap();
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
pub mod syntax;
|
pub mod syntax;
|
||||||
|
@ -297,9 +297,9 @@ impl From<io::Error> for PdfWritingError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<crate::font::SubsettingError> for PdfWritingError {
|
impl From<crate::font::FontError> for PdfWritingError {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn from(err: crate::font::SubsettingError) -> PdfWritingError {
|
fn from(err: crate::font::FontError) -> PdfWritingError {
|
||||||
PdfWritingError { message: format!("{}", err) }
|
PdfWritingError { message: format!("{}", err) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user