Tidying
This commit is contained in:
parent
9624ad635b
commit
bd304b99e5
@ -94,10 +94,6 @@ pub fn add(lhs: Value, rhs: Value) -> StrResult<Value> {
|
||||
if let (Some(&a), Some(&b)) =
|
||||
(a.downcast::<Align>(), b.downcast::<Align>())
|
||||
{
|
||||
dynamic! {
|
||||
Spec<Align>: "2d alignment",
|
||||
}
|
||||
|
||||
return if a.axis() != b.axis() {
|
||||
Ok(Dyn(Dynamic::new(match a.axis() {
|
||||
SpecAxis::Horizontal => Spec { x: a, y: b },
|
||||
|
@ -1,9 +1,11 @@
|
||||
//! Aligning nodes in their parent container.
|
||||
|
||||
use super::prelude::*;
|
||||
use super::ParNode;
|
||||
|
||||
/// `align`: Configure the alignment along the layouting axes.
|
||||
pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let aligns: Spec<_> = args.expect("alignment")?;
|
||||
let aligns: Spec<_> = args.find().unwrap_or_default();
|
||||
let body: Node = args.expect("body")?;
|
||||
|
||||
let mut styles = Styles::new();
|
||||
@ -16,6 +18,26 @@ pub fn align(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
))
|
||||
}
|
||||
|
||||
dynamic! {
|
||||
Align: "alignment",
|
||||
}
|
||||
|
||||
dynamic! {
|
||||
Spec<Align>: "2d alignment",
|
||||
}
|
||||
|
||||
castable! {
|
||||
Spec<Option<Align>>,
|
||||
Expected: "1d or 2d alignment",
|
||||
@align: Align => {
|
||||
let mut aligns = Spec::default();
|
||||
aligns.set(align.axis(), Some(*align));
|
||||
aligns
|
||||
},
|
||||
@aligns: Spec<Align> => aligns.map(Some),
|
||||
|
||||
}
|
||||
|
||||
/// A node that aligns its child.
|
||||
#[derive(Debug, Hash)]
|
||||
pub struct AlignNode {
|
||||
@ -57,19 +79,3 @@ impl Layout for AlignNode {
|
||||
frames
|
||||
}
|
||||
}
|
||||
|
||||
dynamic! {
|
||||
Align: "alignment",
|
||||
}
|
||||
|
||||
castable! {
|
||||
Spec<Option<Align>>,
|
||||
Expected: "1d or 2d alignment",
|
||||
@align: Align => {
|
||||
let mut aligns = Spec::default();
|
||||
aligns.set(align.axis(), Some(*align));
|
||||
aligns
|
||||
},
|
||||
@aligns: Spec<Align> => aligns.map(Some),
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
//! Multi-column layouts.
|
||||
|
||||
use super::prelude::*;
|
||||
use super::ParNode;
|
||||
|
||||
/// `columns`: Stack children along an axis.
|
||||
/// `columns`: Set content into multiple columns.
|
||||
pub fn columns(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let columns = args.expect("column count")?;
|
||||
let gutter = args.named("gutter")?.unwrap_or(Relative::new(0.04).into());
|
||||
@ -36,6 +38,8 @@ impl Layout for ColumnsNode {
|
||||
ctx: &mut LayoutContext,
|
||||
regions: &Regions,
|
||||
) -> Vec<Constrained<Rc<Frame>>> {
|
||||
let columns = self.columns.get();
|
||||
|
||||
// Separating the infinite space into infinite columns does not make
|
||||
// much sense. Note that this line assumes that no infinitely wide
|
||||
// region will follow if the first region's width is finite.
|
||||
@ -51,21 +55,15 @@ impl Layout for ColumnsNode {
|
||||
// `region.backlog` and `regions.last`.
|
||||
let mut sizes = vec![];
|
||||
|
||||
let columns = self.columns.get();
|
||||
|
||||
for (current, base) in regions
|
||||
.iter()
|
||||
.take(1 + regions.backlog.len() + if regions.last.is_some() { 1 } else { 0 })
|
||||
.take(1 + regions.backlog.len() + regions.last.iter().len())
|
||||
{
|
||||
let gutter = self.gutter.resolve(base.x);
|
||||
let width = (current.x - gutter * (columns - 1) as f64) / columns as f64;
|
||||
let size = Size::new(width, current.y);
|
||||
gutters.push(gutter);
|
||||
let size = Size::new(
|
||||
(current.x - gutter * (columns - 1) as f64) / columns as f64,
|
||||
current.y,
|
||||
);
|
||||
for _ in 0 .. columns {
|
||||
sizes.push(size);
|
||||
}
|
||||
sizes.extend(std::iter::repeat(size).take(columns));
|
||||
}
|
||||
|
||||
let first = sizes.remove(0);
|
||||
@ -76,24 +74,22 @@ impl Layout for ColumnsNode {
|
||||
);
|
||||
|
||||
// Retrieve elements for the last region from the vectors.
|
||||
let last_gutter = if regions.last.is_some() {
|
||||
let last_gutter = regions.last.map(|_| {
|
||||
let gutter = gutters.pop().unwrap();
|
||||
let size = sizes.drain(sizes.len() - columns ..).next().unwrap();
|
||||
pod.last = Some(size);
|
||||
Some(gutter)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
gutter
|
||||
});
|
||||
|
||||
pod.backlog = sizes.into_iter();
|
||||
|
||||
let mut finished = vec![];
|
||||
let mut frames = self.child.layout(ctx, &pod).into_iter();
|
||||
|
||||
let dir = ctx.styles.get(ParNode::DIR);
|
||||
|
||||
let mut finished = vec![];
|
||||
let total_regions = (frames.len() as f32 / columns as f32).ceil() as usize;
|
||||
|
||||
// Stitch together the columns for each region.
|
||||
for ((current, base), gutter) in regions
|
||||
.iter()
|
||||
.take(total_regions)
|
||||
@ -103,41 +99,35 @@ impl Layout for ColumnsNode {
|
||||
// Otherwise its the maximum column height for the frame. In that
|
||||
// case, the frame is first created with zero height and then
|
||||
// resized.
|
||||
let mut height = if regions.expand.y { current.y } else { Length::zero() };
|
||||
let mut frame = Frame::new(Spec::new(regions.current.x, height));
|
||||
|
||||
let height = if regions.expand.y { current.y } else { Length::zero() };
|
||||
let mut output = Frame::new(Spec::new(regions.current.x, height));
|
||||
let mut cursor = Length::zero();
|
||||
|
||||
for _ in 0 .. columns {
|
||||
let child_frame = match frames.next() {
|
||||
let frame = match frames.next() {
|
||||
Some(frame) => frame.item,
|
||||
None => break,
|
||||
};
|
||||
|
||||
let width = child_frame.size.x;
|
||||
|
||||
if !regions.expand.y {
|
||||
height.set_max(child_frame.size.y);
|
||||
output.size.y.set_max(frame.size.y);
|
||||
}
|
||||
|
||||
frame.push_frame(
|
||||
Point::with_x(if dir.is_positive() {
|
||||
cursor
|
||||
} else {
|
||||
regions.current.x - cursor - width
|
||||
}),
|
||||
child_frame,
|
||||
);
|
||||
let width = frame.size.x;
|
||||
let x = if dir.is_positive() {
|
||||
cursor
|
||||
} else {
|
||||
regions.current.x - cursor - width
|
||||
};
|
||||
|
||||
output.push_frame(Point::with_x(x), frame);
|
||||
cursor += width + gutter;
|
||||
}
|
||||
|
||||
frame.size.y = height;
|
||||
|
||||
let mut cts = Constraints::new(regions.expand);
|
||||
cts.base = base.map(Some);
|
||||
cts.exact = current.map(Some);
|
||||
finished.push(frame.constrain(cts));
|
||||
finished.push(output.constrain(cts));
|
||||
}
|
||||
|
||||
finished
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! A flow of paragraphs and other block-level nodes.
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use super::prelude::*;
|
||||
|
@ -1,47 +1,20 @@
|
||||
//! Layout along a row and column raster.
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
/// `grid`: Arrange children into a grid.
|
||||
pub fn grid(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
castable! {
|
||||
Vec<TrackSizing>,
|
||||
Expected: "integer or (auto, linear, fractional, or array thereof)",
|
||||
Value::Auto => vec![TrackSizing::Auto],
|
||||
Value::Length(v) => vec![TrackSizing::Linear(v.into())],
|
||||
Value::Relative(v) => vec![TrackSizing::Linear(v.into())],
|
||||
Value::Linear(v) => vec![TrackSizing::Linear(v)],
|
||||
Value::Fractional(v) => vec![TrackSizing::Fractional(v)],
|
||||
Value::Int(v) => vec![TrackSizing::Auto; Value::Int(v).cast()?],
|
||||
Value::Array(values) => values
|
||||
.into_iter()
|
||||
.filter_map(|v| v.cast().ok())
|
||||
.collect(),
|
||||
}
|
||||
|
||||
castable! {
|
||||
TrackSizing,
|
||||
Expected: "auto, linear, or fractional",
|
||||
Value::Auto => Self::Auto,
|
||||
Value::Length(v) => Self::Linear(v.into()),
|
||||
Value::Relative(v) => Self::Linear(v.into()),
|
||||
Value::Linear(v) => Self::Linear(v),
|
||||
Value::Fractional(v) => Self::Fractional(v),
|
||||
}
|
||||
|
||||
let columns = args.named("columns")?.unwrap_or_default();
|
||||
let rows = args.named("rows")?.unwrap_or_default();
|
||||
let tracks = Spec::new(columns, rows);
|
||||
|
||||
let base_gutter: Vec<TrackSizing> = args.named("gutter")?.unwrap_or_default();
|
||||
let column_gutter = args.named("column-gutter")?;
|
||||
let row_gutter = args.named("row-gutter")?;
|
||||
let gutter = Spec::new(
|
||||
column_gutter.unwrap_or_else(|| base_gutter.clone()),
|
||||
row_gutter.unwrap_or(base_gutter),
|
||||
);
|
||||
|
||||
Ok(Value::block(GridNode {
|
||||
tracks,
|
||||
gutter,
|
||||
tracks: Spec::new(columns, rows),
|
||||
gutter: Spec::new(
|
||||
column_gutter.unwrap_or_else(|| base_gutter.clone()),
|
||||
row_gutter.unwrap_or(base_gutter),
|
||||
),
|
||||
children: args.all().map(Node::into_block).collect(),
|
||||
}))
|
||||
}
|
||||
@ -57,17 +30,6 @@ pub struct GridNode {
|
||||
pub children: Vec<PackedNode>,
|
||||
}
|
||||
|
||||
/// Defines how to size a grid cell along an axis.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum TrackSizing {
|
||||
/// Fit the cell to its contents.
|
||||
Auto,
|
||||
/// A length stated in absolute values and/or relative to the parent's size.
|
||||
Linear(Linear),
|
||||
/// A length that is the fraction of the remaining free space in the parent.
|
||||
Fractional(Fractional),
|
||||
}
|
||||
|
||||
impl Layout for GridNode {
|
||||
fn layout(
|
||||
&self,
|
||||
@ -85,6 +47,42 @@ impl Layout for GridNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines how to size a grid cell along an axis.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum TrackSizing {
|
||||
/// Fit the cell to its contents.
|
||||
Auto,
|
||||
/// A length stated in absolute values and/or relative to the parent's size.
|
||||
Linear(Linear),
|
||||
/// A length that is the fraction of the remaining free space in the parent.
|
||||
Fractional(Fractional),
|
||||
}
|
||||
|
||||
castable! {
|
||||
Vec<TrackSizing>,
|
||||
Expected: "integer or (auto, linear, fractional, or array thereof)",
|
||||
Value::Auto => vec![TrackSizing::Auto],
|
||||
Value::Length(v) => vec![TrackSizing::Linear(v.into())],
|
||||
Value::Relative(v) => vec![TrackSizing::Linear(v.into())],
|
||||
Value::Linear(v) => vec![TrackSizing::Linear(v)],
|
||||
Value::Fractional(v) => vec![TrackSizing::Fractional(v)],
|
||||
Value::Int(v) => vec![TrackSizing::Auto; Value::Int(v).cast()?],
|
||||
Value::Array(values) => values
|
||||
.into_iter()
|
||||
.filter_map(|v| v.cast().ok())
|
||||
.collect(),
|
||||
}
|
||||
|
||||
castable! {
|
||||
TrackSizing,
|
||||
Expected: "auto, linear, or fractional",
|
||||
Value::Auto => Self::Auto,
|
||||
Value::Length(v) => Self::Linear(v.into()),
|
||||
Value::Relative(v) => Self::Linear(v.into()),
|
||||
Value::Linear(v) => Self::Linear(v),
|
||||
Value::Fractional(v) => Self::Fractional(v),
|
||||
}
|
||||
|
||||
/// Performs grid layout.
|
||||
struct GridLayouter<'a> {
|
||||
/// The children of the grid.
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Document-structuring section headings.
|
||||
|
||||
use super::prelude::*;
|
||||
use super::{FontFamily, TextNode};
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Raster and vector graphics.
|
||||
|
||||
use std::io;
|
||||
|
||||
use super::prelude::*;
|
||||
@ -106,6 +108,12 @@ pub enum ImageFit {
|
||||
Stretch,
|
||||
}
|
||||
|
||||
impl Default for ImageFit {
|
||||
fn default() -> Self {
|
||||
Self::Cover
|
||||
}
|
||||
}
|
||||
|
||||
castable! {
|
||||
ImageFit,
|
||||
Expected: "string",
|
||||
@ -116,9 +124,3 @@ castable! {
|
||||
_ => Err(r#"expected "cover", "contain" or "stretch""#)?,
|
||||
},
|
||||
}
|
||||
|
||||
impl Default for ImageFit {
|
||||
fn default() -> Self {
|
||||
Self::Cover
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Hyperlinking.
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::util::EcoString;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Unordered (bulleted) and ordered (numbered) lists.
|
||||
|
||||
use std::hash::Hash;
|
||||
|
||||
use super::prelude::*;
|
||||
|
@ -3,44 +3,25 @@
|
||||
//! Call [`new`] to obtain a [`Scope`] containing all standard library
|
||||
//! definitions.
|
||||
|
||||
mod align;
|
||||
mod columns;
|
||||
mod flow;
|
||||
mod grid;
|
||||
mod heading;
|
||||
mod image;
|
||||
mod link;
|
||||
mod list;
|
||||
mod pad;
|
||||
mod page;
|
||||
mod par;
|
||||
mod placed;
|
||||
mod shape;
|
||||
mod sized;
|
||||
mod spacing;
|
||||
mod stack;
|
||||
mod text;
|
||||
mod transform;
|
||||
mod utility;
|
||||
|
||||
/// Helpful imports for creating library functionality.
|
||||
mod prelude {
|
||||
pub use std::fmt::{self, Debug, Formatter};
|
||||
pub use std::num::NonZeroUsize;
|
||||
pub use std::rc::Rc;
|
||||
|
||||
pub use typst_macros::properties;
|
||||
|
||||
pub use crate::diag::{At, TypResult};
|
||||
pub use crate::eval::{
|
||||
Args, Construct, EvalContext, Node, Property, Set, Smart, Styles, Value,
|
||||
};
|
||||
pub use crate::frame::*;
|
||||
pub use crate::geom::*;
|
||||
pub use crate::layout::*;
|
||||
pub use crate::syntax::{Span, Spanned};
|
||||
pub use crate::util::{EcoString, OptionExt};
|
||||
}
|
||||
pub mod align;
|
||||
pub mod columns;
|
||||
pub mod flow;
|
||||
pub mod grid;
|
||||
pub mod heading;
|
||||
pub mod image;
|
||||
pub mod link;
|
||||
pub mod list;
|
||||
pub mod pad;
|
||||
pub mod page;
|
||||
pub mod par;
|
||||
pub mod placed;
|
||||
pub mod shape;
|
||||
pub mod sized;
|
||||
pub mod spacing;
|
||||
pub mod stack;
|
||||
pub mod text;
|
||||
pub mod transform;
|
||||
pub mod utility;
|
||||
|
||||
pub use self::image::*;
|
||||
pub use align::*;
|
||||
@ -62,8 +43,37 @@ pub use text::*;
|
||||
pub use transform::*;
|
||||
pub use utility::*;
|
||||
|
||||
use crate::eval::{Scope, Value};
|
||||
use crate::geom::*;
|
||||
macro_rules! prelude {
|
||||
($($reexport:item)*) => {
|
||||
/// Helpful imports for creating library functionality.
|
||||
pub mod prelude {
|
||||
$(#[doc(no_inline)] $reexport)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
prelude! {
|
||||
pub use std::fmt::{self, Debug, Formatter};
|
||||
pub use std::num::NonZeroUsize;
|
||||
pub use std::rc::Rc;
|
||||
|
||||
pub use typst_macros::properties;
|
||||
|
||||
pub use crate::diag::{At, TypResult};
|
||||
pub use crate::eval::{
|
||||
Args, Construct, EvalContext, Node, Property, Set, Smart, Styles, Value,
|
||||
};
|
||||
pub use crate::frame::*;
|
||||
pub use crate::geom::*;
|
||||
pub use crate::layout::{
|
||||
Constrain, Constrained, Constraints, Layout, LayoutContext, PackedNode, Regions,
|
||||
};
|
||||
pub use crate::syntax::{Span, Spanned};
|
||||
pub use crate::util::{EcoString, OptionExt};
|
||||
}
|
||||
|
||||
use crate::eval::Scope;
|
||||
use prelude::*;
|
||||
|
||||
/// Construct a scope containing all standard library definitions.
|
||||
pub fn new() -> Scope {
|
||||
@ -78,9 +88,8 @@ pub fn new() -> Scope {
|
||||
std.def_class::<ListNode<Ordered>>("enum");
|
||||
|
||||
// Text functions.
|
||||
// TODO(style): These should be classes, once that works for inline nodes.
|
||||
std.def_func("strike", strike);
|
||||
std.def_func("underline", underline);
|
||||
std.def_func("strike", strike);
|
||||
std.def_func("overline", overline);
|
||||
std.def_func("link", link);
|
||||
|
||||
@ -93,8 +102,6 @@ pub fn new() -> Scope {
|
||||
std.def_func("v", v);
|
||||
|
||||
// Layout functions.
|
||||
// TODO(style): Decide which of these should be classes
|
||||
// (and which of their properties should be settable).
|
||||
std.def_func("box", box_);
|
||||
std.def_func("block", block);
|
||||
std.def_func("stack", stack);
|
||||
@ -160,12 +167,6 @@ dynamic! {
|
||||
Dir: "direction",
|
||||
}
|
||||
|
||||
castable! {
|
||||
Paint,
|
||||
Expected: "color",
|
||||
Value::Color(color) => Paint::Solid(color),
|
||||
}
|
||||
|
||||
castable! {
|
||||
usize,
|
||||
Expected: "non-negative integer",
|
||||
@ -173,14 +174,20 @@ castable! {
|
||||
}
|
||||
|
||||
castable! {
|
||||
prelude::NonZeroUsize,
|
||||
NonZeroUsize,
|
||||
Expected: "positive integer",
|
||||
Value::Int(int) => int
|
||||
.try_into()
|
||||
.and_then(|n: usize| n.try_into())
|
||||
.and_then(usize::try_into)
|
||||
.map_err(|_| "must be positive")?,
|
||||
}
|
||||
|
||||
castable! {
|
||||
Paint,
|
||||
Expected: "color",
|
||||
Value::Color(color) => Paint::Solid(color),
|
||||
}
|
||||
|
||||
castable! {
|
||||
String,
|
||||
Expected: "string",
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Surrounding nodes with extra space.
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
/// `pad`: Pad content at the sides.
|
||||
|
@ -1,4 +1,4 @@
|
||||
#![allow(unused)]
|
||||
//! Pages of paper.
|
||||
|
||||
use std::fmt::{self, Display, Formatter};
|
||||
use std::str::FromStr;
|
||||
@ -6,11 +6,6 @@ use std::str::FromStr;
|
||||
use super::prelude::*;
|
||||
use super::{ColumnsNode, PadNode};
|
||||
|
||||
/// `pagebreak`: Start a new page.
|
||||
pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::Node(Node::Pagebreak))
|
||||
}
|
||||
|
||||
/// Layouts its child onto one or multiple pages.
|
||||
#[derive(Clone, PartialEq, Hash)]
|
||||
pub struct PageNode {
|
||||
@ -42,7 +37,7 @@ impl PageNode {
|
||||
pub const FILL: Option<Paint> = None;
|
||||
/// How many columns the page has.
|
||||
pub const COLUMNS: NonZeroUsize = NonZeroUsize::new(1).unwrap();
|
||||
/// How many columns the page has.
|
||||
/// How much space is between the page's columns.
|
||||
pub const COLUMN_GUTTER: Linear = Relative::new(0.04).into();
|
||||
}
|
||||
|
||||
@ -74,11 +69,12 @@ impl Set for PageNode {
|
||||
}
|
||||
|
||||
let margins = args.named("margins")?;
|
||||
styles.set_opt(Self::FLIPPED, args.named("flipped")?);
|
||||
styles.set_opt(Self::LEFT, args.named("left")?.or(margins));
|
||||
styles.set_opt(Self::TOP, args.named("top")?.or(margins));
|
||||
styles.set_opt(Self::RIGHT, args.named("right")?.or(margins));
|
||||
styles.set_opt(Self::BOTTOM, args.named("bottom")?.or(margins));
|
||||
|
||||
styles.set_opt(Self::FLIPPED, args.named("flipped")?);
|
||||
styles.set_opt(Self::FILL, args.named("fill")?);
|
||||
styles.set_opt(Self::COLUMNS, args.named("columns")?);
|
||||
styles.set_opt(Self::COLUMN_GUTTER, args.named("column-gutter")?);
|
||||
@ -163,6 +159,36 @@ impl Debug for PageNode {
|
||||
}
|
||||
}
|
||||
|
||||
/// `pagebreak`: Start a new page.
|
||||
pub fn pagebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::Node(Node::Pagebreak))
|
||||
}
|
||||
|
||||
/// Defines default margins for a class of related papers.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum PaperClass {
|
||||
Custom,
|
||||
Base,
|
||||
US,
|
||||
Newspaper,
|
||||
Book,
|
||||
}
|
||||
|
||||
impl PaperClass {
|
||||
/// The default margins for this page class.
|
||||
fn default_margins(self) -> Sides<Linear> {
|
||||
let f = |r| Relative::new(r).into();
|
||||
let s = |l, t, r, b| Sides::new(f(l), f(t), f(r), f(b));
|
||||
match self {
|
||||
Self::Custom => s(0.1190, 0.0842, 0.1190, 0.0842),
|
||||
Self::Base => s(0.1190, 0.0842, 0.1190, 0.0842),
|
||||
Self::US => s(0.1760, 0.1092, 0.1760, 0.0910),
|
||||
Self::Newspaper => s(0.0455, 0.0587, 0.0455, 0.0294),
|
||||
Self::Book => s(0.1200, 0.0852, 0.1500, 0.0965),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Specification of a paper.
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Paper {
|
||||
@ -197,37 +223,6 @@ impl Default for Paper {
|
||||
}
|
||||
}
|
||||
|
||||
castable! {
|
||||
Paper,
|
||||
Expected: "string",
|
||||
Value::Str(string) => Paper::from_str(&string).map_err(|e| e.to_string())?,
|
||||
}
|
||||
|
||||
/// Defines default margins for a class of related papers.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum PaperClass {
|
||||
Custom,
|
||||
Base,
|
||||
US,
|
||||
Newspaper,
|
||||
Book,
|
||||
}
|
||||
|
||||
impl PaperClass {
|
||||
/// The default margins for this page class.
|
||||
fn default_margins(self) -> Sides<Linear> {
|
||||
let f = |r| Relative::new(r).into();
|
||||
let s = |l, t, r, b| Sides::new(f(l), f(t), f(r), f(b));
|
||||
match self {
|
||||
Self::Custom => s(0.1190, 0.0842, 0.1190, 0.0842),
|
||||
Self::Base => s(0.1190, 0.0842, 0.1190, 0.0842),
|
||||
Self::US => s(0.1760, 0.1092, 0.1760, 0.0910),
|
||||
Self::Newspaper => s(0.0455, 0.0587, 0.0455, 0.0294),
|
||||
Self::Book => s(0.1200, 0.0852, 0.1500, 0.0965),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines paper constants and a paper parsing implementation.
|
||||
macro_rules! papers {
|
||||
($(($var:ident: $class:ident, $width:expr, $height: expr, $($pats:tt)*))*) => {
|
||||
@ -252,18 +247,6 @@ macro_rules! papers {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The error when parsing a [`Paper`] from a string fails.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ParsePaperError;
|
||||
|
||||
impl Display for ParsePaperError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.pad("invalid paper name")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParsePaperError {}
|
||||
};
|
||||
}
|
||||
|
||||
@ -421,3 +404,21 @@ papers! {
|
||||
(PRESENTATION_16_9: Base, 297.0, 167.0625, "presentation-16-9")
|
||||
(PRESENTATION_4_3: Base, 280.0, 210.0, "presentation-4-3")
|
||||
}
|
||||
|
||||
castable! {
|
||||
Paper,
|
||||
Expected: "string",
|
||||
Value::Str(string) => Paper::from_str(&string).map_err(|e| e.to_string())?,
|
||||
}
|
||||
|
||||
/// The error when parsing a [`Paper`] from a string fails.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct ParsePaperError;
|
||||
|
||||
impl Display for ParsePaperError {
|
||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||
f.pad("invalid paper name")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for ParsePaperError {}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Paragraph layout.
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
use std::rc::Rc;
|
||||
|
||||
@ -9,16 +11,6 @@ use super::prelude::*;
|
||||
use super::{shape, ShapedText, SpacingKind, SpacingNode, TextNode};
|
||||
use crate::util::{EcoString, RangeExt, RcExt, SliceExt};
|
||||
|
||||
/// `parbreak`: Start a new paragraph.
|
||||
pub fn parbreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::Node(Node::Parbreak))
|
||||
}
|
||||
|
||||
/// `linebreak`: Start a new line.
|
||||
pub fn linebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::Node(Node::Linebreak))
|
||||
}
|
||||
|
||||
/// A node that arranges its children into a paragraph.
|
||||
#[derive(Hash)]
|
||||
pub struct ParNode(pub Vec<ParChild>);
|
||||
@ -62,17 +54,17 @@ impl Set for ParNode {
|
||||
dir = Some(v);
|
||||
}
|
||||
|
||||
let mut align = None;
|
||||
if let Some(Spanned { v, span }) = args.named::<Spanned<Align>>("align")? {
|
||||
if v.axis() != SpecAxis::Horizontal {
|
||||
bail!(span, "must be horizontal");
|
||||
}
|
||||
align = Some(v);
|
||||
}
|
||||
|
||||
if let (Some(dir), None) = (dir, align) {
|
||||
align = Some(if dir == Dir::LTR { Align::Left } else { Align::Right });
|
||||
}
|
||||
let align =
|
||||
if let Some(Spanned { v, span }) = args.named::<Spanned<Align>>("align")? {
|
||||
if v.axis() != SpecAxis::Horizontal {
|
||||
bail!(span, "must be horizontal");
|
||||
}
|
||||
Some(v)
|
||||
} else if let Some(dir) = dir {
|
||||
Some(if dir == Dir::LTR { Align::Left } else { Align::Right })
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
styles.set_opt(Self::DIR, dir);
|
||||
styles.set_opt(Self::ALIGN, align);
|
||||
@ -107,8 +99,7 @@ impl Layout for ParNode {
|
||||
impl ParNode {
|
||||
/// Concatenate all text in the paragraph into one string, replacing spacing
|
||||
/// with a space character and other non-text nodes with the object
|
||||
/// replacement character. Returns the full text alongside the range each
|
||||
/// child spans in the text.
|
||||
/// replacement character.
|
||||
fn collect_text(&self) -> String {
|
||||
let mut text = String::new();
|
||||
for string in self.strings() {
|
||||
@ -190,6 +181,16 @@ impl Debug for ParChild {
|
||||
}
|
||||
}
|
||||
|
||||
/// `parbreak`: Start a new paragraph.
|
||||
pub fn parbreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::Node(Node::Parbreak))
|
||||
}
|
||||
|
||||
/// `linebreak`: Start a new line.
|
||||
pub fn linebreak(_: &mut EvalContext, _: &mut Args) -> TypResult<Value> {
|
||||
Ok(Value::Node(Node::Linebreak))
|
||||
}
|
||||
|
||||
/// A paragraph representation in which children are already layouted and text
|
||||
/// is separated into shapable runs.
|
||||
struct ParLayouter<'a> {
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Absolute placement of nodes.
|
||||
|
||||
use super::prelude::*;
|
||||
use super::AlignNode;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Colorable geometrical shapes.
|
||||
|
||||
use std::f64::consts::SQRT_2;
|
||||
|
||||
use super::prelude::*;
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Horizontal and vertical sizing of nodes.
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
/// `box`: Size content and place it into a paragraph.
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Horizontal and vertical spacing between nodes.
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
/// `h`: Horizontal spacing.
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Side-by-side layout of nodes along an axis.
|
||||
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
use super::prelude::*;
|
||||
@ -215,17 +217,17 @@ impl<'a> StackLayouter<'a> {
|
||||
}
|
||||
|
||||
let mut output = Frame::new(size);
|
||||
let mut before = Length::zero();
|
||||
let mut cursor = Length::zero();
|
||||
let mut ruler: Align = self.stack.dir.start().into();
|
||||
|
||||
// Place all frames.
|
||||
for item in self.items.drain(..) {
|
||||
match item {
|
||||
StackItem::Absolute(v) => {
|
||||
before += v;
|
||||
cursor += v;
|
||||
}
|
||||
StackItem::Fractional(v) => {
|
||||
before += v.resolve(self.fr, remaining);
|
||||
cursor += v.resolve(self.fr, remaining);
|
||||
}
|
||||
StackItem::Frame(frame, align) => {
|
||||
if self.stack.dir.is_positive() {
|
||||
@ -239,13 +241,13 @@ impl<'a> StackLayouter<'a> {
|
||||
let child = frame.size.get(self.axis);
|
||||
let block = ruler.resolve(parent - self.used.main)
|
||||
+ if self.stack.dir.is_positive() {
|
||||
before
|
||||
cursor
|
||||
} else {
|
||||
self.used.main - child - before
|
||||
self.used.main - child - cursor
|
||||
};
|
||||
|
||||
let pos = Gen::new(Length::zero(), block).to_point(self.axis);
|
||||
before += child;
|
||||
cursor += child;
|
||||
output.push_frame(pos, frame);
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Text shaping and styling.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::convert::TryInto;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
@ -15,33 +17,6 @@ use crate::font::{
|
||||
use crate::geom::{Dir, Em, Length, Point, Size};
|
||||
use crate::util::{EcoString, SliceExt};
|
||||
|
||||
/// `strike`: Typeset striken-through text.
|
||||
pub fn strike(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
line_impl(args, LineKind::Strikethrough)
|
||||
}
|
||||
|
||||
/// `underline`: Typeset underlined text.
|
||||
pub fn underline(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
line_impl(args, LineKind::Underline)
|
||||
}
|
||||
|
||||
/// `overline`: Typeset text with an overline.
|
||||
pub fn overline(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
line_impl(args, LineKind::Overline)
|
||||
}
|
||||
|
||||
fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> {
|
||||
let stroke = args.named("stroke")?.or_else(|| args.find());
|
||||
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.find());
|
||||
let offset = args.named("offset")?;
|
||||
let extent = args.named("extent")?.unwrap_or_default();
|
||||
let body: Node = args.expect("body")?;
|
||||
let deco = LineDecoration { kind, stroke, thickness, offset, extent };
|
||||
Ok(Value::Node(
|
||||
body.styled(Styles::one(TextNode::LINES, vec![deco])),
|
||||
))
|
||||
}
|
||||
|
||||
/// A single run of text with the same style.
|
||||
#[derive(Hash)]
|
||||
pub struct TextNode {
|
||||
@ -216,6 +191,21 @@ impl Debug for FontFamily {
|
||||
}
|
||||
}
|
||||
|
||||
dynamic! {
|
||||
FontFamily: "font family",
|
||||
Value::Str(string) => Self::named(&string),
|
||||
}
|
||||
|
||||
castable! {
|
||||
Vec<FontFamily>,
|
||||
Expected: "string, generic family or array thereof",
|
||||
Value::Str(string) => vec![FontFamily::named(&string)],
|
||||
Value::Array(values) => {
|
||||
values.into_iter().filter_map(|v| v.cast().ok()).collect()
|
||||
},
|
||||
@family: FontFamily => vec![family.clone()],
|
||||
}
|
||||
|
||||
/// A specific font family like "Arial".
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct NamedFamily(String);
|
||||
@ -238,21 +228,6 @@ impl Debug for NamedFamily {
|
||||
}
|
||||
}
|
||||
|
||||
dynamic! {
|
||||
FontFamily: "font family",
|
||||
Value::Str(string) => Self::named(&string),
|
||||
}
|
||||
|
||||
castable! {
|
||||
Vec<FontFamily>,
|
||||
Expected: "string, generic family or array thereof",
|
||||
Value::Str(string) => vec![FontFamily::named(&string)],
|
||||
Value::Array(values) => {
|
||||
values.into_iter().filter_map(|v| v.cast().ok()).collect()
|
||||
},
|
||||
@family: FontFamily => vec![family.clone()],
|
||||
}
|
||||
|
||||
castable! {
|
||||
Vec<NamedFamily>,
|
||||
Expected: "string or array of strings",
|
||||
@ -421,6 +396,33 @@ castable! {
|
||||
.collect(),
|
||||
}
|
||||
|
||||
/// `strike`: Typeset striken-through text.
|
||||
pub fn strike(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
line_impl(args, LineKind::Strikethrough)
|
||||
}
|
||||
|
||||
/// `underline`: Typeset underlined text.
|
||||
pub fn underline(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
line_impl(args, LineKind::Underline)
|
||||
}
|
||||
|
||||
/// `overline`: Typeset text with an overline.
|
||||
pub fn overline(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
line_impl(args, LineKind::Overline)
|
||||
}
|
||||
|
||||
fn line_impl(args: &mut Args, kind: LineKind) -> TypResult<Value> {
|
||||
let stroke = args.named("stroke")?.or_else(|| args.find());
|
||||
let thickness = args.named::<Linear>("thickness")?.or_else(|| args.find());
|
||||
let offset = args.named("offset")?;
|
||||
let extent = args.named("extent")?.unwrap_or_default();
|
||||
let body: Node = args.expect("body")?;
|
||||
let deco = LineDecoration { kind, stroke, thickness, offset, extent };
|
||||
Ok(Value::Node(
|
||||
body.styled(Styles::one(TextNode::LINES, vec![deco])),
|
||||
))
|
||||
}
|
||||
|
||||
/// Defines a line that is positioned over, under or on top of text.
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
|
||||
pub struct LineDecoration {
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Affine transformations on nodes.
|
||||
|
||||
use super::prelude::*;
|
||||
use crate::geom::Transform;
|
||||
|
||||
@ -20,7 +22,7 @@ pub fn scale(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
|
||||
/// `rotate`: Rotate content without affecting layout.
|
||||
pub fn rotate(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let angle = args.expect("angle")?;
|
||||
let angle = args.named("angle")?.or_else(|| args.find()).unwrap_or_default();
|
||||
let transform = Transform::rotation(angle);
|
||||
transform_impl(args, transform)
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Computational utility functions.
|
||||
|
||||
use std::cmp::Ordering;
|
||||
use std::str::FromStr;
|
||||
|
||||
@ -44,7 +46,7 @@ pub fn join(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
/// `int`: Try to convert a value to a integer.
|
||||
/// `int`: Convert a value to a integer.
|
||||
pub fn int(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let Spanned { v, span } = args.expect("value")?;
|
||||
Ok(Value::Int(match v {
|
||||
@ -59,7 +61,7 @@ pub fn int(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// `float`: Try to convert a value to a float.
|
||||
/// `float`: Convert a value to a float.
|
||||
pub fn float(_: &mut EvalContext, args: &mut Args) -> TypResult<Value> {
|
||||
let Spanned { v, span } = args.expect("value")?;
|
||||
Ok(Value::Float(match v {
|
||||
|
Loading…
x
Reference in New Issue
Block a user