summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-22 14:08:50 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-22 14:08:50 +0100
commit98336bfafb947f0b4d55a79c422b915bb417c185 (patch)
treebf0ead89a3d7eed3d8bb87d013c2a813633c75e4 /src
parent39f55481ed7bc5ebc6d310924e90e7b6c0760d3b (diff)
Better font family definitions ✒
Diffstat (limited to 'src')
-rw-r--r--src/env.rs10
-rw-r--r--src/exec/context.rs5
-rw-r--r--src/exec/state.rs81
-rw-r--r--src/layout/shaping.rs57
-rw-r--r--src/layout/text.rs7
-rw-r--r--src/library/font.rs72
-rw-r--r--src/library/mod.rs2
-rw-r--r--src/main.rs2
8 files changed, 132 insertions, 104 deletions
diff --git a/src/env.rs b/src/env.rs
index 3db71e08..75e2853a 100644
--- a/src/env.rs
+++ b/src/env.rs
@@ -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};