Move font family and refactor alignment

This commit is contained in:
Laurenz 2021-07-29 12:21:55 +02:00
parent 853361338b
commit 7d15dc634b
8 changed files with 133 additions and 150 deletions

View File

@ -1,8 +1,9 @@
use std::fmt::{self, Display, Formatter};
use std::rc::Rc;
use crate::color::{Color, RgbaColor};
use crate::font::{FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric};
use crate::font::{
FontFamily, FontStretch, FontStyle, FontVariant, FontWeight, VerticalFontMetric,
};
use crate::geom::*;
use crate::layout::Paint;
use crate::paper::{PaperClass, PAPER_A4};
@ -236,26 +237,6 @@ impl Default for FamilyList {
}
}
/// A generic or named font family.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
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,
})
}
}
/// Describes a line that is positioned over, under or on top of text.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct LineState {

View File

@ -334,6 +334,26 @@ impl FaceId {
}
}
/// A generic or named font family.
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
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,
})
}
}
/// Properties of a single font face.
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct FaceInfo {

View File

@ -1,6 +1,6 @@
use super::*;
/// Where to align something along a directed axis.
/// Where to align something along an axis.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Align {
/// Align at the start of the axis.
@ -9,15 +9,27 @@ pub enum Align {
Center,
/// Align at the end of the axis.
End,
/// Align at the left side of the axis.
Left,
/// Align at the right side of the axis.
Right,
/// Align at the top side of the axis.
Top,
/// Align at the bottom side of the axis.
Bottom,
}
impl Align {
/// Returns the position of this alignment in the given range.
pub fn resolve(self, dir: Dir, range: Range<Length>) -> Length {
match if dir.is_positive() { self } else { self.inv() } {
Self::Start => range.start,
Self::Center => (range.start + range.end) / 2.0,
Self::End => range.end,
/// The axis this alignment belongs to if it is specific.
pub fn axis(self) -> Option<SpecAxis> {
match self {
Self::Start => None,
Self::Center => None,
Self::End => None,
Self::Left => Some(SpecAxis::Horizontal),
Self::Right => Some(SpecAxis::Horizontal),
Self::Top => Some(SpecAxis::Vertical),
Self::Bottom => Some(SpecAxis::Vertical),
}
}
@ -27,6 +39,38 @@ impl Align {
Self::Start => Self::End,
Self::Center => Self::Center,
Self::End => Self::Start,
Self::Left => Self::Right,
Self::Right => Self::Left,
Self::Top => Self::Bottom,
Self::Bottom => Self::Top,
}
}
/// Returns the position of this alignment in the given range.
pub fn resolve(self, dir: Dir, range: Range<Length>) -> Length {
#[cfg(debug_assertions)]
if let Some(axis) = self.axis() {
debug_assert_eq!(axis, dir.axis())
}
match self {
Self::Start => {
if dir.is_positive() {
range.start
} else {
range.end
}
}
Self::Center => (range.start + range.end) / 2.0,
Self::End => {
if dir.is_positive() {
range.end
} else {
range.start
}
}
Self::Left | Self::Top => range.start,
Self::Right | Self::Bottom => range.end,
}
}
}
@ -43,6 +87,10 @@ impl Display for Align {
Self::Start => "start",
Self::Center => "center",
Self::End => "end",
Self::Left => "left",
Self::Right => "right",
Self::Top => "top",
Self::Bottom => "bottom",
})
}
}

View File

@ -86,8 +86,8 @@ pub fn ellipse(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
/// `circle`: A circle with optional content.
pub fn circle(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let radius = args.named::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
let width = radius.or_else(|| args.named(ctx, "width"));
let diameter = args.named::<Length>(ctx, "radius").map(|r| 2.0 * Linear::from(r));
let width = diameter.or_else(|| args.named(ctx, "width"));
let height = width.is_none().then(|| args.named(ctx, "height")).flatten();
let fill = args.named(ctx, "fill");
let body = args.eat().unwrap_or_default();

View File

@ -104,10 +104,10 @@ fn spacing_impl(ctx: &mut EvalContext, args: &mut FuncArgs, axis: GenAxis) -> Va
/// `align`: Configure the alignment along the layouting axes.
pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let first = args.eat::<AlignValue>();
let second = args.eat::<AlignValue>();
let mut horizontal = args.named::<AlignValue>(ctx, "horizontal");
let mut vertical = args.named::<AlignValue>(ctx, "vertical");
let first = args.eat::<Align>();
let second = args.eat::<Align>();
let mut horizontal = args.named(ctx, "horizontal");
let mut vertical = args.named(ctx, "vertical");
let body = args.expect::<Template>(ctx, "body").unwrap_or_default();
for value in first.into_iter().chain(second) {
@ -124,85 +124,18 @@ pub fn align(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
Value::template(move |ctx| {
if let Some(horizontal) = horizontal {
ctx.state.aligns.cross = horizontal.to_align(ctx.state.dir);
ctx.state.aligns.cross = horizontal;
}
if let Some(vertical) = vertical {
let new = vertical.to_align(Dir::TTB);
if ctx.state.aligns.main != new {
ctx.state.aligns.main = new;
ctx.parbreak();
}
ctx.state.aligns.main = vertical;
ctx.parbreak();
}
body.exec(ctx);
})
}
/// An alignment specifier passed to `align`.
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub(super) enum AlignValue {
Start,
Center,
End,
Left,
Right,
Top,
Bottom,
}
impl AlignValue {
fn axis(self) -> Option<SpecAxis> {
match self {
Self::Start => None,
Self::Center => None,
Self::End => None,
Self::Left => Some(SpecAxis::Horizontal),
Self::Right => Some(SpecAxis::Horizontal),
Self::Top => Some(SpecAxis::Vertical),
Self::Bottom => Some(SpecAxis::Vertical),
}
}
fn to_align(self, dir: Dir) -> Align {
let side = |is_at_positive_start| {
if dir.is_positive() == is_at_positive_start {
Align::Start
} else {
Align::End
}
};
match self {
Self::Start => Align::Start,
Self::Center => Align::Center,
Self::End => Align::End,
Self::Left => side(true),
Self::Right => side(false),
Self::Top => side(true),
Self::Bottom => side(false),
}
}
}
impl Display for AlignValue {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(match self {
Self::Start => "start",
Self::Center => "center",
Self::End => "end",
Self::Left => "left",
Self::Right => "right",
Self::Top => "top",
Self::Bottom => "bottom",
})
}
}
dynamic! {
AlignValue: "alignment",
}
/// `box`: Place content in a rectangular box.
pub fn boxed(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let width = args.named(ctx, "width");
@ -247,7 +180,7 @@ pub fn pad(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
/// `stack`: Stack children along an axis.
pub fn stack(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let dir = args.named::<Dir>(ctx, "dir").unwrap_or(Dir::TTB);
let dir = args.named(ctx, "dir").unwrap_or(Dir::TTB);
let children: Vec<_> = args.all().collect();
Value::template(move |ctx| {
@ -272,10 +205,10 @@ pub fn grid(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let columns = args.named::<Tracks>(ctx, "columns").unwrap_or_default();
let rows = args.named::<Tracks>(ctx, "rows").unwrap_or_default();
let gutter_columns = args.named::<Tracks>(ctx, "gutter-columns");
let gutter_rows = args.named::<Tracks>(ctx, "gutter-rows");
let gutter_columns = args.named(ctx, "gutter-columns");
let gutter_rows = args.named(ctx, "gutter-rows");
let gutter = args
.named::<Linear>(ctx, "gutter")
.named(ctx, "gutter")
.map(|v| vec![TrackSizing::Linear(v)])
.unwrap_or_default();

View File

@ -13,13 +13,13 @@ pub use layout::*;
pub use text::*;
pub use utility::*;
use std::fmt::{self, Display, Formatter};
use std::convert::TryFrom;
use std::rc::Rc;
use crate::color::{Color, RgbaColor};
use crate::eval::{EvalContext, FuncArgs, Scope, Template, Type, Value};
use crate::exec::{Exec, FontFamily};
use crate::font::{FontStyle, FontWeight, VerticalFontMetric};
use crate::exec::Exec;
use crate::font::{FontFamily, FontStretch, FontStyle, FontWeight, VerticalFontMetric};
use crate::geom::*;
use crate::syntax::Spanned;
use crate::util::EcoString;
@ -71,17 +71,17 @@ pub fn new() -> Scope {
std.def_const("forest", RgbaColor::new(0x43, 0xA1, 0x27, 0xFF));
// Arbitrary constants.
std.def_const("start", AlignValue::Start);
std.def_const("center", AlignValue::Center);
std.def_const("end", AlignValue::End);
std.def_const("left", AlignValue::Left);
std.def_const("right", AlignValue::Right);
std.def_const("top", AlignValue::Top);
std.def_const("bottom", AlignValue::Bottom);
std.def_const("ltr", Dir::LTR);
std.def_const("rtl", Dir::RTL);
std.def_const("ttb", Dir::TTB);
std.def_const("btt", Dir::BTT);
std.def_const("start", Align::Start);
std.def_const("center", Align::Center);
std.def_const("end", Align::End);
std.def_const("left", Align::Left);
std.def_const("right", Align::Right);
std.def_const("top", Align::Top);
std.def_const("bottom", Align::Bottom);
std.def_const("serif", FontFamily::Serif);
std.def_const("sans-serif", FontFamily::SansSerif);
std.def_const("monospace", FontFamily::Monospace);
@ -102,3 +102,32 @@ pub fn new() -> Scope {
dynamic! {
Dir: "direction",
}
dynamic! {
Align: "alignment",
}
dynamic! {
FontFamily: "font family",
Value::Str(string) => Self::Named(string.to_lowercase()),
}
dynamic! {
FontStyle: "font style",
}
dynamic! {
FontWeight: "font weight",
Value::Int(number) => {
u16::try_from(number).map_or(Self::BLACK, Self::from_number)
},
}
dynamic! {
FontStretch: "font stretch",
Value::Relative(relative) => Self::from_ratio(relative.get() as f32),
}
dynamic! {
VerticalFontMetric: "vertical font metric",
}

View File

@ -1,7 +1,4 @@
use std::convert::TryFrom;
use crate::exec::{LineState, TextState};
use crate::font::{FontStretch, FontStyle, FontWeight};
use crate::layout::Paint;
use super::*;
@ -15,7 +12,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
Some(FontDef(families))
};
let size = args.eat().or_else(|| args.named::<Linear>(ctx, "size"));
let size = args.eat::<Linear>().or_else(|| args.named(ctx, "size"));
let style = args.named(ctx, "style");
let weight = args.named(ctx, "weight");
let stretch = args.named(ctx, "stretch");
@ -104,31 +101,6 @@ castable! {
),
}
dynamic! {
FontFamily: "font family",
Value::Str(string) => Self::Named(string.to_lowercase()),
}
dynamic! {
FontStyle: "font style",
}
dynamic! {
FontWeight: "font weight",
Value::Int(number) => {
u16::try_from(number).map_or(Self::BLACK, Self::from_number)
},
}
dynamic! {
FontStretch: "font stretch",
Value::Relative(relative) => Self::from_ratio(relative.get() as f32),
}
dynamic! {
VerticalFontMetric: "vertical font metric",
}
/// `par`: Configure paragraphs.
pub fn par(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
let par_spacing = args.named(ctx, "spacing");
@ -209,7 +181,7 @@ fn line_impl(
substate: fn(&mut TextState) -> &mut Option<Rc<LineState>>,
) -> Value {
let stroke = args.eat().or_else(|| args.named(ctx, "stroke"));
let thickness = args.eat().or_else(|| args.named::<Linear>(ctx, "thickness"));
let thickness = args.eat::<Linear>().or_else(|| args.named(ctx, "thickness"));
let offset = args.named(ctx, "offset");
let extent = args.named(ctx, "extent").unwrap_or_default();
let body = args.expect::<Template>(ctx, "body").unwrap_or_default();

View File

@ -24,7 +24,7 @@ pub fn repr(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
/// `len`: The length of a string, an array or a dictionary.
pub fn len(ctx: &mut EvalContext, args: &mut FuncArgs) -> Value {
match args.expect::<Spanned<Value>>(ctx, "collection") {
match args.expect(ctx, "collection") {
Some(Spanned { v: Value::Str(v), .. }) => Value::Int(v.len() as i64),
Some(Spanned { v: Value::Array(v), .. }) => Value::Int(v.len() as i64),
Some(Spanned { v: Value::Dict(v), .. }) => Value::Int(v.len() as i64),