diff options
| author | Laurenz <laurmaedje@gmail.com> | 2021-03-22 14:08:50 +0100 |
|---|---|---|
| committer | Laurenz <laurmaedje@gmail.com> | 2021-03-22 14:08:50 +0100 |
| commit | 98336bfafb947f0b4d55a79c422b915bb417c185 (patch) | |
| tree | bf0ead89a3d7eed3d8bb87d013c2a813633c75e4 /src | |
| parent | 39f55481ed7bc5ebc6d310924e90e7b6c0760d3b (diff) | |
Better font family definitions ✒
Diffstat (limited to 'src')
| -rw-r--r-- | src/env.rs | 10 | ||||
| -rw-r--r-- | src/exec/context.rs | 5 | ||||
| -rw-r--r-- | src/exec/state.rs | 81 | ||||
| -rw-r--r-- | src/layout/shaping.rs | 57 | ||||
| -rw-r--r-- | src/layout/text.rs | 7 | ||||
| -rw-r--r-- | src/library/font.rs | 72 | ||||
| -rw-r--r-- | src/library/mod.rs | 2 | ||||
| -rw-r--r-- | src/main.rs | 2 |
8 files changed, 132 insertions, 104 deletions
@@ -7,13 +7,13 @@ use std::fs; use std::io::Cursor; use std::path::{Path, PathBuf}; -use fontdock::{ContainsChar, FaceFromVec, FaceId, FontSource}; +use fontdock::{FaceFromVec, FaceId, FontSource}; use image::io::Reader as ImageReader; use image::{DynamicImage, GenericImageView, ImageFormat}; use ttf_parser::Face; #[cfg(feature = "fs")] -use fontdock::fs::{FsIndex, FsSource}; +use fontdock::{FsIndex, FsSource}; /// Encapsulates all environment dependencies (fonts, resources). #[derive(Debug)] @@ -83,12 +83,6 @@ impl FaceFromVec for FaceBuf { } } -impl ContainsChar for FaceBuf { - fn contains_char(&self, c: char) -> bool { - self.get().glyph_index(c).is_some() - } -} - /// Simplify font loader construction from an [`FsIndex`]. #[cfg(feature = "fs")] pub trait FsIndexExt { diff --git a/src/exec/context.rs b/src/exec/context.rs index f19f6561..6ba5b25f 100644 --- a/src/exec/context.rs +++ b/src/exec/context.rs @@ -3,7 +3,7 @@ use std::rc::Rc; use fontdock::FontStyle; -use super::{Exec, State}; +use super::{Exec, FontFamily, State}; use crate::diag::{Diag, DiagSet, Pass}; use crate::env::Env; use crate::eval::TemplateValue; @@ -74,8 +74,7 @@ impl<'a> ExecContext<'a> { /// Set the font to monospace. pub fn set_monospace(&mut self) { let families = self.state.font.families_mut(); - families.list.insert(0, "monospace".to_string()); - families.flatten(); + families.list.insert(0, FontFamily::Monospace); } /// Push a layout node into the active paragraph. diff --git a/src/exec/state.rs b/src/exec/state.rs index f66694fd..0322c437 100644 --- a/src/exec/state.rs +++ b/src/exec/state.rs @@ -1,6 +1,7 @@ +use std::fmt::{self, Display, Formatter}; use std::rc::Rc; -use fontdock::{fallback, FallbackTree, FontStretch, FontStyle, FontVariant, FontWeight}; +use fontdock::{FontStretch, FontStyle, FontVariant, FontWeight}; use crate::color::{Color, RgbaColor}; use crate::geom::*; @@ -98,8 +99,8 @@ impl Default for ParState { /// Defines font properties. #[derive(Debug, Clone, PartialEq)] pub struct FontState { - /// A tree of font family names and generic class names. - pub families: Rc<FallbackTree>, + /// A list of font families with generic class definitions. + pub families: Rc<FamilyMap>, /// The selected font variant. pub variant: FontVariant, /// The font size. @@ -122,7 +123,7 @@ pub struct FontState { impl FontState { /// Access the `families` mutably. - pub fn families_mut(&mut self) -> &mut FallbackTree { + pub fn families_mut(&mut self) -> &mut FamilyMap { Rc::make_mut(&mut self.families) } @@ -135,12 +136,7 @@ impl FontState { impl Default for FontState { fn default() -> Self { Self { - // The default tree of font fallbacks. - families: Rc::new(fallback! { - list: [], - classes: { "monospace" => ["inconsolata"] }, - base: ["eb garamond", "twitter color emoji"], - }), + families: Rc::new(FamilyMap::default()), variant: FontVariant { style: FontStyle::Normal, weight: FontWeight::REGULAR, @@ -156,3 +152,68 @@ impl Default for FontState { } } } + +/// Font family definitions. +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub struct FamilyMap { + /// The user-defined list of font families. + pub list: Vec<FontFamily>, + /// Definition of serif font families. + pub serif: Vec<String>, + /// Definition of sans-serif font families. + pub sans_serif: Vec<String>, + /// Definition of monospace font families used for raw text. + pub monospace: Vec<String>, + /// Base fonts that are tried if the list has no match. + pub base: Vec<String>, +} + +impl FamilyMap { + /// Flat iterator over this map's family names. + pub fn iter(&self) -> impl Iterator<Item = &str> { + self.list + .iter() + .flat_map(move |family: &FontFamily| { + match family { + FontFamily::Named(name) => std::slice::from_ref(name), + FontFamily::Serif => &self.serif, + FontFamily::SansSerif => &self.sans_serif, + FontFamily::Monospace => &self.monospace, + } + }) + .chain(&self.base) + .map(String::as_str) + } +} + +impl Default for FamilyMap { + fn default() -> Self { + Self { + list: vec![FontFamily::Serif], + serif: vec!["eb garamond".into()], + sans_serif: vec![/* TODO */], + monospace: vec!["inconsolata".into()], + base: vec!["twitter color emoji".into()], + } + } +} + +/// A generic or named font family. +#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] +pub enum FontFamily { + Serif, + SansSerif, + Monospace, + Named(String), +} + +impl Display for FontFamily { + fn fmt(&self, f: &mut Formatter) -> fmt::Result { + f.pad(match self { + Self::Serif => "serif", + Self::SansSerif => "sans-serif", + Self::Monospace => "monospace", + Self::Named(s) => s, + }) + } +} diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs index df27e287..fd10b41e 100644 --- a/src/layout/shaping.rs +++ b/src/layout/shaping.rs @@ -6,10 +6,11 @@ use std::fmt::{self, Debug, Display, Formatter}; -use fontdock::{FaceId, FaceQuery, FallbackTree, FontVariant}; +use fontdock::{FaceId, FontVariant}; use ttf_parser::{Face, GlyphId}; use crate::env::FontLoader; +use crate::exec::FamilyMap; use crate::geom::{Dir, Length, Point, Size}; use crate::layout::{Element, Fill, Frame}; @@ -40,7 +41,7 @@ impl Shaped { glyphs: vec![], offsets: vec![], font_size, - color: color, + color, } } @@ -98,7 +99,7 @@ impl Display for VerticalFontMetric { pub fn shape( text: &str, dir: Dir, - fallback: &FallbackTree, + families: &FamilyMap, variant: FontVariant, font_size: Length, top_edge: VerticalFontMetric, @@ -122,31 +123,33 @@ pub fn shape( }; for c in chars { - let query = FaceQuery { fallback: fallback.iter(), variant, c }; - if let Some(id) = loader.query(query) { - let face = loader.face(id).get(); - let (glyph, glyph_width) = match lookup_glyph(face, c) { - Some(v) => v, - None => continue, - }; - - let units_per_em = f64::from(face.units_per_em().unwrap_or(1000)); - let convert = |units| units / units_per_em * font_size; - - // Flush the buffer and reset the metrics if we use a new font face. - if shaped.face != id { - place(&mut frame, shaped, width, top, bottom); - - shaped = Shaped::new(id, font_size, color); - width = Length::ZERO; - top = convert(f64::from(lookup_metric(face, top_edge))); - bottom = convert(f64::from(lookup_metric(face, bottom_edge))); + for family in families.iter() { + if let Some(id) = loader.query(family, variant) { + let face = loader.face(id).get(); + let (glyph, glyph_width) = match lookup_glyph(face, c) { + Some(v) => v, + None => continue, + }; + + let units_per_em = f64::from(face.units_per_em().unwrap_or(1000)); + let convert = |units| units / units_per_em * font_size; + + // Flush the buffer and reset the metrics if we use a new font face. + if shaped.face != id { + place(&mut frame, shaped, width, top, bottom); + + shaped = Shaped::new(id, font_size, color); + width = Length::ZERO; + top = convert(f64::from(lookup_metric(face, top_edge))); + bottom = convert(f64::from(lookup_metric(face, bottom_edge))); + } + + shaped.text.push(c); + shaped.glyphs.push(glyph); + shaped.offsets.push(width); + width += convert(f64::from(glyph_width)); + break; } - - shaped.text.push(c); - shaped.glyphs.push(glyph); - shaped.offsets.push(width); - width += convert(f64::from(glyph_width)); } } diff --git a/src/layout/text.rs b/src/layout/text.rs index 7f8f97cc..2239afac 100644 --- a/src/layout/text.rs +++ b/src/layout/text.rs @@ -1,9 +1,10 @@ use std::fmt::{self, Debug, Formatter}; use std::rc::Rc; -use fontdock::{FallbackTree, FontVariant}; +use fontdock::FontVariant; use super::*; +use crate::exec::FamilyMap; /// A consecutive, styled run of text. #[derive(Clone, PartialEq)] @@ -14,8 +15,8 @@ pub struct TextNode { pub dir: Dir, /// How to align this text node in its parent. pub aligns: LayoutAligns, - /// The families used for font fallback. - pub families: Rc<FallbackTree>, + /// The list of font families for shaping. + pub families: Rc<FamilyMap>, /// The font variant, pub variant: FontVariant, /// The font size. diff --git a/src/library/font.rs b/src/library/font.rs index ed2c0ef3..6afc617b 100644 --- a/src/library/font.rs +++ b/src/library/font.rs @@ -17,9 +17,9 @@ use super::*; /// - Top edge of the font: `top-edge`, of type `vertical-font-metric`. /// - Bottom edge of the font: `bottom-edge`, of type `vertical-font-metric`. /// - Color the glyphs: `color`, of type `color`. -/// - Serif family definition: `serif`, of type `font-familiy-list`. -/// - Sans-serif family definition: `sans-serif`, of type `font-familiy-list`. -/// - Monospace family definition: `monospace`, of type `font-familiy-list`. +/// - Serif family definition: `serif`, of type `font-family-definition`. +/// - Sans-serif family definition: `sans-serif`, of type `font-family-definition`. +/// - Monospace family definition: `monospace`, of type `font-family-definition`. /// /// # Return value /// A template that configures font properties. The effect is scoped to the body @@ -31,10 +31,9 @@ use super::*; /// - `sans-serif` /// - `monospace` /// - coerces from `string` -/// - Type `font-family-list` +/// - Type `font-family-definition` /// - coerces from `string` /// - coerces from `array` -/// - coerces from `font-family` /// - Type `font-style` /// - `normal` /// - `italic` @@ -58,7 +57,7 @@ use super::*; /// - `descender` pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { let size = args.find::<Linear>(ctx); - let list: Vec<_> = args.filter::<FontFamily>(ctx).map(|f| f.to_string()).collect(); + let list: Vec<_> = args.filter::<FontFamily>(ctx).collect(); let style = args.get(ctx, "style"); let weight = args.get(ctx, "weight"); let stretch = args.get(ctx, "stretch"); @@ -83,9 +82,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { } if !list.is_empty() { - let families = ctx.state.font.families_mut(); - families.list = list.clone(); - families.flatten(); + ctx.state.font.families_mut().list = list.clone(); } if let Some(style) = style { @@ -112,17 +109,16 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { ctx.state.font.color = Fill::Color(color); } - for (variant, arg) in &[ - (FontFamily::Serif, &serif), - (FontFamily::SansSerif, &sans_serif), - (FontFamily::Monospace, &monospace), - ] { - if let Some(FontFamilies(list)) = arg { - let strings = list.into_iter().map(|f| f.to_string()).collect(); - let families = ctx.state.font.families_mut(); - families.update_class_list(variant.to_string(), strings); - families.flatten(); - } + if let Some(FontFamilies(serif)) = &serif { + ctx.state.font.families_mut().serif = serif.clone(); + } + + if let Some(FontFamilies(sans_serif)) = &sans_serif { + ctx.state.font.families_mut().sans_serif = sans_serif.clone(); + } + + if let Some(FontFamilies(monospace)) = &monospace { + ctx.state.font.families_mut().monospace = monospace.clone(); } if let Some(body) = &body { @@ -132,45 +128,19 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value { }) } -/// A list of font families. +/// A list of font family names. #[derive(Debug, Clone, PartialEq)] -struct FontFamilies(Vec<FontFamily>); - -/// A single font family. -#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)] -pub(super) enum FontFamily { - Serif, - SansSerif, - Monospace, - Named(String), -} - -impl FontFamily { - pub fn as_str(&self) -> &str { - match self { - Self::Serif => "serif", - Self::SansSerif => "sans-serif", - Self::Monospace => "monospace", - Self::Named(s) => s, - } - } -} - -impl Display for FontFamily { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.pad(self.as_str()) - } -} +struct FontFamilies(Vec<String>); typify! { - FontFamilies: "font family or array of font families", - Value::Str(string) => Self(vec![FontFamily::Named(string.to_lowercase())]), + FontFamilies: "string or array of strings", + Value::Str(string) => Self(vec![string.to_lowercase()]), Value::Array(values) => Self(values .into_iter() .filter_map(|v| v.cast().ok()) + .map(|string: String| string.to_lowercase()) .collect() ), - #(family: FontFamily) => Self(vec![family]), } typify! { diff --git a/src/library/mod.rs b/src/library/mod.rs index 62640ef4..58e62d56 100644 --- a/src/library/mod.rs +++ b/src/library/mod.rs @@ -31,7 +31,7 @@ use fontdock::{FontStyle, FontWeight}; use crate::eval::{AnyValue, FuncValue, Scope}; use crate::eval::{EvalContext, FuncArgs, TemplateValue, Value}; -use crate::exec::{Exec, ExecContext}; +use crate::exec::{Exec, ExecContext, FontFamily}; use crate::geom::*; use crate::layout::VerticalFontMetric; use crate::syntax::{Node, Spanned}; diff --git a/src/main.rs b/src/main.rs index 74ec743d..9d72440e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,7 +2,7 @@ use std::fs; use std::path::{Path, PathBuf}; use anyhow::{anyhow, bail, Context}; -use fontdock::fs::FsIndex; +use fontdock::FsIndex; use typst::diag::Pass; use typst::env::{Env, FsIndexExt, ResourceLoader}; |
