diff options
| author | Laurenz <laurmaedje@gmail.com> | 2020-01-04 22:43:26 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2020-01-04 22:43:26 +0100 |
| commit | 7b84f3b553de672e5374e142467f63b10009aeca (patch) | |
| tree | fd8265abc8c62213520628babc0cfc3a6f8e98aa /src | |
| parent | 5dfaffc5bdfa811c135f0140c0a0ba917eb8c70f (diff) | |
Adopt new font loading engine ⚙
Diffstat (limited to 'src')
| -rw-r--r-- | src/bin/main.rs | 2 | ||||
| -rw-r--r-- | src/export/pdf.rs | 14 | ||||
| -rw-r--r-- | src/layout/actions.rs | 13 | ||||
| -rw-r--r-- | src/layout/mod.rs | 4 | ||||
| -rw-r--r-- | src/layout/text.rs | 55 | ||||
| -rw-r--r-- | src/layout/tree.rs | 7 | ||||
| -rw-r--r-- | src/library/mod.rs | 80 | ||||
| -rw-r--r-- | src/style.rs | 71 |
8 files changed, 135 insertions, 111 deletions
diff --git a/src/bin/main.rs b/src/bin/main.rs index a2b63d6d..e0bcd16d 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -36,7 +36,7 @@ fn run() -> Result<(), Box<dyn std::error::Error>> { .map_err(|_| "failed to read from source file")?; let mut typesetter = Typesetter::new(); - let provider = FileSystemFontProvider::from_listing("fonts/fonts.toml").unwrap(); + let provider = FileSystemFontProvider::from_index("../fonts/index.json").unwrap(); typesetter.add_font_provider(provider); let layouts = typesetter.typeset(&src)?; diff --git a/src/export/pdf.rs b/src/export/pdf.rs index 0d097a33..c5bc6ee8 100644 --- a/src/export/pdf.rs +++ b/src/export/pdf.rs @@ -13,7 +13,7 @@ use tide::font::{ use toddle::Error as FontError; use toddle::font::OwnedFont; -use toddle::query::SharedFontLoader; +use toddle::query::{SharedFontLoader, FontIndex}; use toddle::tables::{ CharMap, Header, HorizontalMetrics, MacStyleFlags, Name, NameEntry, Post, OS2 @@ -59,7 +59,7 @@ struct ExportProcess<'d, W: Write> { /// go through all font usages and assign a new index for each used font. /// This remapping is stored here because we need it when converting the /// layout actions in `ExportProcess::write_page`. - font_remap: HashMap<usize, usize>, + font_remap: HashMap<FontIndex, usize>, /// These are the fonts sorted by their *new* ids, that is, the values of `font_remap`. fonts: Vec<OwnedFont>, @@ -99,13 +99,13 @@ impl<'d, W: Write> ExportProcess<'d, W> { fn subset_fonts( layouts: &'d MultiLayout, font_loader: &SharedFontLoader - ) -> PdfResult<(Vec<OwnedFont>, HashMap<usize, usize>)> + ) -> PdfResult<(Vec<OwnedFont>, HashMap<FontIndex, usize>)> { let mut fonts = Vec::new(); - let mut font_chars: HashMap<usize, HashSet<char>> = HashMap::new(); - let mut old_to_new: HashMap<usize, usize> = HashMap::new(); - let mut new_to_old: HashMap<usize, usize> = HashMap::new(); - let mut active_font = 0; + let mut font_chars: HashMap<FontIndex, HashSet<char>> = HashMap::new(); + let mut old_to_new: HashMap<FontIndex, usize> = HashMap::new(); + let mut new_to_old: HashMap<usize, FontIndex> = HashMap::new(); + let mut active_font = FontIndex::MAX; // We want to find out which fonts are used at all and which are chars // are used for these. We use this information to create subsetted fonts. diff --git a/src/layout/actions.rs b/src/layout/actions.rs index b0d2c21d..4ab9fdb1 100644 --- a/src/layout/actions.rs +++ b/src/layout/actions.rs @@ -1,6 +1,7 @@ //! Drawing and cofiguration actions composing layouts. use std::fmt::{self, Display, Formatter}; +use toddle::query::FontIndex; use super::*; use LayoutAction::*; @@ -11,7 +12,7 @@ pub enum LayoutAction { /// Move to an absolute position. MoveAbsolute(Size2D), /// Set the font by index and font size. - SetFont(usize, Size), + SetFont(FontIndex, Size), /// Write text starting at the current position. WriteText(String), /// Visualize a box for debugging purposes. @@ -22,7 +23,7 @@ impl Serialize for LayoutAction { fn serialize<W: Write>(&self, f: &mut W) -> io::Result<()> { match self { MoveAbsolute(s) => write!(f, "m {:.4} {:.4}", s.x.to_pt(), s.y.to_pt()), - SetFont(i, s) => write!(f, "f {} {}", i, s.to_pt()), + SetFont(i, s) => write!(f, "f {} {} {}", i.id, i.variant, s.to_pt()), WriteText(s) => write!(f, "w {}", s), DebugBox(s) => write!(f, "b {} {}", s.x.to_pt(), s.y.to_pt()), } @@ -34,7 +35,7 @@ impl Display for LayoutAction { use LayoutAction::*; match self { MoveAbsolute(s) => write!(f, "move {} {}", s.x, s.y), - SetFont(i, s) => write!(f, "font {} {}", i, s), + SetFont(i, s) => write!(f, "font {} {} {}", i.id, i.variant, s), WriteText(s) => write!(f, "write \"{}\"", s), DebugBox(s) => write!(f, "box {}", s), } @@ -58,9 +59,9 @@ debug_display!(LayoutAction); pub struct LayoutActions { pub origin: Size2D, actions: Vec<LayoutAction>, - active_font: (usize, Size), + active_font: (FontIndex, Size), next_pos: Option<Size2D>, - next_font: Option<(usize, Size)>, + next_font: Option<(FontIndex, Size)>, } impl LayoutActions { @@ -69,7 +70,7 @@ impl LayoutActions { LayoutActions { actions: vec![], origin: Size2D::ZERO, - active_font: (std::usize::MAX, Size::ZERO), + active_font: (FontIndex::MAX, Size::ZERO), next_pos: None, next_font: None, } diff --git a/src/layout/mod.rs b/src/layout/mod.rs index 53c3e91e..be1ed43c 100644 --- a/src/layout/mod.rs +++ b/src/layout/mod.rs @@ -2,7 +2,7 @@ use std::io::{self, Write}; use smallvec::SmallVec; -use toddle::query::SharedFontLoader; +use toddle::query::{SharedFontLoader, FontIndex}; use crate::size::{Size, Size2D, SizeBox}; use crate::style::LayoutStyle; @@ -58,7 +58,7 @@ pub struct Layout { impl Layout { /// Returns a vector with all used font indices. - pub fn find_used_fonts(&self) -> Vec<usize> { + pub fn find_used_fonts(&self) -> Vec<FontIndex> { let mut fonts = Vec::new(); for action in &self.actions { if let LayoutAction::SetFont(index, _) = action { diff --git a/src/layout/text.rs b/src/layout/text.rs index e9721429..96704f60 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -1,4 +1,4 @@ -use toddle::query::{SharedFontLoader, FontQuery, FontClass}; +use toddle::query::{SharedFontLoader, FontQuery, FontIndex}; use toddle::tables::{CharMap, Header, HorizontalMetrics}; use crate::size::{Size, Size2D}; @@ -30,9 +30,8 @@ struct TextLayouter<'a, 'p> { text: &'a str, actions: LayoutActions, buffer: String, - active_font: usize, + active_font: FontIndex, width: Size, - classes: Vec<FontClass>, } impl<'a, 'p> TextLayouter<'a, 'p> { @@ -43,9 +42,8 @@ impl<'a, 'p> TextLayouter<'a, 'p> { text, actions: LayoutActions::new(), buffer: String::new(), - active_font: std::usize::MAX, + active_font: FontIndex::MAX, width: Size::ZERO, - classes: ctx.style.classes.clone(), } } @@ -95,39 +93,34 @@ impl<'a, 'p> TextLayouter<'a, 'p> { /// Select the best font for a character and return its index along with /// the width of the char in the font. - fn select_font(&mut self, c: char) -> LayoutResult<(usize, Size)> { + fn select_font(&mut self, c: char) -> LayoutResult<(FontIndex, Size)> { let mut loader = self.ctx.loader.borrow_mut(); - for class in &self.ctx.style.fallback { - self.classes.push(class.clone()); + let query = FontQuery { + fallback: &self.ctx.style.fallback, + variant: self.ctx.style.variant, + c, + }; - let query = FontQuery { - chars: &[c], - classes: &self.classes, - }; + if let Some((font, index)) = loader.get(query) { + let font_unit_ratio = 1.0 / (font.read_table::<Header>()?.units_per_em as f32); + let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); - if let Some((font, index)) = loader.get(query) { - let font_unit_ratio = 1.0 / (font.read_table::<Header>()?.units_per_em as f32); - let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); + let glyph = font + .read_table::<CharMap>()? + .get(c) + .expect("select_font: font should have char"); - let glyph = font - .read_table::<CharMap>()? - .get(c) - .expect("select_font: font should have char"); + let glyph_width = font + .read_table::<HorizontalMetrics>()? + .get(glyph) + .expect("select_font: font should have glyph") + .advance_width as f32; - let glyph_width = font - .read_table::<HorizontalMetrics>()? - .get(glyph) - .expect("select_font: font should have glyph") - .advance_width as f32; + let char_width = font_unit_to_size(glyph_width) + * self.ctx.style.font_size().to_pt(); - let char_width = font_unit_to_size(glyph_width) - * self.ctx.style.font_size().to_pt(); - - return Ok((index, char_width)); - } - - self.classes.pop(); + return Ok((index, char_width)); } error!("no suitable font for character `{}`", c); diff --git a/src/layout/tree.rs b/src/layout/tree.rs index db59ca8d..e77fd528 100644 --- a/src/layout/tree.rs +++ b/src/layout/tree.rs @@ -1,5 +1,4 @@ use smallvec::smallvec; -use toddle::query::FontClass; use crate::func::Command; use crate::syntax::{SyntaxTree, Node, FuncCall}; @@ -45,9 +44,9 @@ impl<'a, 'p> TreeLayouter<'a, 'p> { Node::Space => self.layout_space(), Node::Newline => self.layout_paragraph()?, - Node::ToggleItalics => self.style.text.toggle_class(FontClass::Italic), - Node::ToggleBold => self.style.text.toggle_class(FontClass::Bold), - Node::ToggleMonospace => self.style.text.toggle_class(FontClass::Monospace), + Node::ToggleItalics => {}, + Node::ToggleBold => {}, + Node::ToggleMonospace => {}, Node::Func(func) => self.layout_func(func)?, } diff --git a/src/library/mod.rs b/src/library/mod.rs index 7f7a0411..d4519867 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -1,6 +1,6 @@ //! The standard library. -use toddle::query::FontClass; +use toddle::query::{FontWeight, FontStyle}; use crate::func::prelude::*; use crate::style::{Paper, PaperClass}; @@ -36,11 +36,10 @@ pub fn std() -> Scope { std.add_with_metadata::<Spacing>("h", Some(Horizontal)); std.add_with_metadata::<Spacing>("v", Some(Vertical)); - std.add_with_metadata::<StyleChange>("bold", FontClass::Bold); - std.add_with_metadata::<StyleChange>("italic", FontClass::Italic); - std.add_with_metadata::<StyleChange>("mono", FontClass::Monospace); - - std.add::<FontFamily>("font.family"); + std.add_with_metadata::<FontFamily>("font.family", None); + std.add_with_metadata::<FontFamily>("mono", Some("monospace".to_string())); + std.add::<SetFontStyle>("font.style"); + std.add::<SetFontWeight>("font.weight"); std.add::<FontSize>("font.size"); std @@ -218,25 +217,66 @@ function! { } function! { - /// `bold`, `italic`, `mono`: Sets text with a different style. + /// `font.weight`, `bold`: Set text with a given weight. #[derive(Debug, PartialEq)] - pub struct StyleChange { + pub struct SetFontWeight { body: Option<SyntaxTree>, - class: FontClass, + weight: FontWeight, } - type Meta = FontClass; - parse(args, body, ctx, meta) { - StyleChange { + SetFontWeight { + body: parse!(optional: body, ctx), + weight: match args.get_pos::<Expression>()? { + Expression::Num(weight) => FontWeight(if weight < 0.0 { + 0 + } else if weight < 1000.0 { + weight.round() as u16 + } else { + 1000 + }), + Expression::Ident(Ident(s)) => { + match FontWeight::from_str(&s) { + Some(weight) => weight, + None => error!("invalid font weight: `{}`", s), + } + } + _ => error!("expected identifier or number"), + }, + } + } + + layout(self, ctx) { + let mut style = ctx.style.text.clone(); + style.variant.style.toggle(); + styled(&self.body, &ctx, style) + } +} + +function! { + /// `font.style`: Set the font style (normal / italic). + #[derive(Debug, PartialEq)] + pub struct SetFontStyle { + body: Option<SyntaxTree>, + style: FontStyle, + } + + parse(args, body, ctx) { + SetFontStyle { body: parse!(optional: body, ctx), - class: meta, + style: { + let s = args.get_pos::<String>()?; + match FontStyle::from_str(&s) { + Some(style) => style, + None => error!("invalid font style: `{}`", s), + } + } } } layout(self, ctx) { let mut style = ctx.style.text.clone(); - style.toggle_class(self.class.clone()); + style.variant.style = self.style; styled(&self.body, &ctx, style) } } @@ -249,16 +289,22 @@ function! { family: String, } - parse(args, body, ctx) { + type Meta = Option<String>; + + parse(args, body, ctx, meta) { FontFamily { body: parse!(optional: body, ctx), - family: args.get_pos::<String>()?, + family: if let Some(family) = meta { + family + } else { + args.get_pos::<String>()? + }, } } layout(self, ctx) { let mut style = ctx.style.text.clone(); - style.fallback.insert(0, FontClass::Family(self.family.clone())); + style.fallback.list = vec![self.family.clone()]; styled(&self.body, &ctx, style) } } diff --git a/src/style.rs b/src/style.rs index df5e13d1..ffa10d51 100644 --- a/src/style.rs +++ b/src/style.rs @@ -1,11 +1,11 @@ //! Styles for text and pages. -use toddle::query::FontClass; -use FontClass::*; +use toddle::query::{FontFallbackTree, FontVariant, FontStyle, FontWeight}; use crate::size::{Size, Size2D, SizeBox, ValueBox, PSize}; use crate::syntax::ParseResult; + /// Defines properties of pages and text. #[derive(Debug, Default, Clone)] pub struct LayoutStyle { @@ -16,11 +16,10 @@ pub struct LayoutStyle { /// Defines which fonts to use and how to space text. #[derive(Debug, Clone)] pub struct TextStyle { - /// The classes the font has to be part of. - pub classes: Vec<FontClass>, - /// The fallback classes from which the font needs to match the leftmost - /// possible one. - pub fallback: Vec<FontClass>, + /// A tree of font names and generic family names. + pub fallback: FontFallbackTree, + /// The selected font variant. + pub variant: FontVariant, /// The base font size. pub base_font_size: Size, /// The font scale to apply on the base font size. @@ -53,48 +52,34 @@ impl TextStyle { pub fn paragraph_spacing(&self) -> Size { (self.paragraph_spacing_scale - 1.0) * self.font_size() } +} - /// Toggle a class. - /// - /// If the class was one of _italic_ or _bold_, then: - /// - If it was not present before, the _regular_ class will be removed. - /// - If it was present before, the _regular_ class will be added in case - /// the other style class is not present. - pub fn toggle_class(&mut self, class: FontClass) { - if self.classes.contains(&class) { - // If we retain a Bold or Italic class, we will not add the Regular - // class. - let mut regular = true; - self.classes.retain(|x| { - if class == *x { - false - } else { - if class == Bold || class == Italic { - regular = false; - } - true - } - }); - - if regular { - self.classes.push(Regular); - } - } else { - // If we add an Italic or Bold class, we remove the Regular class. - if class == Italic || class == Bold { - self.classes.retain(|x| x != &Regular); - } - - self.classes.push(class); - } - } +macro_rules! fallback { + (($($f:expr),*), $($c:expr => ($($cf:expr),*)),*) => ({ + let mut fallback = FontFallbackTree::new(vec![$($f.to_string()),*]); + $( + fallback.set_class_list($c.to_string(), vec![$($cf.to_string()),*]) + .expect("TextStyle::default: unexpected error \ + when setting class list"); + )* + fallback + }); } impl Default for TextStyle { fn default() -> TextStyle { TextStyle { - classes: vec![Regular], - fallback: vec![Serif], + fallback: fallback! { + ("sans-serif"), + "serif" => ("source serif pro", "noto serif", "noto emoji"), + "sans-serif" => ("source sans pro", "noto sans", "noto emoji"), + "monospace" => ("source code pro", "noto sans mono", "noto emoji"), + "math" => ("latin modern math", "serif") + }, + variant: FontVariant { + style: FontStyle::Normal, + weight: FontWeight(400), + }, base_font_size: Size::pt(11.0), font_scale: 1.0, word_spacing_scale: 0.25, |
