Unify font and page functions 💕
- Removes font weight and width warnings for now, will be added again later - Adds a bit hacky get_first function for tuples, will be refactored soon anyway
This commit is contained in:
parent
659248d52f
commit
efb78831a7
@ -3,24 +3,33 @@ use crate::length::ScaleLength;
|
||||
use super::*;
|
||||
|
||||
function! {
|
||||
/// `font.family`: Set the font family.
|
||||
/// `font`: Configure the font.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FontFamilyFunc {
|
||||
pub struct FontFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
size: Option<ScaleLength>,
|
||||
style: Option<FontStyle>,
|
||||
weight: Option<FontWeight>,
|
||||
width: Option<FontWidth>,
|
||||
list: Vec<String>,
|
||||
classes: Vec<(String, Vec<String>)>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
let size = header.args.pos.get_first::<ScaleLength>(&mut f.diagnostics);
|
||||
|
||||
let style = header.args.key.get::<FontStyle>(&mut f.diagnostics, "style");
|
||||
let weight = header.args.key.get::<FontWeight>(&mut f.diagnostics, "weight");
|
||||
let width = header.args.key.get::<FontWidth>(&mut f.diagnostics, "width");
|
||||
|
||||
let list = header.args.pos.get_all::<StringLike>(&mut f.diagnostics)
|
||||
.map(|s| s.0.to_lowercase())
|
||||
.collect();
|
||||
|
||||
let tuples: Vec<_> = header.args.key
|
||||
let classes = header.args.key
|
||||
.get_all::<String, Tuple>(&mut f.diagnostics)
|
||||
.collect();
|
||||
|
||||
let classes = tuples.into_iter()
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(|(class, mut tuple)| {
|
||||
let fallback = tuple.get_all::<StringLike>(&mut f.diagnostics)
|
||||
.map(|s| s.0.to_lowercase())
|
||||
@ -29,140 +38,41 @@ function! {
|
||||
})
|
||||
.collect();
|
||||
|
||||
FontFamilyFunc {
|
||||
FontFunc {
|
||||
body: body!(opt: body, ctx, f),
|
||||
size,
|
||||
list,
|
||||
classes,
|
||||
style,
|
||||
weight,
|
||||
width,
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
styled(&self.body, ctx, Some(()),
|
||||
|s, _| {
|
||||
|t, _| {
|
||||
self.size.with(|s| match s {
|
||||
ScaleLength::Absolute(length) => {
|
||||
t.base_font_size = length.as_raw();
|
||||
t.font_scale = 1.0;
|
||||
}
|
||||
ScaleLength::Scaled(scale) => t.font_scale = scale,
|
||||
});
|
||||
|
||||
self.style.with(|s| t.variant.style = s);
|
||||
self.weight.with(|w| t.variant.weight = w);
|
||||
self.width.with(|w| t.variant.width = w);
|
||||
|
||||
if !self.list.is_empty() {
|
||||
*s.fallback.list_mut() = self.list.clone();
|
||||
*t.fallback.list_mut() = self.list.clone();
|
||||
}
|
||||
|
||||
for (class, fallback) in &self.classes {
|
||||
s.fallback.set_class_list(class.clone(), fallback.clone());
|
||||
t.fallback.set_class_list(class.clone(), fallback.clone());
|
||||
}
|
||||
|
||||
s.fallback.flatten();
|
||||
t.fallback.flatten();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function! {
|
||||
/// `font.style`: Set the font style (normal / italic).
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FontStyleFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
style: Option<FontStyle>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
FontStyleFunc {
|
||||
body: body!(opt: body, ctx, f),
|
||||
style: header.args.pos.get::<FontStyle>(&mut f.diagnostics)
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "style"),
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
styled(&self.body, ctx, self.style, |t, s| t.variant.style = s)
|
||||
}
|
||||
}
|
||||
|
||||
function! {
|
||||
/// `font.weight`: Set text with a given weight.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FontWeightFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
weight: Option<FontWeight>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
let body = body!(opt: body, ctx, f);
|
||||
let weight = header.args.pos.get::<Spanned<(FontWeight, bool)>>(&mut f.diagnostics)
|
||||
.map(|Spanned { v: (weight, is_clamped), span }| {
|
||||
if is_clamped {
|
||||
warning!(
|
||||
@f, span,
|
||||
"weight should be between 100 and 900, clamped to {}",
|
||||
weight.0,
|
||||
);
|
||||
}
|
||||
|
||||
weight
|
||||
})
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "weight");
|
||||
|
||||
FontWeightFunc { body, weight }
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
styled(&self.body, ctx, self.weight, |t, w| t.variant.weight = w)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function! {
|
||||
/// `font.width`: Set text with a given width.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FontWidthFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
width: Option<FontWidth>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
let body = body!(opt: body, ctx, f);
|
||||
let width = header.args.pos.get::<Spanned<(FontWidth, bool)>>(&mut f.diagnostics)
|
||||
.map(|Spanned { v: (width, is_clamped), span }| {
|
||||
if is_clamped {
|
||||
warning!(
|
||||
@f, span,
|
||||
"width should be between 1 and 9, clamped to {}",
|
||||
width.to_number(),
|
||||
);
|
||||
}
|
||||
|
||||
width
|
||||
})
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "width");
|
||||
|
||||
FontWidthFunc { body, width }
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
styled(&self.body, ctx, self.width, |t, w| t.variant.width = w)
|
||||
}
|
||||
}
|
||||
|
||||
function! {
|
||||
/// `font.size`: Sets the font size.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct FontSizeFunc {
|
||||
body: Option<SyntaxModel>,
|
||||
size: Option<ScaleLength>,
|
||||
}
|
||||
|
||||
parse(header, body, ctx, f) {
|
||||
FontSizeFunc {
|
||||
body: body!(opt: body, ctx, f),
|
||||
size: header.args.pos.get::<ScaleLength>(&mut f.diagnostics)
|
||||
.or_missing(&mut f.diagnostics, header.name.span, "size")
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
styled(&self.body, ctx, self.size, |t, s| {
|
||||
match s {
|
||||
ScaleLength::Absolute(length) => {
|
||||
t.base_font_size = length.as_raw();
|
||||
t.font_scale = 1.0;
|
||||
}
|
||||
ScaleLength::Scaled(scale) => t.font_scale = scale,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -16,11 +16,7 @@ pub fn std() -> Scope {
|
||||
std.add::<ValFunc>("val");
|
||||
|
||||
// Font setup
|
||||
std.add::<FontFamilyFunc>("font.family");
|
||||
std.add::<FontStyleFunc>("font.style");
|
||||
std.add::<FontWeightFunc>("font.weight");
|
||||
std.add::<FontWidthFunc>("font.width");
|
||||
std.add::<FontSizeFunc>("font.size");
|
||||
std.add::<FontFunc>("font");
|
||||
std.add_with_meta::<ContentSpacingFunc>("word.spacing", ContentKind::Word);
|
||||
|
||||
// Layout
|
||||
@ -40,8 +36,7 @@ pub fn std() -> Scope {
|
||||
std.add_with_meta::<SpacingFunc>("v", Some(Vertical));
|
||||
|
||||
// Page setup
|
||||
std.add::<PageSizeFunc>("page.size");
|
||||
std.add::<PageMarginsFunc>("page.margins");
|
||||
std.add::<PageFunc>("page");
|
||||
|
||||
std
|
||||
}
|
||||
|
@ -3,19 +3,21 @@ use crate::paper::{Paper, PaperClass};
|
||||
use super::*;
|
||||
|
||||
function! {
|
||||
/// `page.size`: Set the size of pages.
|
||||
/// `page`: Configure pages.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PageSizeFunc {
|
||||
pub struct PageFunc {
|
||||
paper: Option<Paper>,
|
||||
extents: AxisMap<Length>,
|
||||
padding: PaddingMap,
|
||||
flip: bool,
|
||||
}
|
||||
|
||||
parse(header, body, state, f) {
|
||||
body!(nope: body, f);
|
||||
PageSizeFunc {
|
||||
PageFunc {
|
||||
paper: header.args.pos.get::<Paper>(&mut f.diagnostics),
|
||||
extents: AxisMap::parse::<ExtentKey>(&mut f.diagnostics, &mut header.args.key),
|
||||
padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args),
|
||||
flip: header.args.key.get::<bool>(&mut f.diagnostics, "flip").unwrap_or(false),
|
||||
}
|
||||
}
|
||||
@ -38,27 +40,8 @@ function! {
|
||||
style.dimensions.swap();
|
||||
}
|
||||
|
||||
vec![SetPageStyle(style)]
|
||||
}
|
||||
}
|
||||
|
||||
function! {
|
||||
/// `page.margins`: Sets the page margins.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct PageMarginsFunc {
|
||||
padding: PaddingMap,
|
||||
}
|
||||
|
||||
parse(header, body, state, f) {
|
||||
body!(nope: body, f);
|
||||
PageMarginsFunc {
|
||||
padding: PaddingMap::parse(&mut f.diagnostics, &mut header.args),
|
||||
}
|
||||
}
|
||||
|
||||
layout(self, ctx, f) {
|
||||
let mut style = ctx.style.page;
|
||||
self.padding.apply(&mut f.diagnostics, ctx.axes, &mut style.margins);
|
||||
|
||||
vec![SetPageStyle(style)]
|
||||
}
|
||||
}
|
||||
|
@ -268,6 +268,25 @@ impl Tuple {
|
||||
None
|
||||
}
|
||||
|
||||
/// Extract (and remove) the first matching value without removing and
|
||||
/// generating diagnostics for all previous items that did not match.
|
||||
pub fn get_first<V: Value>(&mut self, _: &mut Diagnostics) -> Option<V> {
|
||||
let mut i = 0;
|
||||
while i < self.items.len() {
|
||||
let expr = self.items[i].clone();
|
||||
match V::parse(expr) {
|
||||
Ok(output) => {
|
||||
self.items.remove(i);
|
||||
return Some(output)
|
||||
}
|
||||
Err(_) => {},
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Extract and return an iterator over all values that match and generate
|
||||
/// diagnostics for all items that do not match.
|
||||
pub fn get_all<'a, V: Value>(&'a mut self, diagnostics: &'a mut Diagnostics)
|
||||
|
@ -178,7 +178,7 @@ impl PaddingMap {
|
||||
pub fn parse(diagnostics: &mut Diagnostics, args: &mut FuncArgs) -> PaddingMap {
|
||||
let mut map = DedupMap::new();
|
||||
|
||||
let all = args.pos.get::<Spanned<Defaultable<ScaleLength>>>(diagnostics);
|
||||
let all = args.key.get::<Spanned<Defaultable<ScaleLength>>>(diagnostics, "margins");
|
||||
if let Some(Spanned { v, span }) = all {
|
||||
map.insert(diagnostics, Spanned { v: (PaddingKey::All, v.into()), span });
|
||||
}
|
||||
|
@ -82,13 +82,22 @@ pub enum FuncArg {
|
||||
}
|
||||
|
||||
/// Extra methods on [`Options`](Option) used for argument parsing.
|
||||
pub trait OptionExt: Sized {
|
||||
pub trait OptionExt<T>: Sized {
|
||||
/// Calls `f` with `val` if this is `Some(val)`.
|
||||
fn with(self, f: impl FnOnce(T));
|
||||
|
||||
/// Add an error about a missing argument `arg` with the given span if the
|
||||
/// option is `None`.
|
||||
fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self;
|
||||
}
|
||||
|
||||
impl<T> OptionExt for Option<T> {
|
||||
impl<T> OptionExt<T> for Option<T> {
|
||||
fn with(self, f: impl FnOnce(T)) {
|
||||
if let Some(val) = self {
|
||||
f(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn or_missing(self, diagnostics: &mut Diagnostics, span: Span, arg: &str) -> Self {
|
||||
if self.is_none() {
|
||||
diagnostics.push(error!(span, "missing argument: {}", arg));
|
||||
|
@ -143,22 +143,21 @@ impl Value for FontStyle {
|
||||
|
||||
/// The additional boolean specifies whether a number was clamped into the range
|
||||
/// 100 - 900 to make it a valid font weight.
|
||||
impl Value for (FontWeight, bool) {
|
||||
impl Value for FontWeight {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||
match expr.v {
|
||||
Expr::Number(weight) => {
|
||||
let weight = weight.round();
|
||||
if weight >= 100.0 && weight <= 900.0 {
|
||||
Ok((FontWeight(weight as u16), false))
|
||||
Ok(FontWeight(weight as u16))
|
||||
} else {
|
||||
let clamped = weight.min(900.0).max(100.0);
|
||||
Ok((FontWeight(clamped as u16), true))
|
||||
Ok(FontWeight(clamped as u16))
|
||||
}
|
||||
}
|
||||
Expr::Ident(id) => {
|
||||
FontWeight::from_name(id.as_str())
|
||||
.ok_or_else(|| error!("invalid font weight"))
|
||||
.map(|weight| (weight, false))
|
||||
}
|
||||
other => Err(
|
||||
error!("expected identifier or number, found {}", other.name())
|
||||
@ -169,22 +168,21 @@ impl Value for (FontWeight, bool) {
|
||||
|
||||
/// The additional boolean specifies whether a number was clamped into the range
|
||||
/// 1 - 9 to make it a valid font width.
|
||||
impl Value for (FontWidth, bool) {
|
||||
impl Value for FontWidth {
|
||||
fn parse(expr: Spanned<Expr>) -> Result<Self, Diagnostic> {
|
||||
match expr.v {
|
||||
Expr::Number(width) => {
|
||||
let width = width.round();
|
||||
if width >= 1.0 && width <= 9.0 {
|
||||
Ok((FontWidth::new(width as u16).unwrap(), false))
|
||||
Ok(FontWidth::new(width as u16).unwrap())
|
||||
} else {
|
||||
let clamped = width.min(9.0).max(1.0);
|
||||
Ok((FontWidth::new(clamped as u16).unwrap(), true))
|
||||
Ok(FontWidth::new(clamped as u16).unwrap())
|
||||
}
|
||||
}
|
||||
Expr::Ident(id) => {
|
||||
FontWidth::from_name(id.as_str())
|
||||
.ok_or_else(|| error!("invalid font width"))
|
||||
.map(|width| (width, false))
|
||||
}
|
||||
other => Err(
|
||||
error!("expected identifier or number, found {}", other.name())
|
||||
|
@ -1,5 +1,4 @@
|
||||
[page.size: width=450pt, height=300pt]
|
||||
[page.margins: 1cm]
|
||||
[page: width=450pt, height=300pt, margins=1cm]
|
||||
|
||||
[box][
|
||||
*Technische Universität Berlin* [n]
|
||||
|
@ -1,5 +1,4 @@
|
||||
[page.size: w=5cm, h=5cm]
|
||||
[page.margins: 0cm]
|
||||
[page: w=5cm, h=5cm, margins=0cm]
|
||||
|
||||
// Test 1
|
||||
[box: w=1, h=1, debug=false][
|
||||
|
Loading…
Reference in New Issue
Block a user