summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2021-07-25 01:16:38 +0200
committerLaurenz <laurmaedje@gmail.com>2021-07-25 01:16:38 +0200
commitec5384c97f24c3e6d8284926fd3e415f47fe2b04 (patch)
treeae95815628fd9ae9f765453a6a4b8729b26943ab /src
parentdcfbf95220300a7866b6d03953fb4d29511cd6fa (diff)
State-based monospace handling
Diffstat (limited to 'src')
-rw-r--r--src/exec/context.rs19
-rw-r--r--src/exec/mod.rs17
-rw-r--r--src/exec/state.rs48
-rw-r--r--src/layout/shaping.rs41
4 files changed, 72 insertions, 53 deletions
diff --git a/src/exec/context.rs b/src/exec/context.rs
index 925fd7de..13e53b09 100644
--- a/src/exec/context.rs
+++ b/src/exec/context.rs
@@ -1,7 +1,7 @@
use std::mem;
use std::rc::Rc;
-use super::{Exec, ExecWithMap, FontFamily, State};
+use super::{Exec, ExecWithMap, State};
use crate::diag::{Diag, DiagSet, Pass};
use crate::eco::EcoString;
use crate::eval::{ExprMap, Template};
@@ -44,15 +44,6 @@ impl ExecContext {
self.diags.insert(diag);
}
- /// Set the font to monospace.
- pub fn set_monospace(&mut self) {
- self.state
- .font_mut()
- .families_mut()
- .list
- .insert(0, FontFamily::Monospace);
- }
-
/// Execute a template and return the result as a stack node.
pub fn exec_template_stack(&mut self, template: &Template) -> StackNode {
self.exec_stack(|ctx| template.exec(ctx))
@@ -83,6 +74,14 @@ impl ExecContext {
self.stack.par.push(self.make_text_node(text));
}
+ /// Push text, but in monospace.
+ pub fn push_monospace_text(&mut self, text: impl Into<EcoString>) {
+ let prev = Rc::clone(&self.state.font);
+ self.state.font_mut().monospace = true;
+ self.push_text(text);
+ self.state.font = prev;
+ }
+
/// Push a word space into the active paragraph.
pub fn push_word_space(&mut self) {
self.stack.par.push_soft(self.make_text_node(" "));
diff --git a/src/exec/mod.rs b/src/exec/mod.rs
index d61e0793..76857b44 100644
--- a/src/exec/mod.rs
+++ b/src/exec/mod.rs
@@ -7,7 +7,6 @@ pub use context::*;
pub use state::*;
use std::fmt::Write;
-use std::rc::Rc;
use crate::diag::Pass;
use crate::eco::EcoString;
@@ -75,10 +74,7 @@ impl Exec for RawNode {
ctx.parbreak();
}
- let snapshot = Rc::clone(&ctx.state.font);
- ctx.set_monospace();
- ctx.push_text(&self.text);
- ctx.state.font = snapshot;
+ ctx.push_monospace_text(&self.text);
if self.block {
ctx.parbreak();
@@ -143,14 +139,9 @@ impl Exec for Value {
Value::Str(v) => ctx.push_text(v),
Value::Template(v) => v.exec(ctx),
Value::Error => {}
- other => {
- // For values which can't be shown "naturally", we print
- // the representation in monospace.
- let prev = Rc::clone(&ctx.state.font.families);
- ctx.set_monospace();
- ctx.push_text(pretty(other));
- ctx.state.font_mut().families = prev;
- }
+ // For values which can't be shown "naturally", we print the
+ // representation in monospace.
+ other => ctx.push_monospace_text(pretty(other)),
}
}
}
diff --git a/src/exec/state.rs b/src/exec/state.rs
index f99a8901..624394ba 100644
--- a/src/exec/state.rs
+++ b/src/exec/state.rs
@@ -111,6 +111,14 @@ pub struct FontState {
pub families: Rc<FamilyList>,
/// The selected font variant.
pub variant: FontVariant,
+ /// 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,
+ /// Whether the monospace toggle is active or inactive.
+ pub monospace: bool,
/// The font size.
pub size: Length,
/// The top end of the text bounding box.
@@ -119,12 +127,6 @@ pub struct FontState {
pub bottom_edge: VerticalFontMetric,
/// Glyph color.
pub fill: Paint,
- /// 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 specifications for a strikethrough line, if any.
pub strikethrough: Option<Rc<LineState>>,
/// The specifications for a underline, if any.
@@ -138,6 +140,35 @@ impl FontState {
pub fn families_mut(&mut self) -> &mut FamilyList {
Rc::make_mut(&mut self.families)
}
+
+ /// The canonical family iterator.
+ pub fn families(&self) -> impl Iterator<Item = &str> + Clone {
+ let head = if self.monospace {
+ self.families.monospace.as_slice()
+ } else {
+ &[]
+ };
+ head.iter().map(String::as_str).chain(self.families.iter())
+ }
+
+ /// The canonical variant with `strong` and `emph` factored in.
+ pub fn variant(&self) -> FontVariant {
+ 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,
+ }
+ }
+
+ variant
+ }
}
impl Default for FontState {
@@ -149,12 +180,13 @@ impl Default for FontState {
weight: FontWeight::REGULAR,
stretch: FontStretch::NORMAL,
},
+ strong: false,
+ emph: false,
+ monospace: false,
size: Length::pt(11.0),
top_edge: VerticalFontMetric::CapHeight,
bottom_edge: VerticalFontMetric::Baseline,
fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)),
- strong: false,
- emph: false,
strikethrough: None,
underline: None,
overline: None,
diff --git a/src/layout/shaping.rs b/src/layout/shaping.rs
index 5e1bc327..3ede5122 100644
--- a/src/layout/shaping.rs
+++ b/src/layout/shaping.rs
@@ -6,7 +6,7 @@ use rustybuzz::UnicodeBuffer;
use super::{Element, Frame, Glyph, LayoutContext, Text};
use crate::exec::{FontState, LineState};
-use crate::font::{Face, FaceId, FontStyle, LineMetrics};
+use crate::font::{Face, FaceId, FontVariant, LineMetrics};
use crate::geom::{Dir, Length, Point, Size};
use crate::layout::Geometry;
use crate::util::SliceExt;
@@ -188,9 +188,18 @@ pub fn shape<'a>(
state: &'a FontState,
) -> ShapedText<'a> {
let mut glyphs = vec![];
- let families = state.families.iter();
if !text.is_empty() {
- shape_segment(ctx, &mut glyphs, 0, text, dir, state, families, None);
+ shape_segment(
+ ctx,
+ &mut glyphs,
+ 0,
+ text,
+ dir,
+ state.size,
+ state.variant(),
+ state.families(),
+ None,
+ );
}
let (size, baseline) = measure(ctx, &glyphs, state);
@@ -212,7 +221,8 @@ fn shape_segment<'a>(
base: usize,
text: &str,
dir: Dir,
- state: &FontState,
+ size: Length,
+ variant: FontVariant,
mut families: impl Iterator<Item = &'a str> + Clone,
mut first_face: Option<FaceId>,
) {
@@ -221,20 +231,6 @@ fn shape_segment<'a>(
// Try to load the next available font family.
match families.next() {
Some(family) => {
- let mut variant = state.variant;
-
- if state.strong {
- variant.weight = variant.weight.thicken(300);
- }
-
- if state.emph {
- variant.style = match variant.style {
- FontStyle::Normal => FontStyle::Italic,
- FontStyle::Italic => FontStyle::Normal,
- FontStyle::Oblique => FontStyle::Normal,
- }
- }
-
if let Some(id) = ctx.fonts.select(family, variant) {
break (id, true);
}
@@ -280,8 +276,8 @@ fn shape_segment<'a>(
glyphs.push(ShapedGlyph {
face_id,
glyph_id: info.glyph_id as u16,
- x_advance: face.to_em(pos[i].x_advance).to_length(state.size),
- x_offset: face.to_em(pos[i].x_offset).to_length(state.size),
+ x_advance: face.to_em(pos[i].x_advance).to_length(size),
+ x_offset: face.to_em(pos[i].x_offset).to_length(size),
text_index: base + cluster,
safe_to_break: !info.unsafe_to_break(),
});
@@ -332,7 +328,8 @@ fn shape_segment<'a>(
base + range.start,
&text[range],
dir,
- state,
+ size,
+ variant,
families.clone(),
first_face,
);
@@ -362,7 +359,7 @@ fn measure(
if glyphs.is_empty() {
// When there are no glyphs, we just use the vertical metrics of the
// first available font.
- for family in state.families.iter() {
+ for family in state.families() {
if let Some(face_id) = ctx.fonts.select(family, state.variant) {
expand_vertical(ctx.fonts.get(face_id));
break;