summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2020-01-04 22:43:26 +0100
committerLaurenz <laurmaedje@gmail.com>2020-01-04 22:43:26 +0100
commit7b84f3b553de672e5374e142467f63b10009aeca (patch)
treefd8265abc8c62213520628babc0cfc3a6f8e98aa /src
parent5dfaffc5bdfa811c135f0140c0a0ba917eb8c70f (diff)
Adopt new font loading engine ⚙
Diffstat (limited to 'src')
-rw-r--r--src/bin/main.rs2
-rw-r--r--src/export/pdf.rs14
-rw-r--r--src/layout/actions.rs13
-rw-r--r--src/layout/mod.rs4
-rw-r--r--src/layout/text.rs55
-rw-r--r--src/layout/tree.rs7
-rw-r--r--src/library/mod.rs80
-rw-r--r--src/style.rs71
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,