diff --git a/fonts/CMU-SansSerif-Bold-Italic.ttf b/fonts/CMU-SansSerif-Bold-Italic.ttf deleted file mode 100644 index 529277b53..000000000 Binary files a/fonts/CMU-SansSerif-Bold-Italic.ttf and /dev/null differ diff --git a/fonts/CMU-SansSerif-Bold.ttf b/fonts/CMU-SansSerif-Bold.ttf deleted file mode 100644 index d13901bec..000000000 Binary files a/fonts/CMU-SansSerif-Bold.ttf and /dev/null differ diff --git a/fonts/CMU-SansSerif-Italic.ttf b/fonts/CMU-SansSerif-Italic.ttf deleted file mode 100644 index bd30a3f97..000000000 Binary files a/fonts/CMU-SansSerif-Italic.ttf and /dev/null differ diff --git a/fonts/CMU-SansSerif-Regular.ttf b/fonts/CMU-SansSerif-Regular.ttf deleted file mode 100644 index d7513c86a..000000000 Binary files a/fonts/CMU-SansSerif-Regular.ttf and /dev/null differ diff --git a/fonts/CMU-Serif-Bold-Italic.ttf b/fonts/CMU-Serif-Bold-Italic.ttf deleted file mode 100644 index 3b529ed8d..000000000 Binary files a/fonts/CMU-Serif-Bold-Italic.ttf and /dev/null differ diff --git a/fonts/CMU-Serif-Bold.ttf b/fonts/CMU-Serif-Bold.ttf deleted file mode 100644 index 2c7198e5d..000000000 Binary files a/fonts/CMU-Serif-Bold.ttf and /dev/null differ diff --git a/fonts/CMU-Serif-Italic.ttf b/fonts/CMU-Serif-Italic.ttf deleted file mode 100644 index 993d5c029..000000000 Binary files a/fonts/CMU-Serif-Italic.ttf and /dev/null differ diff --git a/fonts/CMU-Serif-Regular.ttf b/fonts/CMU-Serif-Regular.ttf deleted file mode 100644 index 1c3fff0a6..000000000 Binary files a/fonts/CMU-Serif-Regular.ttf and /dev/null differ diff --git a/fonts/CMU-Typewriter-Bold-Italic.ttf b/fonts/CMU-Typewriter-Bold-Italic.ttf deleted file mode 100644 index c7db8b714..000000000 Binary files a/fonts/CMU-Typewriter-Bold-Italic.ttf and /dev/null differ diff --git a/fonts/CMU-Typewriter-Bold.ttf b/fonts/CMU-Typewriter-Bold.ttf deleted file mode 100644 index f940901f4..000000000 Binary files a/fonts/CMU-Typewriter-Bold.ttf and /dev/null differ diff --git a/fonts/CMU-Typewriter-Italic.ttf b/fonts/CMU-Typewriter-Italic.ttf deleted file mode 100644 index 82c90a568..000000000 Binary files a/fonts/CMU-Typewriter-Italic.ttf and /dev/null differ diff --git a/fonts/CMU-Typewriter-Regular.ttf b/fonts/CMU-Typewriter-Regular.ttf deleted file mode 100644 index 1651877db..000000000 Binary files a/fonts/CMU-Typewriter-Regular.ttf and /dev/null differ diff --git a/fonts/Latin-Modern-Math.otf b/fonts/Latin-Modern-Math.otf deleted file mode 100644 index 0e4642e91..000000000 Binary files a/fonts/Latin-Modern-Math.otf and /dev/null differ diff --git a/fonts/License-CMU.txt b/fonts/License-CMU.txt deleted file mode 100644 index a55bb6b2b..000000000 --- a/fonts/License-CMU.txt +++ /dev/null @@ -1,103 +0,0 @@ -Copyright (C) Authors of original metafont fonts: -Donald Ervin Knuth (cm, concrete fonts) -1995, 1996, 1997 J"org Knappen, 1990, 1992 Norbert Schwarz (ec fonts) -1992-2006 A.Khodulev, O.Lapko, A.Berdnikov, V.Volovich (lh fonts) -1997-2005 Claudio Beccari (cb greek fonts) -2002 FUKUI Rei (tipa fonts) -2003-2005 Han The Thanh (Vietnamese fonts) -1996-2005 Walter Schmidt (cmbright fonts) - -Copyright (C) 2003-2009, Andrey V. Panov (panov@canopus.iacp.dvo.ru), -with Reserved Font Family Name "Computer Modern Unicode fonts". - -This Font Software is licensed under the SIL Open Font License, Version 1.1. -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font creation -efforts of academic and linguistic communities, and to provide a free and -open framework in which fonts may be shared and improved in partnership -with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply -to any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software components as -distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, deleting, -or substituting -- in part or in whole -- any of the components of the -Original Version, by changing formats or by porting the Font Software to a -new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, modify, -redistribute, and sell modified and unmodified copies of the Font -Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, -in Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the corresponding -Copyright Holder. This restriction only applies to the primary font name as -presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created -using the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/License-Noto.txt b/fonts/License-Noto.txt deleted file mode 100644 index c82d72e42..000000000 --- a/fonts/License-Noto.txt +++ /dev/null @@ -1,94 +0,0 @@ -Copyright 2018 The Noto Project Authors (github.com/googlei18n/noto-fonts) - -This Font Software is licensed under the SIL Open Font License, -Version 1.1. - -This license is copied below, and is also available with a FAQ at: -http://scripts.sil.org/OFL - ------------------------------------------------------------ -SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 ------------------------------------------------------------ - -PREAMBLE -The goals of the Open Font License (OFL) are to stimulate worldwide -development of collaborative font projects, to support the font -creation efforts of academic and linguistic communities, and to -provide a free and open framework in which fonts may be shared and -improved in partnership with others. - -The OFL allows the licensed fonts to be used, studied, modified and -redistributed freely as long as they are not sold by themselves. The -fonts, including any derivative works, can be bundled, embedded, -redistributed and/or sold with any software provided that any reserved -names are not used by derivative works. The fonts and derivatives, -however, cannot be released under any other type of license. The -requirement for fonts to remain under this license does not apply to -any document created using the fonts or their derivatives. - -DEFINITIONS -"Font Software" refers to the set of files released by the Copyright -Holder(s) under this license and clearly marked as such. This may -include source files, build scripts and documentation. - -"Reserved Font Name" refers to any names specified as such after the -copyright statement(s). - -"Original Version" refers to the collection of Font Software -components as distributed by the Copyright Holder(s). - -"Modified Version" refers to any derivative made by adding to, -deleting, or substituting -- in part or in whole -- any of the -components of the Original Version, by changing formats or by porting -the Font Software to a new environment. - -"Author" refers to any designer, engineer, programmer, technical -writer or other person who contributed to the Font Software. - -PERMISSION & CONDITIONS -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Font Software, to use, study, copy, merge, embed, -modify, redistribute, and sell modified and unmodified copies of the -Font Software, subject to the following conditions: - -1) Neither the Font Software nor any of its individual components, in -Original or Modified Versions, may be sold by itself. - -2) Original or Modified Versions of the Font Software may be bundled, -redistributed and/or sold with any software, provided that each copy -contains the above copyright notice and this license. These can be -included either as stand-alone text files, human-readable headers or -in the appropriate machine-readable metadata fields within text or -binary files as long as those fields can be easily viewed by the user. - -3) No Modified Version of the Font Software may use the Reserved Font -Name(s) unless explicit written permission is granted by the -corresponding Copyright Holder. This restriction only applies to the -primary font name as presented to the users. - -4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font -Software shall not be used to promote, endorse or advertise any -Modified Version, except to acknowledge the contribution(s) of the -Copyright Holder(s) and the Author(s) or with their explicit written -permission. - -5) The Font Software, modified or unmodified, in part or in whole, -must be distributed entirely under this license, and must not be -distributed under any other license. The requirement for fonts to -remain under this license does not apply to any document created using -the Font Software. - -TERMINATION -This license becomes null and void if any of the above conditions are -not met. - -DISCLAIMER -THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE -COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, -INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL -DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM -OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/fonts/NotoEmoji-Regular.ttf b/fonts/NotoEmoji-Regular.ttf deleted file mode 100644 index 19b7badf4..000000000 Binary files a/fonts/NotoEmoji-Regular.ttf and /dev/null differ diff --git a/fonts/fonts.toml b/fonts/fonts.toml deleted file mode 100644 index b289ff5cf..000000000 --- a/fonts/fonts.toml +++ /dev/null @@ -1,14 +0,0 @@ -"CMU-SansSerif-Regular.ttf" = ["Computer Modern", "Regular", "SansSerif"] -"CMU-SansSerif-Italic.ttf" = ["Computer Modern", "Italic", "SansSerif"] -"CMU-SansSerif-Bold.ttf" = ["Computer Modern", "Bold", "SansSerif"] -"CMU-SansSerif-Bold-Italic.ttf" = ["Computer Modern", "Bold", "Italic", "SansSerif"] -"CMU-Serif-Regular.ttf" = ["Computer Modern", "Regular", "Serif"] -"CMU-Serif-Italic.ttf" = ["Computer Modern", "Italic", "Serif"] -"CMU-Serif-Bold.ttf" = ["Computer Modern", "Bold", "Serif"] -"CMU-Serif-Bold-Italic.ttf" = ["Computer Modern", "Bold", "Italic", "Serif"] -"CMU-Typewriter-Regular.ttf" = ["Computer Modern", "Regular", "Serif", "SansSerif", "Monospace"] -"CMU-Typewriter-Italic.ttf" = ["Computer Modern", "Italic", "Serif", "SansSerif", "Monospace"] -"CMU-Typewriter-Bold.ttf" = ["Computer Modern", "Bold", "Serif", "SansSerif", "Monospace"] -"CMU-Typewriter-Bold-Italic.ttf" = ["Computer Modern", "Bold", "Italic", "Serif", "SansSerif", "Monospace"] -"Latin-Modern-Math.otf" = ["Latin Modern", "Latin Modern Math", "Math", "Regular", "Serif"] -"NotoEmoji-Regular.ttf" = ["Noto", "Noto Emoji", "Regular", "SansSerif", "Serif", "Monospace"] diff --git a/src/bin/main.rs b/src/bin/main.rs index a2b63d6d7..e0bcd16d8 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -36,7 +36,7 @@ fn run() -> Result<(), Box> { .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 0d097a33d..c5bc6ee87 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, + font_remap: HashMap, /// These are the fonts sorted by their *new* ids, that is, the values of `font_remap`. fonts: Vec, @@ -99,13 +99,13 @@ impl<'d, W: Write> ExportProcess<'d, W> { fn subset_fonts( layouts: &'d MultiLayout, font_loader: &SharedFontLoader - ) -> PdfResult<(Vec, HashMap)> + ) -> PdfResult<(Vec, HashMap)> { let mut fonts = Vec::new(); - let mut font_chars: HashMap> = HashMap::new(); - let mut old_to_new: HashMap = HashMap::new(); - let mut new_to_old: HashMap = HashMap::new(); - let mut active_font = 0; + let mut font_chars: HashMap> = HashMap::new(); + let mut old_to_new: HashMap = HashMap::new(); + let mut new_to_old: HashMap = 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 b0d2c21d8..4ab9fdb1f 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(&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, - active_font: (usize, Size), + active_font: (FontIndex, Size), next_pos: Option, - 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 53c3e91e3..be1ed43c9 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 { + pub fn find_used_fonts(&self) -> Vec { 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 e97214294..96704f601 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, } 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::
()?.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::
()?.units_per_em as f32); - let font_unit_to_size = |x| Size::pt(font_unit_ratio * x); + let glyph = font + .read_table::()? + .get(c) + .expect("select_font: font should have char"); - let glyph = font - .read_table::()? - .get(c) - .expect("select_font: font should have char"); + let glyph_width = font + .read_table::()? + .get(glyph) + .expect("select_font: font should have glyph") + .advance_width as f32; - let glyph_width = font - .read_table::()? - .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 db59ca8da..e77fd5288 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 7f7a0411a..d4519867d 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::("h", Some(Horizontal)); std.add_with_metadata::("v", Some(Vertical)); - std.add_with_metadata::("bold", FontClass::Bold); - std.add_with_metadata::("italic", FontClass::Italic); - std.add_with_metadata::("mono", FontClass::Monospace); - - std.add::("font.family"); + std.add_with_metadata::("font.family", None); + std.add_with_metadata::("mono", Some("monospace".to_string())); + std.add::("font.style"); + std.add::("font.weight"); std.add::("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, - class: FontClass, + weight: FontWeight, } - type Meta = FontClass; - parse(args, body, ctx, meta) { - StyleChange { + SetFontWeight { body: parse!(optional: body, ctx), - class: meta, + weight: match args.get_pos::()? { + 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.toggle_class(self.class.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, + style: FontStyle, + } + + parse(args, body, ctx) { + SetFontStyle { + body: parse!(optional: body, ctx), + style: { + let s = args.get_pos::()?; + 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.variant.style = self.style; styled(&self.body, &ctx, style) } } @@ -249,16 +289,22 @@ function! { family: String, } - parse(args, body, ctx) { + type Meta = Option; + + parse(args, body, ctx, meta) { FontFamily { body: parse!(optional: body, ctx), - family: args.get_pos::()?, + family: if let Some(family) = meta { + family + } else { + args.get_pos::()? + }, } } 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 df5e13d18..ffa10d513 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, - /// The fallback classes from which the font needs to match the leftmost - /// possible one. - pub fallback: Vec, + /// 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, diff --git a/tests/layout.rs b/tests/layout.rs index 8a72e84bb..096bc43f9 100644 --- a/tests/layout.rs +++ b/tests/layout.rs @@ -47,7 +47,11 @@ fn main() -> Result<()> { println!("Running {} test{}", len, if len > 1 { "s" } else { "" }); for (name, src) in filtered { - panic::catch_unwind(|| test(&name, &src)).ok(); + panic::catch_unwind(|| { + if let Err(e) = test(&name, &src) { + println!("error: {}", e); + } + }).ok(); } println!(); @@ -65,7 +69,7 @@ fn test(name: &str, src: &str) -> Result<()> { .. PageStyle::default() }); - let provider = FileSystemFontProvider::from_listing("fonts/fonts.toml")?; + let provider = FileSystemFontProvider::from_index("../fonts/index.json")?; let font_paths = provider.paths(); typesetter.add_font_provider(provider); @@ -80,8 +84,8 @@ fn test(name: &str, src: &str) -> Result<()> { for layout in &layouts { for index in layout.find_used_fonts() { fonts.entry(index).or_insert_with(|| { - let provider_index = loader.get_provider_and_index(index).1; - font_paths[provider_index].to_string_lossy() + let p = loader.get_provider_and_index(index.id).1; + &font_paths[p][index.variant] }); } } @@ -94,7 +98,7 @@ fn test(name: &str, src: &str) -> Result<()> { // Write the font mapping into the serialization file. writeln!(file, "{}", fonts.len())?; for (index, path) in fonts.iter() { - writeln!(file, "{} {}", index, path)?; + writeln!(file, "{} {} {}", index.id, index.variant, path)?; } layouts.serialize(&mut file)?; diff --git a/tests/render.py b/tests/render.py index fe7a1de41..1387ed534 100644 --- a/tests/render.py +++ b/tests/render.py @@ -35,10 +35,10 @@ class MultiboxRenderer: self.fonts = {} font_count = int(lines[0]) for i in range(font_count): - parts = lines[i + 1].split(' ', 1) - index = int(parts[0]) - path = parts[1] - self.fonts[index] = os.path.join(BASE, '../fonts', path) + parts = lines[i + 1].split(' ', 2) + index = int(parts[0]), int(parts[1]) + path = parts[2] + self.fonts[index] = os.path.join(BASE, '../../fonts', path) self.content = lines[font_count + 1:] @@ -136,8 +136,8 @@ class BoxRenderer: self.cursor = [x, y] elif cmd == 'f': - index = int(parts[0]) - size = pix(float(parts[1])) + index = int(parts[0]), int(parts[1]) + size = pix(float(parts[2])) self.font = ImageFont.truetype(self.fonts[index], size) elif cmd == 'w':