From ae3af533ffba9e3bb3719eafb4e6339f0e23c1f0 Mon Sep 17 00:00:00 2001 From: Laurenz Date: Sat, 16 Feb 2019 18:57:50 +0100 Subject: [PATCH] =?UTF-8?q?Extract=20opentype=20parser=20into=20crate=20?= =?UTF-8?q?=F0=9F=A7=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.toml | 2 +- src/doc.rs | 17 ---- src/font.rs | 270 ---------------------------------------------------- src/lib.rs | 1 - src/pdf.rs | 36 ++----- 5 files changed, 11 insertions(+), 315 deletions(-) delete mode 100644 src/font.rs diff --git a/Cargo.toml b/Cargo.toml index 7f3e41fa7..1a4442fd6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,6 @@ edition = "2018" [dependencies] pdf = { path = "../pdf" } +opentype = { path = "../opentype" } unicode-segmentation = "1.2" unicode-xid = "0.1.0" -byteorder = "1" diff --git a/src/doc.rs b/src/doc.rs index 66aed5330..cc44ca7d2 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -2,7 +2,6 @@ use std::fmt; use crate::parsing::{SyntaxTree, Node}; -use crate::font::{Font, BuiltinFont}; use pdf::Size; @@ -13,8 +12,6 @@ use pdf::Size; pub struct Document { /// The pages of the document. pub pages: Vec, - /// The fonts used by the document. - pub fonts: Vec, } impl Document { @@ -22,7 +19,6 @@ impl Document { pub fn new() -> Document { Document { pages: vec![], - fonts: vec![], } } } @@ -40,15 +36,6 @@ pub struct Page { #[derive(Debug, Clone, Eq, PartialEq)] pub struct Text(pub String); -/// A font (either built-in or external). -#[derive(Debug, Clone, PartialEq)] -pub enum DocumentFont { - /// One of the 14 built-in fonts. - Builtin(BuiltinFont), - /// An externally loaded font. - Loaded(Font), -} - /// A type that can be generated into a document. pub trait Generate { @@ -93,8 +80,6 @@ impl<'s> Generator<'s> { /// Generate the abstract document. fn generate(&mut self) -> GenResult { - let fonts = vec![DocumentFont::Builtin(BuiltinFont::Helvetica)]; - let mut text = String::new(); for node in &self.tree.nodes { match node { @@ -115,7 +100,6 @@ impl<'s> Generator<'s> { Ok(Document { pages: vec![page], - fonts, }) } @@ -153,7 +137,6 @@ mod generator_tests { ] } ], - fonts: vec![DocumentFont::Builtin(BuiltinFont::Helvetica)], }); } } diff --git a/src/font.rs b/src/font.rs deleted file mode 100644 index 1280aec36..000000000 --- a/src/font.rs +++ /dev/null @@ -1,270 +0,0 @@ -//! Reading of metrics and font data from _OpenType_ and _TrueType_ font files. - -#![allow(unused_variables)] - -use std::fmt; -use std::io::{self, Read, Seek, SeekFrom}; -use byteorder::{BE, ReadBytesExt}; - - -/// A loaded opentype (or truetype) font. -#[derive(Debug, Clone, PartialEq)] -pub struct Font { - /// The PostScript name of this font. - pub name: String, -} - -impl Font { - /// Create a new font from a byte source. - pub fn new(data: &mut R) -> FontResult where R: Read + Seek { - OpenTypeReader::new(data).read() - } -} - -/// Built-in fonts. -#[derive(Debug, Copy, Clone, PartialEq)] -#[allow(missing_docs)] -pub enum BuiltinFont { - Courier, - CourierBold, - CourierOblique, - CourierBoldOblique, - Helvetica, - HelveticaBold, - HelveticaOblique, - HelveticaBoldOblique, - TimesRoman, - TimesBold, - TimeItalic, - TimeBoldItalic, - Symbol, - ZapfDingbats, -} - -impl BuiltinFont { - /// The name of the font. - pub fn name(&self) -> &'static str { - use BuiltinFont::*; - match self { - Courier => "Courier", - CourierBold => "Courier-Bold", - CourierOblique => "Courier-Oblique", - CourierBoldOblique => "Courier-BoldOblique", - Helvetica => "Helvetica", - HelveticaBold => "Helvetica-Bold", - HelveticaOblique => "Helvetica-Oblique", - HelveticaBoldOblique => "Helvetica-BoldOblique", - TimesRoman => "Times-Roman", - TimesBold => "Times-Bold", - TimeItalic => "Time-Italic", - TimeBoldItalic => "Time-BoldItalic", - Symbol => "Symbol", - ZapfDingbats => "ZapfDingbats", - } - } -} - - -/// Result type used for tokenization. -type FontResult = std::result::Result; - -/// A failure when loading a font. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct LoadingError { - /// A message describing the error. - pub message: String, -} - -impl From for LoadingError { - fn from(err: io::Error) -> LoadingError { - LoadingError { message: format!("io error: {}", err) } - } -} - -impl fmt::Display for LoadingError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "font loading error: {}", self.message) - } -} - - -/// Reads a font from a _OpenType_ or _TrueType_ font file. -struct OpenTypeReader<'r, R> where R: Read + Seek { - data: &'r mut R, - font: Font, - table_records: Vec, -} - -/// Used to identify a table, design-variation axis, script, -/// language system, feature, or baseline. -#[derive(Clone, PartialEq)] -struct Tag(pub [u8; 4]); - -impl PartialEq<&str> for Tag { - fn eq(&self, other: &&str) -> bool { - other.as_bytes() == &self.0 - } -} - -impl fmt::Debug for Tag { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "\"{}\"", self) - } -} - -impl fmt::Display for Tag { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let a = self.0; - write!(f, "{}{}{}{}", a[0] as char, a[1] as char, a[2] as char, a[3] as char) - } -} - -/// Stores information about one table. -#[derive(Debug, Clone, PartialEq)] -struct TableRecord { - table: Tag, - check_sum: u32, - offset: u32, - length: u32, -} - -impl<'r, R> OpenTypeReader<'r, R> where R: Read + Seek { - /// Create a new reader from a byte source. - pub fn new(data: &'r mut R) -> OpenTypeReader<'r, R> { - OpenTypeReader { - data, - font: Font { - name: String::new(), - }, - table_records: vec![], - } - } - - /// Read the font from the byte source. - pub fn read(mut self) -> FontResult { - self.read_table_records()?; - self.read_name_table()?; - - Ok(self.font) - } - - /// Read the offset table. - fn read_table_records(&mut self) -> FontResult<()> { - let sfnt_version = self.data.read_u32::()?; - let num_tables = self.data.read_u16::()?; - let search_range = self.data.read_u16::()?; - let entry_selector = self.data.read_u16::()?; - let range_shift = self.data.read_u16::()?; - - let outlines = match sfnt_version { - 0x00010000 => "truetype", - 0x4F54544F => "cff", - _ => return self.err("unsuported font outlines"), - }; - - for _ in 0 .. num_tables { - let table = self.read_tag()?; - let check_sum = self.data.read_u32::()?; - let offset = self.data.read_u32::()?; - let length = self.data.read_u32::()?; - - self.table_records.push(TableRecord { - table, - check_sum, - offset, - length, - }); - } - - Ok(()) - } - - /// Read the name table (gives general information about the font). - fn read_name_table(&mut self) -> FontResult<()> { - let table = match self.table_records.iter().find(|record| record.table == "name") { - Some(table) => table, - None => return self.err("missing 'name' table"), - }; - - self.data.seek(SeekFrom::Start(table.offset as u64))?; - - let format = self.data.read_u16::()?; - let count = self.data.read_u16::()?; - let string_offset = self.data.read_u16::()?; - - let storage = (table.offset + string_offset as u32) as u64; - - let mut name = None; - - for _ in 0 .. count { - let platform_id = self.data.read_u16::()?; - let encoding_id = self.data.read_u16::()?; - let language_id = self.data.read_u16::()?; - let name_id = self.data.read_u16::()?; - let length = self.data.read_u16::()?; - let offset = self.data.read_u16::()?; - - // Postscript name is what we are interested in - if name_id == 6 && platform_id == 3 && encoding_id == 1 { - if length % 2 != 0 { - return self.err("invalid encoded name"); - } - - self.data.seek(SeekFrom::Start(storage + offset as u64))?; - let mut buffer = Vec::with_capacity(length as usize / 2); - - for _ in 0 .. length / 2 { - buffer.push(self.data.read_u16::()?); - } - - name = match String::from_utf16(&buffer) { - Ok(string) => Some(string), - Err(_) => return self.err("invalid encoded name"), - }; - - break; - } - } - - self.font.name = match name { - Some(name) => name, - None => return self.err("missing postscript font name"), - }; - - Ok(()) - } - - /// Read a tag (array of four u8's). - fn read_tag(&mut self) -> FontResult { - let mut tag = [0u8; 4]; - self.data.read(&mut tag)?; - Ok(Tag(tag)) - } - - /// Gives a font loading error with a message. - fn err>(&self, message: S) -> FontResult { - Err(LoadingError { message: message.into() }) - } -} - - -#[cfg(test)] -mod font_tests { - use super::*; - - /// Test if the loaded font is the same as the expected font. - fn test(path: &str, font: Font) { - let mut file = std::fs::File::open(path).unwrap(); - assert_eq!(Font::new(&mut file), Ok(font)); - } - - #[test] - fn opentype() { - test("../fonts/NotoSerif-Regular.ttf", Font { - name: "NotoSerif".to_owned(), - }); - test("../fonts/NotoSansMath-Regular.ttf", Font { - name: "NotoSansMath-Regular".to_owned(), - }); - } -} diff --git a/src/lib.rs b/src/lib.rs index 2959925e8..d50c19f96 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,6 +6,5 @@ pub mod parsing; pub mod doc; -pub mod font; pub mod pdf; pub mod utility; diff --git a/src/pdf.rs b/src/pdf.rs index 14feb2ab7..09a4b7d83 100644 --- a/src/pdf.rs +++ b/src/pdf.rs @@ -1,8 +1,8 @@ //! Writing of documents in the _PDF_ format. use std::io::{self, Write}; -use crate::doc::{Document, DocumentFont}; -use pdf::{PdfWriter, Id, Rect, Size, Version, DocumentCatalog, PageTree, +use crate::doc::Document; +use pdf::{PdfWriter, Id, Rect, Version, DocumentCatalog, PageTree, Page, PageData, Resource, font::Type1Font, Text, Trailer}; @@ -25,12 +25,10 @@ impl WritePdf for W { let pages_start = page_tree_id + 1; let pages_end = pages_start + doc.pages.len() as Id; - let resources_start = pages_end; - let font_start = resources_start; - let font_end = font_start + doc.fonts.len() as Id; - let resources_end = font_end; + let font_start = pages_end; + let font_end = font_start + 1; - let content_start = resources_end; + let content_start = font_end; let content_end = content_start + doc.pages.iter().flat_map(|p| p.contents.iter()).count() as Id; @@ -41,16 +39,12 @@ impl WritePdf for W { page_tree: page_tree_id, })?; - let font_resources: Vec<_> = (1 ..= doc.fonts.len() as u32) - .zip(font_start .. font_end) - .map(|(nr, id)| Resource::Font(nr, id)).collect(); - // Root page tree writer.write_obj(page_tree_id, &PageTree { parent: None, kids: (pages_start .. pages_end).collect(), data: PageData { - resources: Some(font_resources), + resources: Some(vec![Resource::Font(1, font_start)]), .. PageData::default() }, })?; @@ -73,20 +67,10 @@ impl WritePdf for W { id += 1; } - // The resources (fonts) - let mut id = font_start; - for font in &doc.fonts { - match font { - DocumentFont::Builtin(font) => { - writer.write_obj(id, &Type1Font { - base_font: font.name().to_owned(), - })?; - }, - DocumentFont::Loaded(_) => unimplemented!(), - } - - id += 1; - } + // The resources, currently only one hardcoded font + writer.write_obj(font_start, &Type1Font { + base_font: "Helvetica".to_owned(), + })?; // The page contents let mut id = content_start;