mirror of
https://github.com/typst/typst
synced 2025-05-14 17:15:28 +08:00
Remove props in favor of using state for everything
This commit is contained in:
parent
c28708aa19
commit
3330767c20
@ -1,4 +1,5 @@
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use super::{Exec, ExecWithMap, FontFamily, State};
|
use super::{Exec, ExecWithMap, FontFamily, State};
|
||||||
use crate::diag::{Diag, DiagSet, Pass};
|
use crate::diag::{Diag, DiagSet, Pass};
|
||||||
@ -43,8 +44,11 @@ impl ExecContext {
|
|||||||
|
|
||||||
/// Set the font to monospace.
|
/// Set the font to monospace.
|
||||||
pub fn set_monospace(&mut self) {
|
pub fn set_monospace(&mut self) {
|
||||||
let families = self.state.font.families_mut();
|
self.state
|
||||||
families.list.insert(0, FontFamily::Monospace);
|
.font_mut()
|
||||||
|
.families_mut()
|
||||||
|
.list
|
||||||
|
.insert(0, FontFamily::Monospace);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a template and return the result as a stack node.
|
/// Execute a template and return the result as a stack node.
|
||||||
@ -108,8 +112,7 @@ impl ExecContext {
|
|||||||
|
|
||||||
/// Apply a forced paragraph break.
|
/// Apply a forced paragraph break.
|
||||||
pub fn parbreak(&mut self) {
|
pub fn parbreak(&mut self) {
|
||||||
let em = self.state.font.resolve_size();
|
let amount = self.state.par.spacing.resolve(self.state.font.size);
|
||||||
let amount = self.state.par.spacing.resolve(em);
|
|
||||||
self.stack.parbreak(&self.state);
|
self.stack.parbreak(&self.state);
|
||||||
self.stack.push_soft(StackChild::Spacing(amount));
|
self.stack.push_soft(StackChild::Spacing(amount));
|
||||||
}
|
}
|
||||||
@ -133,9 +136,11 @@ impl ExecContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_text_node(&self, text: impl Into<String>) -> ParChild {
|
fn make_text_node(&self, text: impl Into<String>) -> ParChild {
|
||||||
let align = self.state.aligns.cross;
|
ParChild::Text(
|
||||||
let props = self.state.font.resolve_props();
|
text.into(),
|
||||||
ParChild::Text(text.into(), props, align)
|
self.state.aligns.cross,
|
||||||
|
Rc::clone(&self.state.font),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,11 +222,10 @@ struct ParBuilder {
|
|||||||
|
|
||||||
impl ParBuilder {
|
impl ParBuilder {
|
||||||
fn new(state: &State) -> Self {
|
fn new(state: &State) -> Self {
|
||||||
let em = state.font.resolve_size();
|
|
||||||
Self {
|
Self {
|
||||||
aligns: state.aligns,
|
aligns: state.aligns,
|
||||||
dir: state.lang.dir,
|
dir: state.lang.dir,
|
||||||
line_spacing: state.par.leading.resolve(em),
|
line_spacing: state.par.leading.resolve(state.font.size),
|
||||||
children: vec![],
|
children: vec![],
|
||||||
last: Last::None,
|
last: Last::None,
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,8 @@ impl ExecWithMap for syntax::Node {
|
|||||||
Self::Space => ctx.push_word_space(),
|
Self::Space => ctx.push_word_space(),
|
||||||
Self::Linebreak(_) => ctx.linebreak(),
|
Self::Linebreak(_) => ctx.linebreak(),
|
||||||
Self::Parbreak(_) => ctx.parbreak(),
|
Self::Parbreak(_) => ctx.parbreak(),
|
||||||
Self::Strong(_) => ctx.state.font.strong ^= true,
|
Self::Strong(_) => ctx.state.font_mut().strong ^= true,
|
||||||
Self::Emph(_) => ctx.state.font.emph ^= true,
|
Self::Emph(_) => ctx.state.font_mut().emph ^= true,
|
||||||
Self::Raw(raw) => raw.exec(ctx),
|
Self::Raw(raw) => raw.exec(ctx),
|
||||||
Self::Heading(heading) => heading.exec_with_map(ctx, map),
|
Self::Heading(heading) => heading.exec_with_map(ctx, map),
|
||||||
Self::List(list) => list.exec_with_map(ctx, map),
|
Self::List(list) => list.exec_with_map(ctx, map),
|
||||||
@ -85,10 +85,11 @@ impl Exec for syntax::RawNode {
|
|||||||
impl ExecWithMap for syntax::HeadingNode {
|
impl ExecWithMap for syntax::HeadingNode {
|
||||||
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
fn exec_with_map(&self, ctx: &mut ExecContext, map: &ExprMap) {
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
|
let font = ctx.state.font_mut();
|
||||||
|
|
||||||
let upscale = 1.6 - 0.1 * self.level as f64;
|
let upscale = 1.6 - 0.1 * self.level as f64;
|
||||||
ctx.state.font.scale *= upscale;
|
font.size *= upscale;
|
||||||
ctx.state.font.strong = true;
|
font.strong = true;
|
||||||
|
|
||||||
self.body.exec_with_map(ctx, map);
|
self.body.exec_with_map(ctx, map);
|
||||||
|
|
||||||
@ -109,7 +110,7 @@ impl ExecWithMap for syntax::ListNode {
|
|||||||
aspect: None,
|
aspect: None,
|
||||||
children: vec![
|
children: vec![
|
||||||
StackChild::Any(bullet.into(), Gen::default()),
|
StackChild::Any(bullet.into(), Gen::default()),
|
||||||
StackChild::Spacing(ctx.state.font.resolve_size() / 2.0),
|
StackChild::Spacing(ctx.state.font.size / 2.0),
|
||||||
StackChild::Any(body.into(), Gen::default()),
|
StackChild::Any(body.into(), Gen::default()),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -139,7 +140,7 @@ impl Exec for Value {
|
|||||||
let prev = Rc::clone(&ctx.state.font.families);
|
let prev = Rc::clone(&ctx.state.font.families);
|
||||||
ctx.set_monospace();
|
ctx.set_monospace();
|
||||||
ctx.push_text(pretty(other));
|
ctx.push_text(pretty(other));
|
||||||
ctx.state.font.families = prev;
|
ctx.state.font_mut().families = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use crate::layout::Fill;
|
|||||||
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
use crate::paper::{Paper, PaperClass, PAPER_A4};
|
||||||
|
|
||||||
/// The execution state.
|
/// The execution state.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Default, Debug, Clone, PartialEq, Hash)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
/// The current language-related settings.
|
/// The current language-related settings.
|
||||||
pub lang: LangState,
|
pub lang: LangState,
|
||||||
@ -17,25 +17,20 @@ pub struct State {
|
|||||||
/// The current paragraph settings.
|
/// The current paragraph settings.
|
||||||
pub par: ParState,
|
pub par: ParState,
|
||||||
/// The current font settings.
|
/// The current font settings.
|
||||||
pub font: FontState,
|
pub font: Rc<FontState>,
|
||||||
/// The current alignments of layouts in their parents.
|
/// The current alignments of layouts in their parents.
|
||||||
pub aligns: Gen<Align>,
|
pub aligns: Gen<Align>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for State {
|
impl State {
|
||||||
fn default() -> Self {
|
/// Access the `font` state mutably.
|
||||||
Self {
|
pub fn font_mut(&mut self) -> &mut FontState {
|
||||||
lang: LangState::default(),
|
Rc::make_mut(&mut self.font)
|
||||||
page: PageState::default(),
|
|
||||||
par: ParState::default(),
|
|
||||||
font: FontState::default(),
|
|
||||||
aligns: Gen::splat(Align::Start),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Defines language properties.
|
/// Defines language properties.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||||
pub struct LangState {
|
pub struct LangState {
|
||||||
/// The direction for text and other inline objects.
|
/// The direction for text and other inline objects.
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
@ -48,7 +43,7 @@ impl Default for LangState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Defines page properties.
|
/// Defines page properties.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||||
pub struct PageState {
|
pub struct PageState {
|
||||||
/// The class of this page.
|
/// The class of this page.
|
||||||
pub class: PaperClass,
|
pub class: PaperClass,
|
||||||
@ -88,7 +83,7 @@ impl Default for PageState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Defines paragraph properties.
|
/// Defines paragraph properties.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||||
pub struct ParState {
|
pub struct ParState {
|
||||||
/// The spacing between paragraphs (dependent on scaled font size).
|
/// The spacing between paragraphs (dependent on scaled font size).
|
||||||
pub spacing: Linear,
|
pub spacing: Linear,
|
||||||
@ -110,7 +105,7 @@ impl Default for ParState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Defines font properties.
|
/// Defines font properties.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||||
pub struct FontState {
|
pub struct FontState {
|
||||||
/// A list of font families with generic class definitions.
|
/// A list of font families with generic class definitions.
|
||||||
pub families: Rc<FamilyList>,
|
pub families: Rc<FamilyList>,
|
||||||
@ -118,8 +113,6 @@ pub struct FontState {
|
|||||||
pub variant: FontVariant,
|
pub variant: FontVariant,
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub size: Length,
|
pub size: Length,
|
||||||
/// The linear to apply on the base font size.
|
|
||||||
pub scale: Linear,
|
|
||||||
/// The top end of the text bounding box.
|
/// The top end of the text bounding box.
|
||||||
pub top_edge: VerticalFontMetric,
|
pub top_edge: VerticalFontMetric,
|
||||||
/// The bottom end of the text bounding box.
|
/// The bottom end of the text bounding box.
|
||||||
@ -133,49 +126,14 @@ pub struct FontState {
|
|||||||
/// whether the next `_` makes italic or non-italic.
|
/// whether the next `_` makes italic or non-italic.
|
||||||
pub emph: bool,
|
pub emph: bool,
|
||||||
/// The specifications for a strikethrough line, if any.
|
/// The specifications for a strikethrough line, if any.
|
||||||
pub strikethrough: Option<LineState>,
|
pub strikethrough: Option<Rc<LineState>>,
|
||||||
/// The specifications for a underline, if any.
|
/// The specifications for a underline, if any.
|
||||||
pub underline: Option<LineState>,
|
pub underline: Option<Rc<LineState>>,
|
||||||
/// The specifications for a overline line, if any.
|
/// The specifications for a overline line, if any.
|
||||||
pub overline: Option<LineState>,
|
pub overline: Option<Rc<LineState>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FontState {
|
impl FontState {
|
||||||
/// The resolved font size.
|
|
||||||
pub fn resolve_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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let size = self.resolve_size();
|
|
||||||
FontProps {
|
|
||||||
families: Rc::clone(&self.families),
|
|
||||||
variant,
|
|
||||||
size,
|
|
||||||
top_edge: self.top_edge,
|
|
||||||
bottom_edge: self.bottom_edge,
|
|
||||||
strikethrough: self.strikethrough.map(|s| s.resolve_props(size, &self.fill)),
|
|
||||||
underline: self.underline.map(|s| s.resolve_props(size, &self.fill)),
|
|
||||||
overline: self.overline.map(|s| s.resolve_props(size, &self.fill)),
|
|
||||||
fill: self.fill,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Access the `families` mutably.
|
/// Access the `families` mutably.
|
||||||
pub fn families_mut(&mut self) -> &mut FamilyList {
|
pub fn families_mut(&mut self) -> &mut FamilyList {
|
||||||
Rc::make_mut(&mut self.families)
|
Rc::make_mut(&mut self.families)
|
||||||
@ -194,7 +152,6 @@ impl Default for FontState {
|
|||||||
size: Length::pt(11.0),
|
size: Length::pt(11.0),
|
||||||
top_edge: VerticalFontMetric::CapHeight,
|
top_edge: VerticalFontMetric::CapHeight,
|
||||||
bottom_edge: VerticalFontMetric::Baseline,
|
bottom_edge: VerticalFontMetric::Baseline,
|
||||||
scale: Linear::one(),
|
|
||||||
fill: Fill::Color(Color::Rgba(RgbaColor::BLACK)),
|
fill: Fill::Color(Color::Rgba(RgbaColor::BLACK)),
|
||||||
strong: false,
|
strong: false,
|
||||||
emph: false,
|
emph: false,
|
||||||
@ -205,11 +162,9 @@ impl Default for FontState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes a line that could be positioned over or under text.
|
/// Describes a line that could be positioned over, under or on top of text.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
||||||
pub struct LineState {
|
pub struct LineState {
|
||||||
/// Color of the line. Will default to text color if `None`.
|
|
||||||
pub fill: Option<Fill>,
|
|
||||||
/// Thickness of the line's stroke. Calling functions should attempt to
|
/// Thickness of the line's stroke. Calling functions should attempt to
|
||||||
/// read this value from the appropriate font tables if this is `None`.
|
/// read this value from the appropriate font tables if this is `None`.
|
||||||
pub strength: Option<Linear>,
|
pub strength: Option<Linear>,
|
||||||
@ -219,40 +174,8 @@ pub struct LineState {
|
|||||||
pub position: Option<Linear>,
|
pub position: Option<Linear>,
|
||||||
/// Amount that the line will be longer or shorter than its associated text.
|
/// Amount that the line will be longer or shorter than its associated text.
|
||||||
pub extent: Linear,
|
pub extent: Linear,
|
||||||
}
|
/// Color of the line. Will default to text color if `None`.
|
||||||
|
pub fill: Option<Fill>,
|
||||||
impl LineState {
|
|
||||||
pub fn resolve_props(&self, font_size: Length, fill: &Fill) -> LineProps {
|
|
||||||
LineProps {
|
|
||||||
fill: self.fill.unwrap_or_else(|| fill.clone()),
|
|
||||||
strength: self.strength.map(|s| s.resolve(font_size)),
|
|
||||||
position: self.position.map(|p| p.resolve(font_size)),
|
|
||||||
extent: self.extent.resolve(font_size),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Properties used for font selection and layout.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
|
||||||
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 fill color of the text.
|
|
||||||
pub fill: Fill,
|
|
||||||
/// The specifications for a strikethrough line, if any.
|
|
||||||
pub strikethrough: Option<LineProps>,
|
|
||||||
/// The specifications for a underline, if any.
|
|
||||||
pub underline: Option<LineProps>,
|
|
||||||
/// The specifications for a overline line, if any.
|
|
||||||
pub overline: Option<LineProps>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Font family definitions.
|
/// Font family definitions.
|
||||||
@ -319,19 +242,3 @@ impl Display for FontFamily {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Describes a line that could be positioned over or under text.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Hash)]
|
|
||||||
pub struct LineProps {
|
|
||||||
/// Color of the line.
|
|
||||||
pub fill: Fill,
|
|
||||||
/// Thickness of the line's stroke. Calling functions should attempt to
|
|
||||||
/// read this value from the appropriate font tables if this is `None`.
|
|
||||||
pub strength: Option<Length>,
|
|
||||||
/// Position of the line relative to the baseline. Calling functions should
|
|
||||||
/// attempt to read this value from the appropriate font tables if this is
|
|
||||||
/// `None`.
|
|
||||||
pub position: Option<Length>,
|
|
||||||
/// Amount that the line will be longer or shorter than its associated text.
|
|
||||||
pub extent: Length,
|
|
||||||
}
|
|
||||||
|
@ -14,7 +14,7 @@ use ttf_parser::{name_id, GlyphId};
|
|||||||
|
|
||||||
use crate::cache::Cache;
|
use crate::cache::Cache;
|
||||||
use crate::color::Color;
|
use crate::color::Color;
|
||||||
use crate::font::{Em, FaceId, VerticalFontMetric};
|
use crate::font::{Em, FaceId};
|
||||||
use crate::geom::{self, Length, Size};
|
use crate::geom::{self, Length, Size};
|
||||||
use crate::image::{Image, ImageId};
|
use crate::image::{Image, ImageId};
|
||||||
use crate::layout::{Element, Fill, Frame, Shape};
|
use crate::layout::{Element, Fill, Frame, Shape};
|
||||||
@ -256,9 +256,9 @@ impl<'a> PdfExporter<'a> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let italic_angle = ttf.italic_angle().unwrap_or(0.0);
|
let italic_angle = ttf.italic_angle().unwrap_or(0.0);
|
||||||
let ascender = face.vertical_metric(VerticalFontMetric::Ascender).to_pdf();
|
let ascender = face.ascender.to_pdf();
|
||||||
let descender = face.vertical_metric(VerticalFontMetric::Descender).to_pdf();
|
let descender = face.descender.to_pdf();
|
||||||
let cap_height = face.vertical_metric(VerticalFontMetric::CapHeight).to_pdf();
|
let cap_height = face.cap_height.to_pdf();
|
||||||
let stem_v = 10.0 + 0.244 * (f32::from(ttf.weight().to_number()) - 50.0);
|
let stem_v = 10.0 + 0.244 * (f32::from(ttf.weight().to_number()) - 50.0);
|
||||||
|
|
||||||
// Write the base font object referencing the CID font.
|
// Write the base font object referencing the CID font.
|
||||||
|
58
src/font.rs
58
src/font.rs
@ -15,10 +15,19 @@ pub struct Face {
|
|||||||
index: u32,
|
index: u32,
|
||||||
ttf: rustybuzz::Face<'static>,
|
ttf: rustybuzz::Face<'static>,
|
||||||
units_per_em: f64,
|
units_per_em: f64,
|
||||||
ascender: Em,
|
pub ascender: Em,
|
||||||
cap_height: Em,
|
pub cap_height: Em,
|
||||||
x_height: Em,
|
pub x_height: Em,
|
||||||
descender: Em,
|
pub descender: Em,
|
||||||
|
pub strikethrough: LineMetrics,
|
||||||
|
pub underline: LineMetrics,
|
||||||
|
pub overline: LineMetrics,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Metrics for a decorative line.
|
||||||
|
pub struct LineMetrics {
|
||||||
|
pub strength: Em,
|
||||||
|
pub position: Em,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Face {
|
impl Face {
|
||||||
@ -35,22 +44,45 @@ impl Face {
|
|||||||
|
|
||||||
let ttf = rustybuzz::Face::from_slice(slice, index)?;
|
let ttf = rustybuzz::Face::from_slice(slice, index)?;
|
||||||
|
|
||||||
// Look up some metrics we may need often.
|
|
||||||
let units_per_em = f64::from(ttf.units_per_em());
|
let units_per_em = f64::from(ttf.units_per_em());
|
||||||
let ascender = ttf.typographic_ascender().unwrap_or(ttf.ascender());
|
let to_em = |units| Em::from_units(units, units_per_em);
|
||||||
let cap_height = ttf.capital_height().filter(|&h| h > 0).unwrap_or(ascender);
|
|
||||||
let x_height = ttf.x_height().filter(|&h| h > 0).unwrap_or(ascender);
|
let ascender = to_em(ttf.typographic_ascender().unwrap_or(ttf.ascender()));
|
||||||
let descender = ttf.typographic_descender().unwrap_or(ttf.descender());
|
let cap_height = ttf.capital_height().filter(|&h| h > 0).map_or(ascender, to_em);
|
||||||
|
let x_height = ttf.x_height().filter(|&h| h > 0).map_or(ascender, to_em);
|
||||||
|
let descender = to_em(ttf.typographic_descender().unwrap_or(ttf.descender()));
|
||||||
|
|
||||||
|
let strikeout = ttf.strikeout_metrics();
|
||||||
|
let underline = ttf.underline_metrics();
|
||||||
|
let default = Em::new(0.06);
|
||||||
|
|
||||||
|
let strikethrough = LineMetrics {
|
||||||
|
strength: strikeout.or(underline).map_or(default, |s| to_em(s.thickness)),
|
||||||
|
position: strikeout.map_or(Em::new(0.25), |s| to_em(s.position)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let underline = LineMetrics {
|
||||||
|
strength: underline.or(strikeout).map_or(default, |s| to_em(s.thickness)),
|
||||||
|
position: underline.map_or(Em::new(-0.2), |s| to_em(s.position)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let overline = LineMetrics {
|
||||||
|
strength: underline.strength,
|
||||||
|
position: cap_height + Em::new(0.1),
|
||||||
|
};
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
buffer,
|
buffer,
|
||||||
index,
|
index,
|
||||||
ttf,
|
ttf,
|
||||||
units_per_em,
|
units_per_em,
|
||||||
ascender: Em::from_units(ascender, units_per_em),
|
ascender,
|
||||||
cap_height: Em::from_units(cap_height, units_per_em),
|
cap_height,
|
||||||
x_height: Em::from_units(x_height, units_per_em),
|
x_height,
|
||||||
descender: Em::from_units(descender, units_per_em),
|
descender,
|
||||||
|
strikethrough,
|
||||||
|
underline,
|
||||||
|
overline,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
use decorum::N64;
|
||||||
|
|
||||||
/// An angle.
|
/// An angle.
|
||||||
#[derive(Default, Copy, Clone, PartialEq, PartialOrd)]
|
#[derive(Default, Copy, Clone, PartialEq, PartialOrd, Hash)]
|
||||||
pub struct Angle {
|
pub struct Angle {
|
||||||
/// The angle in raw units.
|
/// The angle in raw units.
|
||||||
raw: f64,
|
raw: N64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Angle {
|
impl Angle {
|
||||||
/// The zero angle.
|
/// The zero angle.
|
||||||
pub const ZERO: Self = Self { raw: 0.0 };
|
pub fn zero() -> Self {
|
||||||
|
Self { raw: N64::from(0.0) }
|
||||||
|
}
|
||||||
|
|
||||||
/// Create an angle from a number of radians.
|
/// Create an angle from a number of radians.
|
||||||
pub fn rad(rad: f64) -> Self {
|
pub fn rad(rad: f64) -> Self {
|
||||||
@ -23,7 +26,7 @@ impl Angle {
|
|||||||
|
|
||||||
/// Create an angle from a number of raw units.
|
/// Create an angle from a number of raw units.
|
||||||
pub fn raw(raw: f64) -> Self {
|
pub fn raw(raw: f64) -> Self {
|
||||||
Self { raw }
|
Self { raw: N64::from(raw) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this to a number of radians.
|
/// Convert this to a number of radians.
|
||||||
@ -38,17 +41,17 @@ impl Angle {
|
|||||||
|
|
||||||
/// Get the value of this angle in raw units.
|
/// Get the value of this angle in raw units.
|
||||||
pub fn to_raw(self) -> f64 {
|
pub fn to_raw(self) -> f64 {
|
||||||
self.raw
|
self.raw.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create an angle from a value in a unit.
|
/// Create an angle from a value in a unit.
|
||||||
pub fn with_unit(val: f64, unit: AngularUnit) -> Self {
|
pub fn with_unit(val: f64, unit: AngularUnit) -> Self {
|
||||||
Self { raw: val * unit.raw_scale() }
|
Self { raw: N64::from(val * unit.raw_scale()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the value of this length in unit.
|
/// Get the value of this length in unit.
|
||||||
pub fn to_unit(self, unit: AngularUnit) -> f64 {
|
pub fn to_unit(self, unit: AngularUnit) -> f64 {
|
||||||
self.raw / unit.raw_scale()
|
self.to_raw() / unit.raw_scale()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +122,7 @@ impl Div for Angle {
|
|||||||
type Output = f64;
|
type Output = f64;
|
||||||
|
|
||||||
fn div(self, other: Self) -> f64 {
|
fn div(self, other: Self) -> f64 {
|
||||||
self.raw / other.raw
|
self.to_raw() / other.to_raw()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,7 +133,7 @@ assign_impl!(Angle /= f64);
|
|||||||
|
|
||||||
impl Sum for Angle {
|
impl Sum for Angle {
|
||||||
fn sum<I: Iterator<Item = Angle>>(iter: I) -> Self {
|
fn sum<I: Iterator<Item = Angle>>(iter: I) -> Self {
|
||||||
iter.fold(Angle::ZERO, Add::add)
|
iter.fold(Angle::zero(), Add::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Different units of angular measurement.
|
/// Different units of angular measurement.
|
||||||
|
@ -3,7 +3,7 @@ use super::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// A point in 2D.
|
/// A point in 2D.
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
pub struct Point {
|
pub struct Point {
|
||||||
/// The x coordinate.
|
/// The x coordinate.
|
||||||
pub x: Length,
|
pub x: Length,
|
||||||
|
@ -3,7 +3,7 @@ use super::*;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
/// A size in 2D.
|
/// A size in 2D.
|
||||||
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Default, Copy, Clone, PartialEq, Serialize, Deserialize, Hash)]
|
||||||
pub struct Size {
|
pub struct Size {
|
||||||
/// The width.
|
/// The width.
|
||||||
pub width: Length,
|
pub width: Length,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
/// A container with a horizontal and vertical component.
|
/// A container with a horizontal and vertical component.
|
||||||
#[derive(Default, Copy, Clone, Eq, PartialEq)]
|
#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub struct Spec<T> {
|
pub struct Spec<T> {
|
||||||
/// The horizontal component.
|
/// The horizontal component.
|
||||||
pub horizontal: T,
|
pub horizontal: T,
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use unicode_bidi::{BidiInfo, Level};
|
use unicode_bidi::{BidiInfo, Level};
|
||||||
use xi_unicode::LineBreakIterator;
|
use xi_unicode::LineBreakIterator;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::exec::FontProps;
|
use crate::exec::FontState;
|
||||||
use crate::util::{RangeExt, SliceExt};
|
use crate::util::{RangeExt, SliceExt};
|
||||||
|
|
||||||
type Range = std::ops::Range<usize>;
|
type Range = std::ops::Range<usize>;
|
||||||
@ -26,7 +27,7 @@ pub enum ParChild {
|
|||||||
/// Spacing between other nodes.
|
/// Spacing between other nodes.
|
||||||
Spacing(Length),
|
Spacing(Length),
|
||||||
/// A run of text and how to align it in its line.
|
/// A run of text and how to align it in its line.
|
||||||
Text(String, FontProps, Align),
|
Text(String, Align, Rc<FontState>),
|
||||||
/// Any child node and how to align it in its line.
|
/// Any child node and how to align it in its line.
|
||||||
Any(AnyNode, Align),
|
Any(AnyNode, Align),
|
||||||
}
|
}
|
||||||
@ -131,11 +132,11 @@ impl<'a> ParLayout<'a> {
|
|||||||
items.push(ParItem::Spacing(amount));
|
items.push(ParItem::Spacing(amount));
|
||||||
ranges.push(range);
|
ranges.push(range);
|
||||||
}
|
}
|
||||||
ParChild::Text(_, ref props, align) => {
|
ParChild::Text(_, align, ref state) => {
|
||||||
// TODO: Also split by language and script.
|
// TODO: Also split by language and script.
|
||||||
for (subrange, dir) in split_runs(&bidi, range) {
|
for (subrange, dir) in split_runs(&bidi, range) {
|
||||||
let text = &bidi.text[subrange.clone()];
|
let text = &bidi.text[subrange.clone()];
|
||||||
let shaped = shape(ctx, text, dir, props);
|
let shaped = shape(ctx, text, dir, state);
|
||||||
items.push(ParItem::Text(shaped, align));
|
items.push(ParItem::Text(shaped, align));
|
||||||
ranges.push(subrange);
|
ranges.push(subrange);
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt::{self, Debug, Formatter};
|
use std::fmt::{self, Debug, Formatter};
|
||||||
use std::ops::{Add, Range};
|
use std::ops::Range;
|
||||||
|
|
||||||
use rustybuzz::UnicodeBuffer;
|
use rustybuzz::UnicodeBuffer;
|
||||||
|
|
||||||
use super::{Element, Frame, Glyph, LayoutContext, Text};
|
use super::{Element, Frame, Glyph, LayoutContext, Text};
|
||||||
use crate::exec::FontProps;
|
use crate::exec::{FontState, LineState};
|
||||||
use crate::font::{Em, Face, FaceId, VerticalFontMetric};
|
use crate::font::{Face, FaceId, FontStyle, LineMetrics};
|
||||||
use crate::geom::{Dir, Length, Point, Size};
|
use crate::geom::{Dir, Length, Point, Size};
|
||||||
use crate::layout::Shape;
|
use crate::layout::Shape;
|
||||||
use crate::util::SliceExt;
|
use crate::util::SliceExt;
|
||||||
@ -23,7 +23,7 @@ pub struct ShapedText<'a> {
|
|||||||
/// The text direction.
|
/// The text direction.
|
||||||
pub dir: Dir,
|
pub dir: Dir,
|
||||||
/// The properties used for font selection.
|
/// The properties used for font selection.
|
||||||
pub props: &'a FontProps,
|
pub state: &'a FontState,
|
||||||
/// The font size.
|
/// The font size.
|
||||||
pub size: Size,
|
pub size: Size,
|
||||||
/// The baseline from the top of the frame.
|
/// The baseline from the top of the frame.
|
||||||
@ -69,8 +69,8 @@ impl<'a> ShapedText<'a> {
|
|||||||
|
|
||||||
let mut text = Text {
|
let mut text = Text {
|
||||||
face_id,
|
face_id,
|
||||||
size: self.props.size,
|
size: self.state.size,
|
||||||
fill: self.props.fill,
|
fill: self.state.fill,
|
||||||
glyphs: vec![],
|
glyphs: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ impl<'a> ShapedText<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
frame.push(pos, Element::Text(text));
|
frame.push(pos, Element::Text(text));
|
||||||
decorate(ctx, &mut frame, &self.props, face_id, pos, width);
|
decorate(ctx, &mut frame, pos, width, face_id, &self.state);
|
||||||
|
|
||||||
offset += width;
|
offset += width;
|
||||||
}
|
}
|
||||||
@ -101,17 +101,17 @@ impl<'a> ShapedText<'a> {
|
|||||||
text_range: Range<usize>,
|
text_range: Range<usize>,
|
||||||
) -> ShapedText<'a> {
|
) -> ShapedText<'a> {
|
||||||
if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) {
|
if let Some(glyphs) = self.slice_safe_to_break(text_range.clone()) {
|
||||||
let (size, baseline) = measure(ctx, glyphs, self.props);
|
let (size, baseline) = measure(ctx, glyphs, self.state);
|
||||||
Self {
|
Self {
|
||||||
text: &self.text[text_range],
|
text: &self.text[text_range],
|
||||||
dir: self.dir,
|
dir: self.dir,
|
||||||
props: self.props,
|
state: self.state,
|
||||||
size,
|
size,
|
||||||
baseline,
|
baseline,
|
||||||
glyphs: Cow::Borrowed(glyphs),
|
glyphs: Cow::Borrowed(glyphs),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
shape(ctx, &self.text[text_range], self.dir, self.props)
|
shape(ctx, &self.text[text_range], self.dir, self.state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,20 +185,20 @@ pub fn shape<'a>(
|
|||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
text: &'a str,
|
text: &'a str,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
props: &'a FontProps,
|
state: &'a FontState,
|
||||||
) -> ShapedText<'a> {
|
) -> ShapedText<'a> {
|
||||||
let mut glyphs = vec![];
|
let mut glyphs = vec![];
|
||||||
let families = props.families.iter();
|
let families = state.families.iter();
|
||||||
if !text.is_empty() {
|
if !text.is_empty() {
|
||||||
shape_segment(ctx, &mut glyphs, 0, text, dir, props, families, None);
|
shape_segment(ctx, &mut glyphs, 0, text, dir, state, families, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
let (size, baseline) = measure(ctx, &glyphs, props);
|
let (size, baseline) = measure(ctx, &glyphs, state);
|
||||||
|
|
||||||
ShapedText {
|
ShapedText {
|
||||||
text,
|
text,
|
||||||
dir,
|
dir,
|
||||||
props,
|
state,
|
||||||
size,
|
size,
|
||||||
baseline,
|
baseline,
|
||||||
glyphs: Cow::Owned(glyphs),
|
glyphs: Cow::Owned(glyphs),
|
||||||
@ -212,7 +212,7 @@ fn shape_segment<'a>(
|
|||||||
base: usize,
|
base: usize,
|
||||||
text: &str,
|
text: &str,
|
||||||
dir: Dir,
|
dir: Dir,
|
||||||
props: &FontProps,
|
state: &FontState,
|
||||||
mut families: impl Iterator<Item = &'a str> + Clone,
|
mut families: impl Iterator<Item = &'a str> + Clone,
|
||||||
mut first_face: Option<FaceId>,
|
mut first_face: Option<FaceId>,
|
||||||
) {
|
) {
|
||||||
@ -221,7 +221,21 @@ fn shape_segment<'a>(
|
|||||||
// Try to load the next available font family.
|
// Try to load the next available font family.
|
||||||
match families.next() {
|
match families.next() {
|
||||||
Some(family) => {
|
Some(family) => {
|
||||||
match ctx.cache.font.select(ctx.loader, family, props.variant) {
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match ctx.cache.font.select(ctx.loader, family, variant) {
|
||||||
Some(id) => break (id, true),
|
Some(id) => break (id, true),
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
@ -267,8 +281,8 @@ fn shape_segment<'a>(
|
|||||||
glyphs.push(ShapedGlyph {
|
glyphs.push(ShapedGlyph {
|
||||||
face_id,
|
face_id,
|
||||||
glyph_id: info.codepoint as u16,
|
glyph_id: info.codepoint as u16,
|
||||||
x_advance: face.to_em(pos[i].x_advance).to_length(props.size),
|
x_advance: face.to_em(pos[i].x_advance).to_length(state.size),
|
||||||
x_offset: face.to_em(pos[i].x_offset).to_length(props.size),
|
x_offset: face.to_em(pos[i].x_offset).to_length(state.size),
|
||||||
text_index: base + cluster,
|
text_index: base + cluster,
|
||||||
safe_to_break: !info.unsafe_to_break(),
|
safe_to_break: !info.unsafe_to_break(),
|
||||||
});
|
});
|
||||||
@ -319,7 +333,7 @@ fn shape_segment<'a>(
|
|||||||
base + range.start,
|
base + range.start,
|
||||||
&text[range],
|
&text[range],
|
||||||
dir,
|
dir,
|
||||||
props,
|
state,
|
||||||
families.clone(),
|
families.clone(),
|
||||||
first_face,
|
first_face,
|
||||||
);
|
);
|
||||||
@ -336,7 +350,7 @@ fn shape_segment<'a>(
|
|||||||
fn measure(
|
fn measure(
|
||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
glyphs: &[ShapedGlyph],
|
glyphs: &[ShapedGlyph],
|
||||||
props: &FontProps,
|
state: &FontState,
|
||||||
) -> (Size, Length) {
|
) -> (Size, Length) {
|
||||||
let cache = &mut ctx.cache.font;
|
let cache = &mut ctx.cache.font;
|
||||||
|
|
||||||
@ -344,15 +358,15 @@ fn measure(
|
|||||||
let mut top = Length::zero();
|
let mut top = Length::zero();
|
||||||
let mut bottom = Length::zero();
|
let mut bottom = Length::zero();
|
||||||
let mut expand_vertical = |face: &Face| {
|
let mut expand_vertical = |face: &Face| {
|
||||||
top.set_max(face.vertical_metric(props.top_edge).to_length(props.size));
|
top.set_max(face.vertical_metric(state.top_edge).to_length(state.size));
|
||||||
bottom.set_max(-face.vertical_metric(props.bottom_edge).to_length(props.size));
|
bottom.set_max(-face.vertical_metric(state.bottom_edge).to_length(state.size));
|
||||||
};
|
};
|
||||||
|
|
||||||
if glyphs.is_empty() {
|
if glyphs.is_empty() {
|
||||||
// When there are no glyphs, we just use the vertical metrics of the
|
// When there are no glyphs, we just use the vertical metrics of the
|
||||||
// first available font.
|
// first available font.
|
||||||
for family in props.families.iter() {
|
for family in state.families.iter() {
|
||||||
if let Some(face_id) = cache.select(ctx.loader, family, props.variant) {
|
if let Some(face_id) = cache.select(ctx.loader, family, state.variant) {
|
||||||
expand_vertical(cache.get(face_id));
|
expand_vertical(cache.get(face_id));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -375,76 +389,44 @@ fn measure(
|
|||||||
fn decorate(
|
fn decorate(
|
||||||
ctx: &LayoutContext,
|
ctx: &LayoutContext,
|
||||||
frame: &mut Frame,
|
frame: &mut Frame,
|
||||||
props: &FontProps,
|
|
||||||
face_id: FaceId,
|
|
||||||
pos: Point,
|
pos: Point,
|
||||||
width: Length,
|
width: Length,
|
||||||
|
face_id: FaceId,
|
||||||
|
state: &FontState,
|
||||||
) {
|
) {
|
||||||
let mut apply = |strength, position, extent, fill| {
|
let mut apply = |substate: &LineState, metrics: fn(&Face) -> &LineMetrics| {
|
||||||
|
let metrics = metrics(&ctx.cache.font.get(face_id));
|
||||||
|
|
||||||
|
let strength = substate
|
||||||
|
.strength
|
||||||
|
.map(|s| s.resolve(state.size))
|
||||||
|
.unwrap_or(metrics.strength.to_length(state.size));
|
||||||
|
|
||||||
|
let position = substate
|
||||||
|
.position
|
||||||
|
.map(|s| s.resolve(state.size))
|
||||||
|
.unwrap_or(metrics.position.to_length(state.size));
|
||||||
|
|
||||||
|
let extent = substate.extent.resolve(state.size);
|
||||||
|
let fill = substate.fill.unwrap_or(state.fill);
|
||||||
|
|
||||||
let pos = Point::new(pos.x - extent, pos.y - position);
|
let pos = Point::new(pos.x - extent, pos.y - position);
|
||||||
let target = Point::new(width + 2.0 * extent, Length::zero());
|
let target = Point::new(width + 2.0 * extent, Length::zero());
|
||||||
frame.push(pos, Element::Geometry(Shape::Line(target, strength), fill));
|
let shape = Shape::Line(target, strength);
|
||||||
|
let element = Element::Geometry(shape, fill);
|
||||||
|
|
||||||
|
frame.push(pos, element);
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(strikethrough) = props.strikethrough {
|
if let Some(strikethrough) = &state.strikethrough {
|
||||||
let face = ctx.cache.font.get(face_id);
|
apply(strikethrough, |face| &face.strikethrough);
|
||||||
|
|
||||||
let strength = strikethrough.strength.unwrap_or_else(|| {
|
|
||||||
face.ttf()
|
|
||||||
.strikeout_metrics()
|
|
||||||
.or_else(|| face.ttf().underline_metrics())
|
|
||||||
.map_or(Em::new(0.06), |m| face.to_em(m.thickness))
|
|
||||||
.to_length(props.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
let position = strikethrough.position.unwrap_or_else(|| {
|
|
||||||
face.ttf()
|
|
||||||
.strikeout_metrics()
|
|
||||||
.map_or(Em::new(0.25), |m| face.to_em(m.position))
|
|
||||||
.to_length(props.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
apply(strength, position, strikethrough.extent, strikethrough.fill);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(underline) = props.underline {
|
if let Some(underline) = &state.underline {
|
||||||
let face = ctx.cache.font.get(face_id);
|
apply(underline, |face| &face.underline);
|
||||||
|
|
||||||
let strength = underline.strength.unwrap_or_else(|| {
|
|
||||||
face.ttf()
|
|
||||||
.underline_metrics()
|
|
||||||
.or_else(|| face.ttf().strikeout_metrics())
|
|
||||||
.map_or(Em::new(0.06), |m| face.to_em(m.thickness))
|
|
||||||
.to_length(props.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
let position = underline.position.unwrap_or_else(|| {
|
|
||||||
face.ttf()
|
|
||||||
.underline_metrics()
|
|
||||||
.map_or(Em::new(-0.2), |m| face.to_em(m.position))
|
|
||||||
.to_length(props.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
apply(strength, position, underline.extent, underline.fill);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(overline) = props.overline {
|
if let Some(overline) = &state.overline {
|
||||||
let face = ctx.cache.font.get(face_id);
|
apply(overline, |face| &face.overline);
|
||||||
|
|
||||||
let strength = overline.strength.unwrap_or_else(|| {
|
|
||||||
face.ttf()
|
|
||||||
.underline_metrics()
|
|
||||||
.or_else(|| face.ttf().strikeout_metrics())
|
|
||||||
.map_or(Em::new(0.06), |m| face.to_em(m.thickness))
|
|
||||||
.to_length(props.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
let position = overline.position.unwrap_or_else(|| {
|
|
||||||
face.vertical_metric(VerticalFontMetric::CapHeight)
|
|
||||||
.add(Em::new(0.1))
|
|
||||||
.to_length(props.size)
|
|
||||||
});
|
|
||||||
|
|
||||||
apply(strength, position, overline.extent, overline.fill);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ fn line_impl(
|
|||||||
name: &str,
|
name: &str,
|
||||||
ctx: &mut EvalContext,
|
ctx: &mut EvalContext,
|
||||||
args: &mut FuncArgs,
|
args: &mut FuncArgs,
|
||||||
substate: impl Fn(&mut FontState) -> &mut Option<LineState> + 'static,
|
substate: fn(&mut FontState) -> &mut Option<Rc<LineState>>,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
let color = args.eat_named(ctx, "color");
|
let color = args.eat_named(ctx, "color");
|
||||||
let position = args.eat_named(ctx, "position");
|
let position = args.eat_named(ctx, "position");
|
||||||
@ -64,17 +64,19 @@ fn line_impl(
|
|||||||
let body = args.eat::<TemplateValue>(ctx);
|
let body = args.eat::<TemplateValue>(ctx);
|
||||||
|
|
||||||
// Suppress any existing strikethrough if strength is explicitly zero.
|
// Suppress any existing strikethrough if strength is explicitly zero.
|
||||||
let state = strength.map_or(true, |s| !s.is_zero()).then(|| LineState {
|
let state = strength.map_or(true, |s| !s.is_zero()).then(|| {
|
||||||
fill: color.map(Fill::Color),
|
Rc::new(LineState {
|
||||||
strength,
|
strength,
|
||||||
position,
|
position,
|
||||||
extent,
|
extent,
|
||||||
|
fill: color.map(Fill::Color),
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
Value::template(name, move |ctx| {
|
Value::template(name, move |ctx| {
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
|
|
||||||
*substate(&mut ctx.state.font) = state;
|
*substate(ctx.state.font_mut()) = state.clone();
|
||||||
|
|
||||||
if let Some(body) = &body {
|
if let Some(body) = &body {
|
||||||
body.exec(ctx);
|
body.exec(ctx);
|
||||||
|
@ -64,54 +64,50 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
|
|||||||
|
|
||||||
Value::template("font", move |ctx| {
|
Value::template("font", move |ctx| {
|
||||||
let snapshot = ctx.state.clone();
|
let snapshot = ctx.state.clone();
|
||||||
|
let font = ctx.state.font_mut();
|
||||||
|
|
||||||
if let Some(linear) = size {
|
if let Some(linear) = size {
|
||||||
if linear.rel.is_zero() {
|
font.size = linear.resolve(font.size);
|
||||||
ctx.state.font.size = linear.abs;
|
|
||||||
ctx.state.font.scale = Linear::one();
|
|
||||||
} else {
|
|
||||||
ctx.state.font.scale = linear;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !list.is_empty() {
|
if !list.is_empty() {
|
||||||
ctx.state.font.families_mut().list = list.clone();
|
font.families_mut().list = list.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(style) = style {
|
if let Some(style) = style {
|
||||||
ctx.state.font.variant.style = style;
|
font.variant.style = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(weight) = weight {
|
if let Some(weight) = weight {
|
||||||
ctx.state.font.variant.weight = weight;
|
font.variant.weight = weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(stretch) = stretch {
|
if let Some(stretch) = stretch {
|
||||||
ctx.state.font.variant.stretch = stretch;
|
font.variant.stretch = stretch;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(top_edge) = top_edge {
|
if let Some(top_edge) = top_edge {
|
||||||
ctx.state.font.top_edge = top_edge;
|
font.top_edge = top_edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bottom_edge) = bottom_edge {
|
if let Some(bottom_edge) = bottom_edge {
|
||||||
ctx.state.font.bottom_edge = bottom_edge;
|
font.bottom_edge = bottom_edge;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(color) = color {
|
if let Some(color) = color {
|
||||||
ctx.state.font.fill = Fill::Color(color);
|
font.fill = Fill::Color(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(FontFamilies(serif)) = &serif {
|
if let Some(FontFamilies(serif)) = &serif {
|
||||||
ctx.state.font.families_mut().serif = serif.clone();
|
font.families_mut().serif = serif.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(FontFamilies(sans_serif)) = &sans_serif {
|
if let Some(FontFamilies(sans_serif)) = &sans_serif {
|
||||||
ctx.state.font.families_mut().sans_serif = sans_serif.clone();
|
font.families_mut().sans_serif = sans_serif.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(FontFamilies(monospace)) = &monospace {
|
if let Some(FontFamilies(monospace)) = &monospace {
|
||||||
ctx.state.font.families_mut().monospace = monospace.clone();
|
font.families_mut().monospace = monospace.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(body) = &body {
|
if let Some(body) = &body {
|
||||||
|
@ -34,6 +34,7 @@ pub use spacing::*;
|
|||||||
pub use stack::*;
|
pub use stack::*;
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
use std::fmt::{self, Display, Formatter};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::color::RgbaColor;
|
use crate::color::RgbaColor;
|
||||||
use crate::eval::{EvalContext, FuncArgs, Scope, TemplateValue, Value};
|
use crate::eval::{EvalContext, FuncArgs, Scope, TemplateValue, Value};
|
||||||
|
@ -31,7 +31,8 @@ fn spacing_impl(
|
|||||||
let spacing: Option<Linear> = args.eat_expect(ctx, "spacing");
|
let spacing: Option<Linear> = args.eat_expect(ctx, "spacing");
|
||||||
Value::template(name, move |ctx| {
|
Value::template(name, move |ctx| {
|
||||||
if let Some(linear) = spacing {
|
if let Some(linear) = spacing {
|
||||||
let amount = linear.resolve(ctx.state.font.resolve_size());
|
// TODO: Should this really always be font-size relative?
|
||||||
|
let amount = linear.resolve(ctx.state.font.size);
|
||||||
ctx.push_spacing(axis, amount);
|
ctx.push_spacing(axis, amount);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -26,7 +26,7 @@ impl Paper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Defines default margins for a class of related papers.
|
/// Defines default margins for a class of related papers.
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||||
pub enum PaperClass {
|
pub enum PaperClass {
|
||||||
Custom,
|
Custom,
|
||||||
Base,
|
Base,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user