mirror of
https://github.com/typst/typst
synced 2025-05-14 04:56:26 +08:00
Move font family and refactor alignment
This commit is contained in:
parent
853361338b
commit
7d15dc634b
@ -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 {
|
||||
|
20
src/font.rs
20
src/font.rs
@ -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 {
|
||||
|
@ -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",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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",
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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),
|
||||
|
Loading…
x
Reference in New Issue
Block a user