summaryrefslogtreecommitdiff
path: root/src/exec
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-03-24 17:12:34 +0100
committerLaurenz <laurmaedje@gmail.com>2021-03-24 17:12:34 +0100
commit73615f7e3ce23f2ea656d04ea9f96184f5ebdc0a (patch)
tree7691b792e1e4b33469a72c40fc76854f1de0814e /src/exec
parent6720520ec06dd0718f81049b2b11e81664f7ef62 (diff)
Text shaping 🚀
- Shapes text with rustybuzz - Font fallback with family list - Tofus are shown in the first font Co-Authored-By: Martin <mhaug@live.de>
Diffstat (limited to 'src/exec')
-rw-r--r--src/exec/context.rs56
-rw-r--r--src/exec/state.rs76
2 files changed, 77 insertions, 55 deletions
diff --git a/src/exec/context.rs b/src/exec/context.rs
index 6ba5b25f..b6a67a2e 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -1,7 +1,4 @@
use std::mem;
-use std::rc::Rc;
-
-use fontdock::FontStyle;
use super::{Exec, FontFamily, State};
use crate::diag::{Diag, DiagSet, Pass};
@@ -87,7 +84,7 @@ impl<'a> ExecContext<'a> {
/// Push a word space into the active paragraph.
pub fn push_space(&mut self) {
- let em = self.state.font.font_size();
+ let em = self.state.font.resolve_size();
self.push(SpacingNode {
amount: self.state.par.word_spacing.resolve(em),
softness: 1,
@@ -103,19 +100,19 @@ impl<'a> ExecContext<'a> {
while let Some(c) = scanner.eat_merging_crlf() {
if is_newline(c) {
- self.push(self.make_text_node(mem::take(&mut line)));
+ self.push(TextNode::new(mem::take(&mut line), &self.state));
self.push_linebreak();
} else {
line.push(c);
}
}
- self.push(self.make_text_node(line));
+ self.push(TextNode::new(line, &self.state));
}
/// Apply a forced line break.
pub fn push_linebreak(&mut self) {
- let em = self.state.font.font_size();
+ let em = self.state.font.resolve_size();
self.push_into_stack(SpacingNode {
amount: self.state.par.leading.resolve(em),
softness: 2,
@@ -124,7 +121,7 @@ impl<'a> ExecContext<'a> {
/// Apply a forced paragraph break.
pub fn push_parbreak(&mut self) {
- let em = self.state.font.font_size();
+ let em = self.state.font.resolve_size();
self.push_into_stack(SpacingNode {
amount: self.state.par.spacing.resolve(em),
softness: 1,
@@ -154,36 +151,6 @@ impl<'a> ExecContext<'a> {
result
}
- /// Construct a text node from the given string based on the active text
- /// state.
- pub fn make_text_node(&self, text: String) -> TextNode {
- let mut variant = self.state.font.variant;
-
- if self.state.font.strong {
- variant.weight = variant.weight.thicken(300);
- }
-
- if self.state.font.emph {
- variant.style = match variant.style {
- FontStyle::Normal => FontStyle::Italic,
- FontStyle::Italic => FontStyle::Normal,
- FontStyle::Oblique => FontStyle::Normal,
- }
- }
-
- TextNode {
- text,
- dir: self.state.dirs.cross,
- aligns: self.state.aligns,
- families: Rc::clone(&self.state.font.families),
- variant,
- font_size: self.state.font.font_size(),
- top_edge: self.state.font.top_edge,
- bottom_edge: self.state.font.bottom_edge,
- color: self.state.font.color,
- }
- }
-
/// Finish the active paragraph.
fn finish_par(&mut self) {
let mut par = mem::replace(&mut self.par, ParNode::new(&self.state));
@@ -292,7 +259,7 @@ impl StackNode {
impl ParNode {
fn new(state: &State) -> Self {
- let em = state.font.font_size();
+ let em = state.font.resolve_size();
Self {
dirs: state.dirs,
aligns: state.aligns,
@@ -301,3 +268,14 @@ impl ParNode {
}
}
}
+
+impl TextNode {
+ fn new(text: String, state: &State) -> Self {
+ Self {
+ text,
+ dir: state.dirs.cross,
+ aligns: state.aligns,
+ props: state.font.resolve_props(),
+ }
+ }
+}
diff --git a/src/exec/state.rs b/src/exec/state.rs
index 0322c437..7957f312 100644
--- a/src/exec/state.rs
+++ b/src/exec/state.rs
@@ -4,8 +4,9 @@ use std::rc::Rc;
use fontdock::{FontStretch, FontStyle, FontVariant, FontWeight};
use crate::color::{Color, RgbaColor};
+use crate::font::VerticalFontMetric;
use crate::geom::*;
-use crate::layout::{Fill, VerticalFontMetric};
+use crate::layout::Fill;
use crate::paper::{Paper, PaperClass, PAPER_A4};
/// The evaluation state.
@@ -100,7 +101,7 @@ impl Default for ParState {
#[derive(Debug, Clone, PartialEq)]
pub struct FontState {
/// A list of font families with generic class definitions.
- pub families: Rc<FamilyMap>,
+ pub families: Rc<FamilyList>,
/// The selected font variant.
pub variant: FontVariant,
/// The font size.
@@ -111,32 +112,58 @@ pub struct FontState {
pub top_edge: VerticalFontMetric,
/// The bottom end of the text bounding box.
pub bottom_edge: VerticalFontMetric,
+ /// The glyph fill color / texture.
+ pub color: Fill,
/// Whether the strong toggle is active or inactive. This determines
/// whether the next `*` adds or removes font weight.
pub strong: bool,
/// Whether the emphasis toggle is active or inactive. This determines
/// whether the next `_` makes italic or non-italic.
pub emph: bool,
- /// The glyph fill color / texture.
- pub color: Fill,
}
impl FontState {
- /// Access the `families` mutably.
- pub fn families_mut(&mut self) -> &mut FamilyMap {
- Rc::make_mut(&mut self.families)
+ /// The resolved font size.
+ pub fn resolve_size(&self) -> Length {
+ self.scale.resolve(self.size)
}
- /// The absolute font size.
- pub fn font_size(&self) -> Length {
- self.scale.resolve(self.size)
+ /// Resolve font properties.
+ pub fn resolve_props(&self) -> FontProps {
+ let mut variant = self.variant;
+
+ if self.strong {
+ variant.weight = variant.weight.thicken(300);
+ }
+
+ if self.emph {
+ variant.style = match variant.style {
+ FontStyle::Normal => FontStyle::Italic,
+ FontStyle::Italic => FontStyle::Normal,
+ FontStyle::Oblique => FontStyle::Normal,
+ }
+ }
+
+ FontProps {
+ families: Rc::clone(&self.families),
+ variant,
+ size: self.resolve_size(),
+ top_edge: self.top_edge,
+ bottom_edge: self.bottom_edge,
+ color: self.color,
+ }
+ }
+
+ /// Access the `families` mutably.
+ pub fn families_mut(&mut self) -> &mut FamilyList {
+ Rc::make_mut(&mut self.families)
}
}
impl Default for FontState {
fn default() -> Self {
Self {
- families: Rc::new(FamilyMap::default()),
+ families: Rc::new(FamilyList::default()),
variant: FontVariant {
style: FontStyle::Normal,
weight: FontWeight::REGULAR,
@@ -146,16 +173,33 @@ impl Default for FontState {
top_edge: VerticalFontMetric::CapHeight,
bottom_edge: VerticalFontMetric::Baseline,
scale: Linear::ONE,
+ color: Fill::Color(Color::Rgba(RgbaColor::BLACK)),
strong: false,
emph: false,
- color: Fill::Color(Color::Rgba(RgbaColor::BLACK)),
}
}
}
+/// Properties used for font selection and layout.
+#[derive(Debug, Clone, PartialEq)]
+pub struct FontProps {
+ /// The list of font families to use for shaping.
+ pub families: Rc<FamilyList>,
+ /// Which variant of the font to use.
+ pub variant: FontVariant,
+ /// The font size.
+ pub size: Length,
+ /// What line to consider the top edge of text.
+ pub top_edge: VerticalFontMetric,
+ /// What line to consider the bottom edge of text.
+ pub bottom_edge: VerticalFontMetric,
+ /// The color of the text.
+ pub color: Fill,
+}
+
/// Font family definitions.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
-pub struct FamilyMap {
+pub struct FamilyList {
/// The user-defined list of font families.
pub list: Vec<FontFamily>,
/// Definition of serif font families.
@@ -168,9 +212,9 @@ pub struct FamilyMap {
pub base: Vec<String>,
}
-impl FamilyMap {
+impl FamilyList {
/// Flat iterator over this map's family names.
- pub fn iter(&self) -> impl Iterator<Item = &str> {
+ pub fn iter(&self) -> impl Iterator<Item = &str> + Clone {
self.list
.iter()
.flat_map(move |family: &FontFamily| {
@@ -186,7 +230,7 @@ impl FamilyMap {
}
}
-impl Default for FamilyMap {
+impl Default for FamilyList {
fn default() -> Self {
Self {
list: vec![FontFamily::Serif],